fix typo
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if ( s.href  && s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size] + (
1079                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1080             );
1081             
1082         });
1083         
1084         if (this.hidden) {
1085             cfg.cls += ' hidden';
1086         }
1087         
1088         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089             cfg.cls +=' alert alert-' + this.alert;
1090         }
1091         
1092         
1093         if (this.html.length) {
1094             cfg.html = this.html;
1095         }
1096         if (this.fa) {
1097             var fasize = '';
1098             if (this.fasize > 1) {
1099                 fasize = ' fa-' + this.fasize + 'x';
1100             }
1101             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1102             
1103             
1104         }
1105         if (this.icon) {
1106             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1107         }
1108         
1109         return cfg;
1110     }
1111    
1112 });
1113
1114  
1115
1116  /*
1117  * - LGPL
1118  *
1119  * page container.
1120  * 
1121  */
1122
1123
1124 /**
1125  * @class Roo.bootstrap.Container
1126  * @extends Roo.bootstrap.Component
1127  * Bootstrap Container class
1128  * @cfg {Boolean} jumbotron is it a jumbotron element
1129  * @cfg {String} html content of element
1130  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1132  * @cfg {String} header content of header (for panel)
1133  * @cfg {String} footer content of footer (for panel)
1134  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135  * @cfg {String} tag (header|aside|section) type of HTML tag.
1136  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137  * @cfg {String} fa font awesome icon
1138  * @cfg {String} icon (info-sign|check|...) glyphicon name
1139  * @cfg {Boolean} hidden (true|false) hide the element
1140  * @cfg {Boolean} expandable (true|false) default false
1141  * @cfg {Boolean} expanded (true|false) default true
1142  * @cfg {String} rheader contet on the right of header
1143  * @cfg {Boolean} clickable (true|false) default false
1144
1145  *     
1146  * @constructor
1147  * Create a new Container
1148  * @param {Object} config The config object
1149  */
1150
1151 Roo.bootstrap.Container = function(config){
1152     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1153     
1154     this.addEvents({
1155         // raw events
1156          /**
1157          * @event expand
1158          * After the panel has been expand
1159          * 
1160          * @param {Roo.bootstrap.Container} this
1161          */
1162         "expand" : true,
1163         /**
1164          * @event collapse
1165          * After the panel has been collapsed
1166          * 
1167          * @param {Roo.bootstrap.Container} this
1168          */
1169         "collapse" : true,
1170         /**
1171          * @event click
1172          * When a element is chick
1173          * @param {Roo.bootstrap.Container} this
1174          * @param {Roo.EventObject} e
1175          */
1176         "click" : true
1177     });
1178 };
1179
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1181     
1182     jumbotron : false,
1183     well: '',
1184     panel : '',
1185     header: '',
1186     footer : '',
1187     sticky: '',
1188     tag : false,
1189     alert : false,
1190     fa: false,
1191     icon : false,
1192     expandable : false,
1193     rheader : '',
1194     expanded : true,
1195     clickable: false,
1196   
1197      
1198     getChildContainer : function() {
1199         
1200         if(!this.el){
1201             return false;
1202         }
1203         
1204         if (this.panel.length) {
1205             return this.el.select('.panel-body',true).first();
1206         }
1207         
1208         return this.el;
1209     },
1210     
1211     
1212     getAutoCreate : function(){
1213         
1214         var cfg = {
1215             tag : this.tag || 'div',
1216             html : '',
1217             cls : ''
1218         };
1219         if (this.jumbotron) {
1220             cfg.cls = 'jumbotron';
1221         }
1222         
1223         
1224         
1225         // - this is applied by the parent..
1226         //if (this.cls) {
1227         //    cfg.cls = this.cls + '';
1228         //}
1229         
1230         if (this.sticky.length) {
1231             
1232             var bd = Roo.get(document.body);
1233             if (!bd.hasClass('bootstrap-sticky')) {
1234                 bd.addClass('bootstrap-sticky');
1235                 Roo.select('html',true).setStyle('height', '100%');
1236             }
1237              
1238             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239         }
1240         
1241         
1242         if (this.well.length) {
1243             switch (this.well) {
1244                 case 'lg':
1245                 case 'sm':
1246                     cfg.cls +=' well well-' +this.well;
1247                     break;
1248                 default:
1249                     cfg.cls +=' well';
1250                     break;
1251             }
1252         }
1253         
1254         if (this.hidden) {
1255             cfg.cls += ' hidden';
1256         }
1257         
1258         
1259         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260             cfg.cls +=' alert alert-' + this.alert;
1261         }
1262         
1263         var body = cfg;
1264         
1265         if (this.panel.length) {
1266             cfg.cls += ' panel panel-' + this.panel;
1267             cfg.cn = [];
1268             if (this.header.length) {
1269                 
1270                 var h = [];
1271                 
1272                 if(this.expandable){
1273                     
1274                     cfg.cls = cfg.cls + ' expandable';
1275                     
1276                     h.push({
1277                         tag: 'i',
1278                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1279                     });
1280                     
1281                 }
1282                 
1283                 h.push(
1284                     {
1285                         tag: 'span',
1286                         cls : 'panel-title',
1287                         html : (this.expandable ? '&nbsp;' : '') + this.header
1288                     },
1289                     {
1290                         tag: 'span',
1291                         cls: 'panel-header-right',
1292                         html: this.rheader
1293                     }
1294                 );
1295                 
1296                 cfg.cn.push({
1297                     cls : 'panel-heading',
1298                     style : this.expandable ? 'cursor: pointer' : '',
1299                     cn : h
1300                 });
1301                 
1302             }
1303             
1304             body = false;
1305             cfg.cn.push({
1306                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1307                 html : this.html
1308             });
1309             
1310             
1311             if (this.footer.length) {
1312                 cfg.cn.push({
1313                     cls : 'panel-footer',
1314                     html : this.footer
1315                     
1316                 });
1317             }
1318             
1319         }
1320         
1321         if (body) {
1322             body.html = this.html || cfg.html;
1323             // prefix with the icons..
1324             if (this.fa) {
1325                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1326             }
1327             if (this.icon) {
1328                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1329             }
1330             
1331             
1332         }
1333         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334             cfg.cls =  'container';
1335         }
1336         
1337         return cfg;
1338     },
1339     
1340     initEvents: function() 
1341     {
1342         if(this.expandable){
1343             var headerEl = this.headerEl();
1344         
1345             if(headerEl){
1346                 headerEl.on('click', this.onToggleClick, this);
1347             }
1348         }
1349         
1350         if(this.clickable){
1351             this.el.on('click', this.onClick, this);
1352         }
1353         
1354     },
1355     
1356     onToggleClick : function()
1357     {
1358         var headerEl = this.headerEl();
1359         
1360         if(!headerEl){
1361             return;
1362         }
1363         
1364         if(this.expanded){
1365             this.collapse();
1366             return;
1367         }
1368         
1369         this.expand();
1370     },
1371     
1372     expand : function()
1373     {
1374         if(this.fireEvent('expand', this)) {
1375             
1376             this.expanded = true;
1377             
1378             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1379             
1380             this.el.select('.panel-body',true).first().removeClass('hide');
1381             
1382             var toggleEl = this.toggleEl();
1383
1384             if(!toggleEl){
1385                 return;
1386             }
1387
1388             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1389         }
1390         
1391     },
1392     
1393     collapse : function()
1394     {
1395         if(this.fireEvent('collapse', this)) {
1396             
1397             this.expanded = false;
1398             
1399             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400             this.el.select('.panel-body',true).first().addClass('hide');
1401         
1402             var toggleEl = this.toggleEl();
1403
1404             if(!toggleEl){
1405                 return;
1406             }
1407
1408             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409         }
1410     },
1411     
1412     toggleEl : function()
1413     {
1414         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415             return;
1416         }
1417         
1418         return this.el.select('.panel-heading .fa',true).first();
1419     },
1420     
1421     headerEl : function()
1422     {
1423         if(!this.el || !this.panel.length || !this.header.length){
1424             return;
1425         }
1426         
1427         return this.el.select('.panel-heading',true).first()
1428     },
1429     
1430     bodyEl : function()
1431     {
1432         if(!this.el || !this.panel.length){
1433             return;
1434         }
1435         
1436         return this.el.select('.panel-body',true).first()
1437     },
1438     
1439     titleEl : function()
1440     {
1441         if(!this.el || !this.panel.length || !this.header.length){
1442             return;
1443         }
1444         
1445         return this.el.select('.panel-title',true).first();
1446     },
1447     
1448     setTitle : function(v)
1449     {
1450         var titleEl = this.titleEl();
1451         
1452         if(!titleEl){
1453             return;
1454         }
1455         
1456         titleEl.dom.innerHTML = v;
1457     },
1458     
1459     getTitle : function()
1460     {
1461         
1462         var titleEl = this.titleEl();
1463         
1464         if(!titleEl){
1465             return '';
1466         }
1467         
1468         return titleEl.dom.innerHTML;
1469     },
1470     
1471     setRightTitle : function(v)
1472     {
1473         var t = this.el.select('.panel-header-right',true).first();
1474         
1475         if(!t){
1476             return;
1477         }
1478         
1479         t.dom.innerHTML = v;
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         e.preventDefault();
1485         
1486         this.fireEvent('click', this, e);
1487     }
1488 });
1489
1490  /*
1491  *  - LGPL
1492  *
1493  *  This is BS4's Card element.. - similar to our containers probably..
1494  * 
1495  */
1496 /**
1497  * @class Roo.bootstrap.Card
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Card class
1500  *
1501  *
1502  * possible... may not be implemented..
1503  * @cfg {String} header_image  src url of image.
1504  * @cfg {String} header
1505  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1506  * 
1507  * @cfg {String} title
1508  * @cfg {String} subtitle
1509  * @cfg {String} html -- html contents - or just use children..
1510  * @cfg {String} footer
1511  
1512  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1513  * 
1514  * @cfg {String} margin (0|1|2|3|4|5|auto)
1515  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1516  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1517  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1518  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1519  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1520  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1521  *
1522  * @cfg {String} padding (0|1|2|3|4|5)
1523  * @cfg {String} padding_top (0|1|2|3|4|5)
1524  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1525  * @cfg {String} padding_left (0|1|2|3|4|5)
1526  * @cfg {String} padding_right (0|1|2|3|4|5)
1527  * @cfg {String} padding_x (0|1|2|3|4|5)
1528  * @cfg {String} padding_y (0|1|2|3|4|5)
1529  *
1530  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1531  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1532  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1533  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1534  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1535  
1536  * @constructor
1537  * Create a new Container
1538  * @param {Object} config The config object
1539  */
1540
1541 Roo.bootstrap.Card = function(config){
1542     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1543     
1544     this.addEvents({
1545         
1546     });
1547 };
1548
1549
1550 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1551     
1552     
1553     weight : '',
1554     
1555     margin: '', /// may be better in component?
1556     margin_top: '', 
1557     margin_bottom: '', 
1558     margin_left: '',
1559     margin_right: '',
1560     margin_x: '',
1561     margin_y: '',
1562     
1563     padding : '',
1564     padding_top: '', 
1565     padding_bottom: '', 
1566     padding_left: '',
1567     padding_right: '',
1568     padding_x: '',
1569     padding_y: '',
1570     
1571     display: '', 
1572     display_xs: '', 
1573     display_sm: '', 
1574     display_lg: '',
1575     display_xl: '',
1576  
1577     header_image  : '',
1578     header : '',
1579     header_size : 0,
1580     title : '',
1581     subtitle : '',
1582     html : '',
1583     
1584     
1585     childContainer : false,
1586
1587     layoutCls : function()
1588     {
1589         var cls = '';
1590         var t = this;
1591         
1592         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1593             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1594             
1595             if (t['margin' + (v.length ? '_' : '') + v].length) {
1596                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
1597             }
1598             if (t['padding' + (v.length ? '_' : '') + v].length) {
1599                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
1600             }
1601         });
1602         
1603         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1604             if (t['display' + (v.length ? '_' : '') + v].length) {
1605                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v].length
1606             }
1607         });
1608         
1609         // more generic support?
1610         if (this.hidden) {
1611             cls += ' d-none';
1612         }
1613         
1614         return cls;
1615     },
1616  
1617        // Roo.log("Call onRender: " + this.xtype);
1618         /*  We are looking at something like this.
1619 <div class="card">
1620     <img src="..." class="card-img-top" alt="...">
1621     <div class="card-body">
1622         <h5 class="card-title">Card title</h5>
1623          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1624
1625         >> this bit is really the body...
1626         <div> << we will ad dthis in hopefully it will not break shit.
1627         
1628         ** card text does not actually have any styling...
1629         
1630             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1631         
1632         </div> <<
1633           <a href="#" class="card-link">Card link</a>
1634           
1635     </div>
1636     <div class="card-footer">
1637         <small class="text-muted">Last updated 3 mins ago</small>
1638     </div>
1639 </div>
1640          */
1641     getAutoCreate : function(){
1642         
1643         var cfg = {
1644             tag : 'div',
1645             cls : 'card',
1646             cn : [ ]
1647         };
1648         
1649         if (this.weight.length && this.weight != 'light') {
1650             cfg.cls += ' text-white'
1651         }
1652         if (this.weight.length) {
1653             cfg.cls += ' bg-' + this.weight;
1654         }
1655         
1656         cfg.cls += this.layoutCls(); 
1657         
1658         if (this.header.length) {
1659             cfg.cn.push({
1660                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1661                 cls : 'card-header',
1662                 html : this.header // escape?
1663             });
1664         }
1665         if (this.header_image.length) {
1666             cfg.cn.push({
1667                 tag : 'img',
1668                 cls : 'card-img-top',
1669                 src: this.header_image // escape?
1670             });
1671         }
1672         
1673         var body = {
1674             tag : 'div',
1675             cls : 'card-body',
1676             cn : []
1677         };
1678         cfg.push(body);
1679         
1680         if (this.title.length) {
1681             body.cn.push({
1682                 tag : 'div',
1683                 cls : 'card-title',
1684                 src: this.title // escape?
1685             });
1686         }
1687         
1688         if (this.subtitle.length) {
1689             body.cn.push({
1690                 tag : 'div',
1691                 cls : 'card-title',
1692                 src: this.subtitle // escape?
1693             });
1694         }
1695         
1696         body.cn.push({
1697             tag : 'div',
1698             cls : 'roo-card-body-ctr'
1699         });
1700         
1701         // fixme ? handle objects?
1702         if (this.footer.length) {
1703             body.cn.push({
1704                 tag : 'div',
1705                 cls : 'card-footer',
1706                 html: this.footer // escape?
1707             });
1708         }
1709         // footer...
1710         
1711         return cfg;
1712     },
1713     
1714     
1715     getChildContainer : function()
1716     {
1717         
1718         if(!this.el){
1719             return false;
1720         }
1721         return this.el.select('.roo-card-body-ctr',true).first();    
1722     }
1723     
1724 });
1725
1726 /*
1727  * - LGPL
1728  *
1729  * image
1730  * 
1731  */
1732
1733
1734 /**
1735  * @class Roo.bootstrap.Img
1736  * @extends Roo.bootstrap.Component
1737  * Bootstrap Img class
1738  * @cfg {Boolean} imgResponsive false | true
1739  * @cfg {String} border rounded | circle | thumbnail
1740  * @cfg {String} src image source
1741  * @cfg {String} alt image alternative text
1742  * @cfg {String} href a tag href
1743  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1744  * @cfg {String} xsUrl xs image source
1745  * @cfg {String} smUrl sm image source
1746  * @cfg {String} mdUrl md image source
1747  * @cfg {String} lgUrl lg image source
1748  * 
1749  * @constructor
1750  * Create a new Input
1751  * @param {Object} config The config object
1752  */
1753
1754 Roo.bootstrap.Img = function(config){
1755     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1756     
1757     this.addEvents({
1758         // img events
1759         /**
1760          * @event click
1761          * The img click event for the img.
1762          * @param {Roo.EventObject} e
1763          */
1764         "click" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1769     
1770     imgResponsive: true,
1771     border: '',
1772     src: 'about:blank',
1773     href: false,
1774     target: false,
1775     xsUrl: '',
1776     smUrl: '',
1777     mdUrl: '',
1778     lgUrl: '',
1779
1780     getAutoCreate : function()
1781     {   
1782         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1783             return this.createSingleImg();
1784         }
1785         
1786         var cfg = {
1787             tag: 'div',
1788             cls: 'roo-image-responsive-group',
1789             cn: []
1790         };
1791         var _this = this;
1792         
1793         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1794             
1795             if(!_this[size + 'Url']){
1796                 return;
1797             }
1798             
1799             var img = {
1800                 tag: 'img',
1801                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1802                 html: _this.html || cfg.html,
1803                 src: _this[size + 'Url']
1804             };
1805             
1806             img.cls += ' roo-image-responsive-' + size;
1807             
1808             var s = ['xs', 'sm', 'md', 'lg'];
1809             
1810             s.splice(s.indexOf(size), 1);
1811             
1812             Roo.each(s, function(ss){
1813                 img.cls += ' hidden-' + ss;
1814             });
1815             
1816             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1817                 cfg.cls += ' img-' + _this.border;
1818             }
1819             
1820             if(_this.alt){
1821                 cfg.alt = _this.alt;
1822             }
1823             
1824             if(_this.href){
1825                 var a = {
1826                     tag: 'a',
1827                     href: _this.href,
1828                     cn: [
1829                         img
1830                     ]
1831                 };
1832
1833                 if(this.target){
1834                     a.target = _this.target;
1835                 }
1836             }
1837             
1838             cfg.cn.push((_this.href) ? a : img);
1839             
1840         });
1841         
1842         return cfg;
1843     },
1844     
1845     createSingleImg : function()
1846     {
1847         var cfg = {
1848             tag: 'img',
1849             cls: (this.imgResponsive) ? 'img-responsive' : '',
1850             html : null,
1851             src : 'about:blank'  // just incase src get's set to undefined?!?
1852         };
1853         
1854         cfg.html = this.html || cfg.html;
1855         
1856         cfg.src = this.src || cfg.src;
1857         
1858         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1859             cfg.cls += ' img-' + this.border;
1860         }
1861         
1862         if(this.alt){
1863             cfg.alt = this.alt;
1864         }
1865         
1866         if(this.href){
1867             var a = {
1868                 tag: 'a',
1869                 href: this.href,
1870                 cn: [
1871                     cfg
1872                 ]
1873             };
1874             
1875             if(this.target){
1876                 a.target = this.target;
1877             }
1878             
1879         }
1880         
1881         return (this.href) ? a : cfg;
1882     },
1883     
1884     initEvents: function() 
1885     {
1886         if(!this.href){
1887             this.el.on('click', this.onClick, this);
1888         }
1889         
1890     },
1891     
1892     onClick : function(e)
1893     {
1894         Roo.log('img onclick');
1895         this.fireEvent('click', this, e);
1896     },
1897     /**
1898      * Sets the url of the image - used to update it
1899      * @param {String} url the url of the image
1900      */
1901     
1902     setSrc : function(url)
1903     {
1904         this.src =  url;
1905         
1906         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1907             this.el.dom.src =  url;
1908             return;
1909         }
1910         
1911         this.el.select('img', true).first().dom.src =  url;
1912     }
1913     
1914     
1915    
1916 });
1917
1918  /*
1919  * - LGPL
1920  *
1921  * image
1922  * 
1923  */
1924
1925
1926 /**
1927  * @class Roo.bootstrap.Link
1928  * @extends Roo.bootstrap.Component
1929  * Bootstrap Link Class
1930  * @cfg {String} alt image alternative text
1931  * @cfg {String} href a tag href
1932  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1933  * @cfg {String} html the content of the link.
1934  * @cfg {String} anchor name for the anchor link
1935  * @cfg {String} fa - favicon
1936
1937  * @cfg {Boolean} preventDefault (true | false) default false
1938
1939  * 
1940  * @constructor
1941  * Create a new Input
1942  * @param {Object} config The config object
1943  */
1944
1945 Roo.bootstrap.Link = function(config){
1946     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1947     
1948     this.addEvents({
1949         // img events
1950         /**
1951          * @event click
1952          * The img click event for the img.
1953          * @param {Roo.EventObject} e
1954          */
1955         "click" : true
1956     });
1957 };
1958
1959 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1960     
1961     href: false,
1962     target: false,
1963     preventDefault: false,
1964     anchor : false,
1965     alt : false,
1966     fa: false,
1967
1968
1969     getAutoCreate : function()
1970     {
1971         var html = this.html || '';
1972         
1973         if (this.fa !== false) {
1974             html = '<i class="fa fa-' + this.fa + '"></i>';
1975         }
1976         var cfg = {
1977             tag: 'a'
1978         };
1979         // anchor's do not require html/href...
1980         if (this.anchor === false) {
1981             cfg.html = html;
1982             cfg.href = this.href || '#';
1983         } else {
1984             cfg.name = this.anchor;
1985             if (this.html !== false || this.fa !== false) {
1986                 cfg.html = html;
1987             }
1988             if (this.href !== false) {
1989                 cfg.href = this.href;
1990             }
1991         }
1992         
1993         if(this.alt !== false){
1994             cfg.alt = this.alt;
1995         }
1996         
1997         
1998         if(this.target !== false) {
1999             cfg.target = this.target;
2000         }
2001         
2002         return cfg;
2003     },
2004     
2005     initEvents: function() {
2006         
2007         if(!this.href || this.preventDefault){
2008             this.el.on('click', this.onClick, this);
2009         }
2010     },
2011     
2012     onClick : function(e)
2013     {
2014         if(this.preventDefault){
2015             e.preventDefault();
2016         }
2017         //Roo.log('img onclick');
2018         this.fireEvent('click', this, e);
2019     }
2020    
2021 });
2022
2023  /*
2024  * - LGPL
2025  *
2026  * header
2027  * 
2028  */
2029
2030 /**
2031  * @class Roo.bootstrap.Header
2032  * @extends Roo.bootstrap.Component
2033  * Bootstrap Header class
2034  * @cfg {String} html content of header
2035  * @cfg {Number} level (1|2|3|4|5|6) default 1
2036  * 
2037  * @constructor
2038  * Create a new Header
2039  * @param {Object} config The config object
2040  */
2041
2042
2043 Roo.bootstrap.Header  = function(config){
2044     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2045 };
2046
2047 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2048     
2049     //href : false,
2050     html : false,
2051     level : 1,
2052     
2053     
2054     
2055     getAutoCreate : function(){
2056         
2057         
2058         
2059         var cfg = {
2060             tag: 'h' + (1 *this.level),
2061             html: this.html || ''
2062         } ;
2063         
2064         return cfg;
2065     }
2066    
2067 });
2068
2069  
2070
2071  /*
2072  * Based on:
2073  * Ext JS Library 1.1.1
2074  * Copyright(c) 2006-2007, Ext JS, LLC.
2075  *
2076  * Originally Released Under LGPL - original licence link has changed is not relivant.
2077  *
2078  * Fork - LGPL
2079  * <script type="text/javascript">
2080  */
2081  
2082 /**
2083  * @class Roo.bootstrap.MenuMgr
2084  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2085  * @singleton
2086  */
2087 Roo.bootstrap.MenuMgr = function(){
2088    var menus, active, groups = {}, attached = false, lastShow = new Date();
2089
2090    // private - called when first menu is created
2091    function init(){
2092        menus = {};
2093        active = new Roo.util.MixedCollection();
2094        Roo.get(document).addKeyListener(27, function(){
2095            if(active.length > 0){
2096                hideAll();
2097            }
2098        });
2099    }
2100
2101    // private
2102    function hideAll(){
2103        if(active && active.length > 0){
2104            var c = active.clone();
2105            c.each(function(m){
2106                m.hide();
2107            });
2108        }
2109    }
2110
2111    // private
2112    function onHide(m){
2113        active.remove(m);
2114        if(active.length < 1){
2115            Roo.get(document).un("mouseup", onMouseDown);
2116             
2117            attached = false;
2118        }
2119    }
2120
2121    // private
2122    function onShow(m){
2123        var last = active.last();
2124        lastShow = new Date();
2125        active.add(m);
2126        if(!attached){
2127           Roo.get(document).on("mouseup", onMouseDown);
2128            
2129            attached = true;
2130        }
2131        if(m.parentMenu){
2132           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2133           m.parentMenu.activeChild = m;
2134        }else if(last && last.isVisible()){
2135           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2136        }
2137    }
2138
2139    // private
2140    function onBeforeHide(m){
2141        if(m.activeChild){
2142            m.activeChild.hide();
2143        }
2144        if(m.autoHideTimer){
2145            clearTimeout(m.autoHideTimer);
2146            delete m.autoHideTimer;
2147        }
2148    }
2149
2150    // private
2151    function onBeforeShow(m){
2152        var pm = m.parentMenu;
2153        if(!pm && !m.allowOtherMenus){
2154            hideAll();
2155        }else if(pm && pm.activeChild && active != m){
2156            pm.activeChild.hide();
2157        }
2158    }
2159
2160    // private this should really trigger on mouseup..
2161    function onMouseDown(e){
2162         Roo.log("on Mouse Up");
2163         
2164         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2165             Roo.log("MenuManager hideAll");
2166             hideAll();
2167             e.stopEvent();
2168         }
2169         
2170         
2171    }
2172
2173    // private
2174    function onBeforeCheck(mi, state){
2175        if(state){
2176            var g = groups[mi.group];
2177            for(var i = 0, l = g.length; i < l; i++){
2178                if(g[i] != mi){
2179                    g[i].setChecked(false);
2180                }
2181            }
2182        }
2183    }
2184
2185    return {
2186
2187        /**
2188         * Hides all menus that are currently visible
2189         */
2190        hideAll : function(){
2191             hideAll();  
2192        },
2193
2194        // private
2195        register : function(menu){
2196            if(!menus){
2197                init();
2198            }
2199            menus[menu.id] = menu;
2200            menu.on("beforehide", onBeforeHide);
2201            menu.on("hide", onHide);
2202            menu.on("beforeshow", onBeforeShow);
2203            menu.on("show", onShow);
2204            var g = menu.group;
2205            if(g && menu.events["checkchange"]){
2206                if(!groups[g]){
2207                    groups[g] = [];
2208                }
2209                groups[g].push(menu);
2210                menu.on("checkchange", onCheck);
2211            }
2212        },
2213
2214         /**
2215          * Returns a {@link Roo.menu.Menu} object
2216          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2217          * be used to generate and return a new Menu instance.
2218          */
2219        get : function(menu){
2220            if(typeof menu == "string"){ // menu id
2221                return menus[menu];
2222            }else if(menu.events){  // menu instance
2223                return menu;
2224            }
2225            /*else if(typeof menu.length == 'number'){ // array of menu items?
2226                return new Roo.bootstrap.Menu({items:menu});
2227            }else{ // otherwise, must be a config
2228                return new Roo.bootstrap.Menu(menu);
2229            }
2230            */
2231            return false;
2232        },
2233
2234        // private
2235        unregister : function(menu){
2236            delete menus[menu.id];
2237            menu.un("beforehide", onBeforeHide);
2238            menu.un("hide", onHide);
2239            menu.un("beforeshow", onBeforeShow);
2240            menu.un("show", onShow);
2241            var g = menu.group;
2242            if(g && menu.events["checkchange"]){
2243                groups[g].remove(menu);
2244                menu.un("checkchange", onCheck);
2245            }
2246        },
2247
2248        // private
2249        registerCheckable : function(menuItem){
2250            var g = menuItem.group;
2251            if(g){
2252                if(!groups[g]){
2253                    groups[g] = [];
2254                }
2255                groups[g].push(menuItem);
2256                menuItem.on("beforecheckchange", onBeforeCheck);
2257            }
2258        },
2259
2260        // private
2261        unregisterCheckable : function(menuItem){
2262            var g = menuItem.group;
2263            if(g){
2264                groups[g].remove(menuItem);
2265                menuItem.un("beforecheckchange", onBeforeCheck);
2266            }
2267        }
2268    };
2269 }();/*
2270  * - LGPL
2271  *
2272  * menu
2273  * 
2274  */
2275
2276 /**
2277  * @class Roo.bootstrap.Menu
2278  * @extends Roo.bootstrap.Component
2279  * Bootstrap Menu class - container for MenuItems
2280  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2281  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2282  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2283  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2284  * 
2285  * @constructor
2286  * Create a new Menu
2287  * @param {Object} config The config object
2288  */
2289
2290
2291 Roo.bootstrap.Menu = function(config){
2292     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2293     if (this.registerMenu && this.type != 'treeview')  {
2294         Roo.bootstrap.MenuMgr.register(this);
2295     }
2296     
2297     
2298     this.addEvents({
2299         /**
2300          * @event beforeshow
2301          * Fires before this menu is displayed (return false to block)
2302          * @param {Roo.menu.Menu} this
2303          */
2304         beforeshow : true,
2305         /**
2306          * @event beforehide
2307          * Fires before this menu is hidden (return false to block)
2308          * @param {Roo.menu.Menu} this
2309          */
2310         beforehide : true,
2311         /**
2312          * @event show
2313          * Fires after this menu is displayed
2314          * @param {Roo.menu.Menu} this
2315          */
2316         show : true,
2317         /**
2318          * @event hide
2319          * Fires after this menu is hidden
2320          * @param {Roo.menu.Menu} this
2321          */
2322         hide : true,
2323         /**
2324          * @event click
2325          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2326          * @param {Roo.menu.Menu} this
2327          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2328          * @param {Roo.EventObject} e
2329          */
2330         click : true,
2331         /**
2332          * @event mouseover
2333          * Fires when the mouse is hovering over this menu
2334          * @param {Roo.menu.Menu} this
2335          * @param {Roo.EventObject} e
2336          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2337          */
2338         mouseover : true,
2339         /**
2340          * @event mouseout
2341          * Fires when the mouse exits this menu
2342          * @param {Roo.menu.Menu} this
2343          * @param {Roo.EventObject} e
2344          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2345          */
2346         mouseout : true,
2347         /**
2348          * @event itemclick
2349          * Fires when a menu item contained in this menu is clicked
2350          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2351          * @param {Roo.EventObject} e
2352          */
2353         itemclick: true
2354     });
2355     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2356 };
2357
2358 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2359     
2360    /// html : false,
2361     //align : '',
2362     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2363     type: false,
2364     /**
2365      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2366      */
2367     registerMenu : true,
2368     
2369     menuItems :false, // stores the menu items..
2370     
2371     hidden:true,
2372         
2373     parentMenu : false,
2374     
2375     stopEvent : true,
2376     
2377     isLink : false,
2378     
2379     getChildContainer : function() {
2380         return this.el;  
2381     },
2382     
2383     getAutoCreate : function(){
2384          
2385         //if (['right'].indexOf(this.align)!==-1) {
2386         //    cfg.cn[1].cls += ' pull-right'
2387         //}
2388         
2389         
2390         var cfg = {
2391             tag : 'ul',
2392             cls : 'dropdown-menu' ,
2393             style : 'z-index:1000'
2394             
2395         };
2396         
2397         if (this.type === 'submenu') {
2398             cfg.cls = 'submenu active';
2399         }
2400         if (this.type === 'treeview') {
2401             cfg.cls = 'treeview-menu';
2402         }
2403         
2404         return cfg;
2405     },
2406     initEvents : function() {
2407         
2408        // Roo.log("ADD event");
2409        // Roo.log(this.triggerEl.dom);
2410         
2411         this.triggerEl.on('click', this.onTriggerClick, this);
2412         
2413         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2414         
2415         
2416         if (this.triggerEl.hasClass('nav-item')) {
2417             // dropdown toggle on the 'a' in BS4?
2418             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2419         } else {
2420             this.triggerEl.addClass('dropdown-toggle');
2421         }
2422         if (Roo.isTouch) {
2423             this.el.on('touchstart'  , this.onTouch, this);
2424         }
2425         this.el.on('click' , this.onClick, this);
2426
2427         this.el.on("mouseover", this.onMouseOver, this);
2428         this.el.on("mouseout", this.onMouseOut, this);
2429         
2430     },
2431     
2432     findTargetItem : function(e)
2433     {
2434         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2435         if(!t){
2436             return false;
2437         }
2438         //Roo.log(t);         Roo.log(t.id);
2439         if(t && t.id){
2440             //Roo.log(this.menuitems);
2441             return this.menuitems.get(t.id);
2442             
2443             //return this.items.get(t.menuItemId);
2444         }
2445         
2446         return false;
2447     },
2448     
2449     onTouch : function(e) 
2450     {
2451         Roo.log("menu.onTouch");
2452         //e.stopEvent(); this make the user popdown broken
2453         this.onClick(e);
2454     },
2455     
2456     onClick : function(e)
2457     {
2458         Roo.log("menu.onClick");
2459         
2460         var t = this.findTargetItem(e);
2461         if(!t || t.isContainer){
2462             return;
2463         }
2464         Roo.log(e);
2465         /*
2466         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2467             if(t == this.activeItem && t.shouldDeactivate(e)){
2468                 this.activeItem.deactivate();
2469                 delete this.activeItem;
2470                 return;
2471             }
2472             if(t.canActivate){
2473                 this.setActiveItem(t, true);
2474             }
2475             return;
2476             
2477             
2478         }
2479         */
2480        
2481         Roo.log('pass click event');
2482         
2483         t.onClick(e);
2484         
2485         this.fireEvent("click", this, t, e);
2486         
2487         var _this = this;
2488         
2489         if(!t.href.length || t.href == '#'){
2490             (function() { _this.hide(); }).defer(100);
2491         }
2492         
2493     },
2494     
2495     onMouseOver : function(e){
2496         var t  = this.findTargetItem(e);
2497         //Roo.log(t);
2498         //if(t){
2499         //    if(t.canActivate && !t.disabled){
2500         //        this.setActiveItem(t, true);
2501         //    }
2502         //}
2503         
2504         this.fireEvent("mouseover", this, e, t);
2505     },
2506     isVisible : function(){
2507         return !this.hidden;
2508     },
2509     onMouseOut : function(e){
2510         var t  = this.findTargetItem(e);
2511         
2512         //if(t ){
2513         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2514         //        this.activeItem.deactivate();
2515         //        delete this.activeItem;
2516         //    }
2517         //}
2518         this.fireEvent("mouseout", this, e, t);
2519     },
2520     
2521     
2522     /**
2523      * Displays this menu relative to another element
2524      * @param {String/HTMLElement/Roo.Element} element The element to align to
2525      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2526      * the element (defaults to this.defaultAlign)
2527      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2528      */
2529     show : function(el, pos, parentMenu)
2530     {
2531         if (false === this.fireEvent("beforeshow", this)) {
2532             Roo.log("show canceled");
2533             return;
2534         }
2535         this.parentMenu = parentMenu;
2536         if(!this.el){
2537             this.render();
2538         }
2539         
2540         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2541     },
2542      /**
2543      * Displays this menu at a specific xy position
2544      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2545      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2546      */
2547     showAt : function(xy, parentMenu, /* private: */_e){
2548         this.parentMenu = parentMenu;
2549         if(!this.el){
2550             this.render();
2551         }
2552         if(_e !== false){
2553             this.fireEvent("beforeshow", this);
2554             //xy = this.el.adjustForConstraints(xy);
2555         }
2556         
2557         //this.el.show();
2558         this.hideMenuItems();
2559         this.hidden = false;
2560         this.triggerEl.addClass('open');
2561         this.el.addClass('show');
2562         
2563         // reassign x when hitting right
2564         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2565             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2566         }
2567         
2568         // reassign y when hitting bottom
2569         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2570             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2571         }
2572         
2573         // but the list may align on trigger left or trigger top... should it be a properity?
2574         
2575         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2576             this.el.setXY(xy);
2577         }
2578         
2579         this.focus();
2580         this.fireEvent("show", this);
2581     },
2582     
2583     focus : function(){
2584         return;
2585         if(!this.hidden){
2586             this.doFocus.defer(50, this);
2587         }
2588     },
2589
2590     doFocus : function(){
2591         if(!this.hidden){
2592             this.focusEl.focus();
2593         }
2594     },
2595
2596     /**
2597      * Hides this menu and optionally all parent menus
2598      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2599      */
2600     hide : function(deep)
2601     {
2602         if (false === this.fireEvent("beforehide", this)) {
2603             Roo.log("hide canceled");
2604             return;
2605         }
2606         this.hideMenuItems();
2607         if(this.el && this.isVisible()){
2608            
2609             if(this.activeItem){
2610                 this.activeItem.deactivate();
2611                 this.activeItem = null;
2612             }
2613             this.triggerEl.removeClass('open');;
2614             this.el.removeClass('show');
2615             this.hidden = true;
2616             this.fireEvent("hide", this);
2617         }
2618         if(deep === true && this.parentMenu){
2619             this.parentMenu.hide(true);
2620         }
2621     },
2622     
2623     onTriggerClick : function(e)
2624     {
2625         Roo.log('trigger click');
2626         
2627         var target = e.getTarget();
2628         
2629         Roo.log(target.nodeName.toLowerCase());
2630         
2631         if(target.nodeName.toLowerCase() === 'i'){
2632             e.preventDefault();
2633         }
2634         
2635     },
2636     
2637     onTriggerPress  : function(e)
2638     {
2639         Roo.log('trigger press');
2640         //Roo.log(e.getTarget());
2641        // Roo.log(this.triggerEl.dom);
2642        
2643         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2644         var pel = Roo.get(e.getTarget());
2645         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2646             Roo.log('is treeview or dropdown?');
2647             return;
2648         }
2649         
2650         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2651             return;
2652         }
2653         
2654         if (this.isVisible()) {
2655             Roo.log('hide');
2656             this.hide();
2657         } else {
2658             Roo.log('show');
2659             this.show(this.triggerEl, '?', false);
2660         }
2661         
2662         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2663             e.stopEvent();
2664         }
2665         
2666     },
2667        
2668     
2669     hideMenuItems : function()
2670     {
2671         Roo.log("hide Menu Items");
2672         if (!this.el) { 
2673             return;
2674         }
2675         
2676         this.el.select('.open',true).each(function(aa) {
2677             
2678             aa.removeClass('open');
2679          
2680         });
2681     },
2682     addxtypeChild : function (tree, cntr) {
2683         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2684           
2685         this.menuitems.add(comp);
2686         return comp;
2687
2688     },
2689     getEl : function()
2690     {
2691         Roo.log(this.el);
2692         return this.el;
2693     },
2694     
2695     clear : function()
2696     {
2697         this.getEl().dom.innerHTML = '';
2698         this.menuitems.clear();
2699     }
2700 });
2701
2702  
2703  /*
2704  * - LGPL
2705  *
2706  * menu item
2707  * 
2708  */
2709
2710
2711 /**
2712  * @class Roo.bootstrap.MenuItem
2713  * @extends Roo.bootstrap.Component
2714  * Bootstrap MenuItem class
2715  * @cfg {String} html the menu label
2716  * @cfg {String} href the link
2717  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2718  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2719  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2720  * @cfg {String} fa favicon to show on left of menu item.
2721  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2722  * 
2723  * 
2724  * @constructor
2725  * Create a new MenuItem
2726  * @param {Object} config The config object
2727  */
2728
2729
2730 Roo.bootstrap.MenuItem = function(config){
2731     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2732     this.addEvents({
2733         // raw events
2734         /**
2735          * @event click
2736          * The raw click event for the entire grid.
2737          * @param {Roo.bootstrap.MenuItem} this
2738          * @param {Roo.EventObject} e
2739          */
2740         "click" : true
2741     });
2742 };
2743
2744 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2745     
2746     href : false,
2747     html : false,
2748     preventDefault: false,
2749     isContainer : false,
2750     active : false,
2751     fa: false,
2752     
2753     getAutoCreate : function(){
2754         
2755         if(this.isContainer){
2756             return {
2757                 tag: 'li',
2758                 cls: 'dropdown-menu-item '
2759             };
2760         }
2761         var ctag = {
2762             tag: 'span',
2763             html: 'Link'
2764         };
2765         
2766         var anc = {
2767             tag : 'a',
2768             cls : 'dropdown-item',
2769             href : '#',
2770             cn : [  ]
2771         };
2772         
2773         if (this.fa !== false) {
2774             anc.cn.push({
2775                 tag : 'i',
2776                 cls : 'fa fa-' + this.fa
2777             });
2778         }
2779         
2780         anc.cn.push(ctag);
2781         
2782         
2783         var cfg= {
2784             tag: 'li',
2785             cls: 'dropdown-menu-item',
2786             cn: [ anc ]
2787         };
2788         if (this.parent().type == 'treeview') {
2789             cfg.cls = 'treeview-menu';
2790         }
2791         if (this.active) {
2792             cfg.cls += ' active';
2793         }
2794         
2795         
2796         
2797         anc.href = this.href || cfg.cn[0].href ;
2798         ctag.html = this.html || cfg.cn[0].html ;
2799         return cfg;
2800     },
2801     
2802     initEvents: function()
2803     {
2804         if (this.parent().type == 'treeview') {
2805             this.el.select('a').on('click', this.onClick, this);
2806         }
2807         
2808         if (this.menu) {
2809             this.menu.parentType = this.xtype;
2810             this.menu.triggerEl = this.el;
2811             this.menu = this.addxtype(Roo.apply({}, this.menu));
2812         }
2813         
2814     },
2815     onClick : function(e)
2816     {
2817         Roo.log('item on click ');
2818         
2819         if(this.preventDefault){
2820             e.preventDefault();
2821         }
2822         //this.parent().hideMenuItems();
2823         
2824         this.fireEvent('click', this, e);
2825     },
2826     getEl : function()
2827     {
2828         return this.el;
2829     } 
2830 });
2831
2832  
2833
2834  /*
2835  * - LGPL
2836  *
2837  * menu separator
2838  * 
2839  */
2840
2841
2842 /**
2843  * @class Roo.bootstrap.MenuSeparator
2844  * @extends Roo.bootstrap.Component
2845  * Bootstrap MenuSeparator class
2846  * 
2847  * @constructor
2848  * Create a new MenuItem
2849  * @param {Object} config The config object
2850  */
2851
2852
2853 Roo.bootstrap.MenuSeparator = function(config){
2854     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2855 };
2856
2857 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2858     
2859     getAutoCreate : function(){
2860         var cfg = {
2861             cls: 'divider',
2862             tag : 'li'
2863         };
2864         
2865         return cfg;
2866     }
2867    
2868 });
2869
2870  
2871
2872  
2873 /*
2874 * Licence: LGPL
2875 */
2876
2877 /**
2878  * @class Roo.bootstrap.Modal
2879  * @extends Roo.bootstrap.Component
2880  * Bootstrap Modal class
2881  * @cfg {String} title Title of dialog
2882  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2883  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2884  * @cfg {Boolean} specificTitle default false
2885  * @cfg {Array} buttons Array of buttons or standard button set..
2886  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2887  * @cfg {Boolean} animate default true
2888  * @cfg {Boolean} allow_close default true
2889  * @cfg {Boolean} fitwindow default false
2890  * @cfg {Number} width fixed width - usefull for chrome extension only really.
2891  * @cfg {Number} height fixed height - usefull for chrome extension only really.
2892  * @cfg {String} size (sm|lg) default empty
2893  * @cfg {Number} max_width set the max width of modal
2894  *
2895  *
2896  * @constructor
2897  * Create a new Modal Dialog
2898  * @param {Object} config The config object
2899  */
2900
2901 Roo.bootstrap.Modal = function(config){
2902     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2903     this.addEvents({
2904         // raw events
2905         /**
2906          * @event btnclick
2907          * The raw btnclick event for the button
2908          * @param {Roo.EventObject} e
2909          */
2910         "btnclick" : true,
2911         /**
2912          * @event resize
2913          * Fire when dialog resize
2914          * @param {Roo.bootstrap.Modal} this
2915          * @param {Roo.EventObject} e
2916          */
2917         "resize" : true
2918     });
2919     this.buttons = this.buttons || [];
2920
2921     if (this.tmpl) {
2922         this.tmpl = Roo.factory(this.tmpl);
2923     }
2924
2925 };
2926
2927 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2928
2929     title : 'test dialog',
2930
2931     buttons : false,
2932
2933     // set on load...
2934
2935     html: false,
2936
2937     tmp: false,
2938
2939     specificTitle: false,
2940
2941     buttonPosition: 'right',
2942
2943     allow_close : true,
2944
2945     animate : true,
2946
2947     fitwindow: false,
2948     
2949      // private
2950     dialogEl: false,
2951     bodyEl:  false,
2952     footerEl:  false,
2953     titleEl:  false,
2954     closeEl:  false,
2955
2956     size: '',
2957     
2958     max_width: 0,
2959     
2960     max_height: 0,
2961     
2962     fit_content: false,
2963
2964     onRender : function(ct, position)
2965     {
2966         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2967
2968         if(!this.el){
2969             var cfg = Roo.apply({},  this.getAutoCreate());
2970             cfg.id = Roo.id();
2971             //if(!cfg.name){
2972             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2973             //}
2974             //if (!cfg.name.length) {
2975             //    delete cfg.name;
2976            // }
2977             if (this.cls) {
2978                 cfg.cls += ' ' + this.cls;
2979             }
2980             if (this.style) {
2981                 cfg.style = this.style;
2982             }
2983             this.el = Roo.get(document.body).createChild(cfg, position);
2984         }
2985         //var type = this.el.dom.type;
2986
2987
2988         if(this.tabIndex !== undefined){
2989             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2990         }
2991
2992         this.dialogEl = this.el.select('.modal-dialog',true).first();
2993         this.bodyEl = this.el.select('.modal-body',true).first();
2994         this.closeEl = this.el.select('.modal-header .close', true).first();
2995         this.headerEl = this.el.select('.modal-header',true).first();
2996         this.titleEl = this.el.select('.modal-title',true).first();
2997         this.footerEl = this.el.select('.modal-footer',true).first();
2998
2999         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3000         
3001         //this.el.addClass("x-dlg-modal");
3002
3003         if (this.buttons.length) {
3004             Roo.each(this.buttons, function(bb) {
3005                 var b = Roo.apply({}, bb);
3006                 b.xns = b.xns || Roo.bootstrap;
3007                 b.xtype = b.xtype || 'Button';
3008                 if (typeof(b.listeners) == 'undefined') {
3009                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3010                 }
3011
3012                 var btn = Roo.factory(b);
3013
3014                 btn.render(this.getButtonContainer());
3015
3016             },this);
3017         }
3018         // render the children.
3019         var nitems = [];
3020
3021         if(typeof(this.items) != 'undefined'){
3022             var items = this.items;
3023             delete this.items;
3024
3025             for(var i =0;i < items.length;i++) {
3026                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3027             }
3028         }
3029
3030         this.items = nitems;
3031
3032         // where are these used - they used to be body/close/footer
3033
3034
3035         this.initEvents();
3036         //this.el.addClass([this.fieldClass, this.cls]);
3037
3038     },
3039
3040     getAutoCreate : function()
3041     {
3042         // we will default to modal-body-overflow - might need to remove or make optional later.
3043         var bdy = {
3044                 cls : 'modal-body enable-modal-body-overflow ', 
3045                 html : this.html || ''
3046         };
3047
3048         var title = {
3049             tag: 'h4',
3050             cls : 'modal-title',
3051             html : this.title
3052         };
3053
3054         if(this.specificTitle){
3055             title = this.title;
3056
3057         }
3058
3059         var header = [];
3060         if (this.allow_close && Roo.bootstrap.version == 3) {
3061             header.push({
3062                 tag: 'button',
3063                 cls : 'close',
3064                 html : '&times'
3065             });
3066         }
3067
3068         header.push(title);
3069
3070         if (this.allow_close && Roo.bootstrap.version == 4) {
3071             header.push({
3072                 tag: 'button',
3073                 cls : 'close',
3074                 html : '&times'
3075             });
3076         }
3077         
3078         var size = '';
3079
3080         if(this.size.length){
3081             size = 'modal-' + this.size;
3082         }
3083         
3084         var footer = Roo.bootstrap.version == 3 ?
3085             {
3086                 cls : 'modal-footer',
3087                 cn : [
3088                     {
3089                         tag: 'div',
3090                         cls: 'btn-' + this.buttonPosition
3091                     }
3092                 ]
3093
3094             } :
3095             {  // BS4 uses mr-auto on left buttons....
3096                 cls : 'modal-footer'
3097             };
3098
3099             
3100
3101         
3102         
3103         var modal = {
3104             cls: "modal",
3105              cn : [
3106                 {
3107                     cls: "modal-dialog " + size,
3108                     cn : [
3109                         {
3110                             cls : "modal-content",
3111                             cn : [
3112                                 {
3113                                     cls : 'modal-header',
3114                                     cn : header
3115                                 },
3116                                 bdy,
3117                                 footer
3118                             ]
3119
3120                         }
3121                     ]
3122
3123                 }
3124             ]
3125         };
3126
3127         if(this.animate){
3128             modal.cls += ' fade';
3129         }
3130
3131         return modal;
3132
3133     },
3134     getChildContainer : function() {
3135
3136          return this.bodyEl;
3137
3138     },
3139     getButtonContainer : function() {
3140         
3141          return Roo.bootstrap.version == 4 ?
3142             this.el.select('.modal-footer',true).first()
3143             : this.el.select('.modal-footer div',true).first();
3144
3145     },
3146     initEvents : function()
3147     {
3148         if (this.allow_close) {
3149             this.closeEl.on('click', this.hide, this);
3150         }
3151         Roo.EventManager.onWindowResize(this.resize, this, true);
3152
3153
3154     },
3155   
3156
3157     resize : function()
3158     {
3159         this.maskEl.setSize(
3160             Roo.lib.Dom.getViewWidth(true),
3161             Roo.lib.Dom.getViewHeight(true)
3162         );
3163         
3164         if (this.fitwindow) {
3165             
3166            
3167             this.setSize(
3168                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3169                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3170             );
3171             return;
3172         }
3173         
3174         if(this.max_width !== 0) {
3175             
3176             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3177             
3178             if(this.height) {
3179                 this.setSize(w, this.height);
3180                 return;
3181             }
3182             
3183             if(this.max_height) {
3184                 this.setSize(w,Math.min(
3185                     this.max_height,
3186                     Roo.lib.Dom.getViewportHeight(true) - 60
3187                 ));
3188                 
3189                 return;
3190             }
3191             
3192             if(!this.fit_content) {
3193                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3194                 return;
3195             }
3196             
3197             this.setSize(w, Math.min(
3198                 60 +
3199                 this.headerEl.getHeight() + 
3200                 this.footerEl.getHeight() + 
3201                 this.getChildHeight(this.bodyEl.dom.childNodes),
3202                 Roo.lib.Dom.getViewportHeight(true) - 60)
3203             );
3204         }
3205         
3206     },
3207
3208     setSize : function(w,h)
3209     {
3210         if (!w && !h) {
3211             return;
3212         }
3213         
3214         this.resizeTo(w,h);
3215     },
3216
3217     show : function() {
3218
3219         if (!this.rendered) {
3220             this.render();
3221         }
3222
3223         //this.el.setStyle('display', 'block');
3224         this.el.removeClass('hideing');
3225         this.el.dom.style.display='block';
3226         
3227         Roo.get(document.body).addClass('modal-open');
3228  
3229         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3230             
3231             (function(){
3232                 this.el.addClass('show');
3233                 this.el.addClass('in');
3234             }).defer(50, this);
3235         }else{
3236             this.el.addClass('show');
3237             this.el.addClass('in');
3238         }
3239
3240         // not sure how we can show data in here..
3241         //if (this.tmpl) {
3242         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3243         //}
3244
3245         Roo.get(document.body).addClass("x-body-masked");
3246         
3247         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3248         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3249         this.maskEl.dom.style.display = 'block';
3250         this.maskEl.addClass('show');
3251         
3252         
3253         this.resize();
3254         
3255         this.fireEvent('show', this);
3256
3257         // set zindex here - otherwise it appears to be ignored...
3258         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3259
3260         (function () {
3261             this.items.forEach( function(e) {
3262                 e.layout ? e.layout() : false;
3263
3264             });
3265         }).defer(100,this);
3266
3267     },
3268     hide : function()
3269     {
3270         if(this.fireEvent("beforehide", this) !== false){
3271             
3272             this.maskEl.removeClass('show');
3273             
3274             this.maskEl.dom.style.display = '';
3275             Roo.get(document.body).removeClass("x-body-masked");
3276             this.el.removeClass('in');
3277             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3278
3279             if(this.animate){ // why
3280                 this.el.addClass('hideing');
3281                 this.el.removeClass('show');
3282                 (function(){
3283                     if (!this.el.hasClass('hideing')) {
3284                         return; // it's been shown again...
3285                     }
3286                     
3287                     this.el.dom.style.display='';
3288
3289                     Roo.get(document.body).removeClass('modal-open');
3290                     this.el.removeClass('hideing');
3291                 }).defer(150,this);
3292                 
3293             }else{
3294                 this.el.removeClass('show');
3295                 this.el.dom.style.display='';
3296                 Roo.get(document.body).removeClass('modal-open');
3297
3298             }
3299             this.fireEvent('hide', this);
3300         }
3301     },
3302     isVisible : function()
3303     {
3304         
3305         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3306         
3307     },
3308
3309     addButton : function(str, cb)
3310     {
3311
3312
3313         var b = Roo.apply({}, { html : str } );
3314         b.xns = b.xns || Roo.bootstrap;
3315         b.xtype = b.xtype || 'Button';
3316         if (typeof(b.listeners) == 'undefined') {
3317             b.listeners = { click : cb.createDelegate(this)  };
3318         }
3319
3320         var btn = Roo.factory(b);
3321
3322         btn.render(this.getButtonContainer());
3323
3324         return btn;
3325
3326     },
3327
3328     setDefaultButton : function(btn)
3329     {
3330         //this.el.select('.modal-footer').()
3331     },
3332
3333     resizeTo: function(w,h)
3334     {
3335         this.dialogEl.setWidth(w);
3336         
3337         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3338
3339         this.bodyEl.setHeight(h - diff);
3340         
3341         this.fireEvent('resize', this);
3342     },
3343     
3344     setContentSize  : function(w, h)
3345     {
3346
3347     },
3348     onButtonClick: function(btn,e)
3349     {
3350         //Roo.log([a,b,c]);
3351         this.fireEvent('btnclick', btn.name, e);
3352     },
3353      /**
3354      * Set the title of the Dialog
3355      * @param {String} str new Title
3356      */
3357     setTitle: function(str) {
3358         this.titleEl.dom.innerHTML = str;
3359     },
3360     /**
3361      * Set the body of the Dialog
3362      * @param {String} str new Title
3363      */
3364     setBody: function(str) {
3365         this.bodyEl.dom.innerHTML = str;
3366     },
3367     /**
3368      * Set the body of the Dialog using the template
3369      * @param {Obj} data - apply this data to the template and replace the body contents.
3370      */
3371     applyBody: function(obj)
3372     {
3373         if (!this.tmpl) {
3374             Roo.log("Error - using apply Body without a template");
3375             //code
3376         }
3377         this.tmpl.overwrite(this.bodyEl, obj);
3378     },
3379     
3380     getChildHeight : function(child_nodes)
3381     {
3382         if(
3383             !child_nodes ||
3384             child_nodes.length == 0
3385         ) {
3386             return;
3387         }
3388         
3389         var child_height = 0;
3390         
3391         for(var i = 0; i < child_nodes.length; i++) {
3392             
3393             /*
3394             * for modal with tabs...
3395             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3396                 
3397                 var layout_childs = child_nodes[i].childNodes;
3398                 
3399                 for(var j = 0; j < layout_childs.length; j++) {
3400                     
3401                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3402                         
3403                         var layout_body_childs = layout_childs[j].childNodes;
3404                         
3405                         for(var k = 0; k < layout_body_childs.length; k++) {
3406                             
3407                             if(layout_body_childs[k].classList.contains('navbar')) {
3408                                 child_height += layout_body_childs[k].offsetHeight;
3409                                 continue;
3410                             }
3411                             
3412                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3413                                 
3414                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3415                                 
3416                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3417                                     
3418                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3419                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3420                                         continue;
3421                                     }
3422                                     
3423                                 }
3424                                 
3425                             }
3426                             
3427                         }
3428                     }
3429                 }
3430                 continue;
3431             }
3432             */
3433             
3434             child_height += child_nodes[i].offsetHeight;
3435             // Roo.log(child_nodes[i].offsetHeight);
3436         }
3437         
3438         return child_height;
3439     }
3440
3441 });
3442
3443
3444 Roo.apply(Roo.bootstrap.Modal,  {
3445     /**
3446          * Button config that displays a single OK button
3447          * @type Object
3448          */
3449         OK :  [{
3450             name : 'ok',
3451             weight : 'primary',
3452             html : 'OK'
3453         }],
3454         /**
3455          * Button config that displays Yes and No buttons
3456          * @type Object
3457          */
3458         YESNO : [
3459             {
3460                 name  : 'no',
3461                 html : 'No'
3462             },
3463             {
3464                 name  :'yes',
3465                 weight : 'primary',
3466                 html : 'Yes'
3467             }
3468         ],
3469
3470         /**
3471          * Button config that displays OK and Cancel buttons
3472          * @type Object
3473          */
3474         OKCANCEL : [
3475             {
3476                name : 'cancel',
3477                 html : 'Cancel'
3478             },
3479             {
3480                 name : 'ok',
3481                 weight : 'primary',
3482                 html : 'OK'
3483             }
3484         ],
3485         /**
3486          * Button config that displays Yes, No and Cancel buttons
3487          * @type Object
3488          */
3489         YESNOCANCEL : [
3490             {
3491                 name : 'yes',
3492                 weight : 'primary',
3493                 html : 'Yes'
3494             },
3495             {
3496                 name : 'no',
3497                 html : 'No'
3498             },
3499             {
3500                 name : 'cancel',
3501                 html : 'Cancel'
3502             }
3503         ],
3504         
3505         zIndex : 10001
3506 });
3507 /*
3508  * - LGPL
3509  *
3510  * messagebox - can be used as a replace
3511  * 
3512  */
3513 /**
3514  * @class Roo.MessageBox
3515  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3516  * Example usage:
3517  *<pre><code>
3518 // Basic alert:
3519 Roo.Msg.alert('Status', 'Changes saved successfully.');
3520
3521 // Prompt for user data:
3522 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3523     if (btn == 'ok'){
3524         // process text value...
3525     }
3526 });
3527
3528 // Show a dialog using config options:
3529 Roo.Msg.show({
3530    title:'Save Changes?',
3531    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3532    buttons: Roo.Msg.YESNOCANCEL,
3533    fn: processResult,
3534    animEl: 'elId'
3535 });
3536 </code></pre>
3537  * @singleton
3538  */
3539 Roo.bootstrap.MessageBox = function(){
3540     var dlg, opt, mask, waitTimer;
3541     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3542     var buttons, activeTextEl, bwidth;
3543
3544     
3545     // private
3546     var handleButton = function(button){
3547         dlg.hide();
3548         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3549     };
3550
3551     // private
3552     var handleHide = function(){
3553         if(opt && opt.cls){
3554             dlg.el.removeClass(opt.cls);
3555         }
3556         //if(waitTimer){
3557         //    Roo.TaskMgr.stop(waitTimer);
3558         //    waitTimer = null;
3559         //}
3560     };
3561
3562     // private
3563     var updateButtons = function(b){
3564         var width = 0;
3565         if(!b){
3566             buttons["ok"].hide();
3567             buttons["cancel"].hide();
3568             buttons["yes"].hide();
3569             buttons["no"].hide();
3570             dlg.footerEl.hide();
3571             
3572             return width;
3573         }
3574         dlg.footerEl.show();
3575         for(var k in buttons){
3576             if(typeof buttons[k] != "function"){
3577                 if(b[k]){
3578                     buttons[k].show();
3579                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3580                     width += buttons[k].el.getWidth()+15;
3581                 }else{
3582                     buttons[k].hide();
3583                 }
3584             }
3585         }
3586         return width;
3587     };
3588
3589     // private
3590     var handleEsc = function(d, k, e){
3591         if(opt && opt.closable !== false){
3592             dlg.hide();
3593         }
3594         if(e){
3595             e.stopEvent();
3596         }
3597     };
3598
3599     return {
3600         /**
3601          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3602          * @return {Roo.BasicDialog} The BasicDialog element
3603          */
3604         getDialog : function(){
3605            if(!dlg){
3606                 dlg = new Roo.bootstrap.Modal( {
3607                     //draggable: true,
3608                     //resizable:false,
3609                     //constraintoviewport:false,
3610                     //fixedcenter:true,
3611                     //collapsible : false,
3612                     //shim:true,
3613                     //modal: true,
3614                 //    width: 'auto',
3615                   //  height:100,
3616                     //buttonAlign:"center",
3617                     closeClick : function(){
3618                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3619                             handleButton("no");
3620                         }else{
3621                             handleButton("cancel");
3622                         }
3623                     }
3624                 });
3625                 dlg.render();
3626                 dlg.on("hide", handleHide);
3627                 mask = dlg.mask;
3628                 //dlg.addKeyListener(27, handleEsc);
3629                 buttons = {};
3630                 this.buttons = buttons;
3631                 var bt = this.buttonText;
3632                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3633                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3634                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3635                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3636                 //Roo.log(buttons);
3637                 bodyEl = dlg.bodyEl.createChild({
3638
3639                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3640                         '<textarea class="roo-mb-textarea"></textarea>' +
3641                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3642                 });
3643                 msgEl = bodyEl.dom.firstChild;
3644                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3645                 textboxEl.enableDisplayMode();
3646                 textboxEl.addKeyListener([10,13], function(){
3647                     if(dlg.isVisible() && opt && opt.buttons){
3648                         if(opt.buttons.ok){
3649                             handleButton("ok");
3650                         }else if(opt.buttons.yes){
3651                             handleButton("yes");
3652                         }
3653                     }
3654                 });
3655                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3656                 textareaEl.enableDisplayMode();
3657                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3658                 progressEl.enableDisplayMode();
3659                 
3660                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3661                 var pf = progressEl.dom.firstChild;
3662                 if (pf) {
3663                     pp = Roo.get(pf.firstChild);
3664                     pp.setHeight(pf.offsetHeight);
3665                 }
3666                 
3667             }
3668             return dlg;
3669         },
3670
3671         /**
3672          * Updates the message box body text
3673          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3674          * the XHTML-compliant non-breaking space character '&amp;#160;')
3675          * @return {Roo.MessageBox} This message box
3676          */
3677         updateText : function(text)
3678         {
3679             if(!dlg.isVisible() && !opt.width){
3680                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3681                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3682             }
3683             msgEl.innerHTML = text || '&#160;';
3684       
3685             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3686             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3687             var w = Math.max(
3688                     Math.min(opt.width || cw , this.maxWidth), 
3689                     Math.max(opt.minWidth || this.minWidth, bwidth)
3690             );
3691             if(opt.prompt){
3692                 activeTextEl.setWidth(w);
3693             }
3694             if(dlg.isVisible()){
3695                 dlg.fixedcenter = false;
3696             }
3697             // to big, make it scroll. = But as usual stupid IE does not support
3698             // !important..
3699             
3700             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3701                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3702                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3703             } else {
3704                 bodyEl.dom.style.height = '';
3705                 bodyEl.dom.style.overflowY = '';
3706             }
3707             if (cw > w) {
3708                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3709             } else {
3710                 bodyEl.dom.style.overflowX = '';
3711             }
3712             
3713             dlg.setContentSize(w, bodyEl.getHeight());
3714             if(dlg.isVisible()){
3715                 dlg.fixedcenter = true;
3716             }
3717             return this;
3718         },
3719
3720         /**
3721          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3722          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3723          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3724          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3725          * @return {Roo.MessageBox} This message box
3726          */
3727         updateProgress : function(value, text){
3728             if(text){
3729                 this.updateText(text);
3730             }
3731             
3732             if (pp) { // weird bug on my firefox - for some reason this is not defined
3733                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3734                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3735             }
3736             return this;
3737         },        
3738
3739         /**
3740          * Returns true if the message box is currently displayed
3741          * @return {Boolean} True if the message box is visible, else false
3742          */
3743         isVisible : function(){
3744             return dlg && dlg.isVisible();  
3745         },
3746
3747         /**
3748          * Hides the message box if it is displayed
3749          */
3750         hide : function(){
3751             if(this.isVisible()){
3752                 dlg.hide();
3753             }  
3754         },
3755
3756         /**
3757          * Displays a new message box, or reinitializes an existing message box, based on the config options
3758          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3759          * The following config object properties are supported:
3760          * <pre>
3761 Property    Type             Description
3762 ----------  ---------------  ------------------------------------------------------------------------------------
3763 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3764                                    closes (defaults to undefined)
3765 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3766                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3767 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3768                                    progress and wait dialogs will ignore this property and always hide the
3769                                    close button as they can only be closed programmatically.
3770 cls               String           A custom CSS class to apply to the message box element
3771 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3772                                    displayed (defaults to 75)
3773 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3774                                    function will be btn (the name of the button that was clicked, if applicable,
3775                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3776                                    Progress and wait dialogs will ignore this option since they do not respond to
3777                                    user actions and can only be closed programmatically, so any required function
3778                                    should be called by the same code after it closes the dialog.
3779 icon              String           A CSS class that provides a background image to be used as an icon for
3780                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3781 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3782 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3783 modal             Boolean          False to allow user interaction with the page while the message box is
3784                                    displayed (defaults to true)
3785 msg               String           A string that will replace the existing message box body text (defaults
3786                                    to the XHTML-compliant non-breaking space character '&#160;')
3787 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3788 progress          Boolean          True to display a progress bar (defaults to false)
3789 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3790 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3791 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3792 title             String           The title text
3793 value             String           The string value to set into the active textbox element if displayed
3794 wait              Boolean          True to display a progress bar (defaults to false)
3795 width             Number           The width of the dialog in pixels
3796 </pre>
3797          *
3798          * Example usage:
3799          * <pre><code>
3800 Roo.Msg.show({
3801    title: 'Address',
3802    msg: 'Please enter your address:',
3803    width: 300,
3804    buttons: Roo.MessageBox.OKCANCEL,
3805    multiline: true,
3806    fn: saveAddress,
3807    animEl: 'addAddressBtn'
3808 });
3809 </code></pre>
3810          * @param {Object} config Configuration options
3811          * @return {Roo.MessageBox} This message box
3812          */
3813         show : function(options)
3814         {
3815             
3816             // this causes nightmares if you show one dialog after another
3817             // especially on callbacks..
3818              
3819             if(this.isVisible()){
3820                 
3821                 this.hide();
3822                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3823                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3824                 Roo.log("New Dialog Message:" +  options.msg )
3825                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3826                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3827                 
3828             }
3829             var d = this.getDialog();
3830             opt = options;
3831             d.setTitle(opt.title || "&#160;");
3832             d.closeEl.setDisplayed(opt.closable !== false);
3833             activeTextEl = textboxEl;
3834             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3835             if(opt.prompt){
3836                 if(opt.multiline){
3837                     textboxEl.hide();
3838                     textareaEl.show();
3839                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3840                         opt.multiline : this.defaultTextHeight);
3841                     activeTextEl = textareaEl;
3842                 }else{
3843                     textboxEl.show();
3844                     textareaEl.hide();
3845                 }
3846             }else{
3847                 textboxEl.hide();
3848                 textareaEl.hide();
3849             }
3850             progressEl.setDisplayed(opt.progress === true);
3851             if (opt.progress) {
3852                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3853             }
3854             this.updateProgress(0);
3855             activeTextEl.dom.value = opt.value || "";
3856             if(opt.prompt){
3857                 dlg.setDefaultButton(activeTextEl);
3858             }else{
3859                 var bs = opt.buttons;
3860                 var db = null;
3861                 if(bs && bs.ok){
3862                     db = buttons["ok"];
3863                 }else if(bs && bs.yes){
3864                     db = buttons["yes"];
3865                 }
3866                 dlg.setDefaultButton(db);
3867             }
3868             bwidth = updateButtons(opt.buttons);
3869             this.updateText(opt.msg);
3870             if(opt.cls){
3871                 d.el.addClass(opt.cls);
3872             }
3873             d.proxyDrag = opt.proxyDrag === true;
3874             d.modal = opt.modal !== false;
3875             d.mask = opt.modal !== false ? mask : false;
3876             if(!d.isVisible()){
3877                 // force it to the end of the z-index stack so it gets a cursor in FF
3878                 document.body.appendChild(dlg.el.dom);
3879                 d.animateTarget = null;
3880                 d.show(options.animEl);
3881             }
3882             return this;
3883         },
3884
3885         /**
3886          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3887          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3888          * and closing the message box when the process is complete.
3889          * @param {String} title The title bar text
3890          * @param {String} msg The message box body text
3891          * @return {Roo.MessageBox} This message box
3892          */
3893         progress : function(title, msg){
3894             this.show({
3895                 title : title,
3896                 msg : msg,
3897                 buttons: false,
3898                 progress:true,
3899                 closable:false,
3900                 minWidth: this.minProgressWidth,
3901                 modal : true
3902             });
3903             return this;
3904         },
3905
3906         /**
3907          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3908          * If a callback function is passed it will be called after the user clicks the button, and the
3909          * id of the button that was clicked will be passed as the only parameter to the callback
3910          * (could also be the top-right close button).
3911          * @param {String} title The title bar text
3912          * @param {String} msg The message box body text
3913          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3914          * @param {Object} scope (optional) The scope of the callback function
3915          * @return {Roo.MessageBox} This message box
3916          */
3917         alert : function(title, msg, fn, scope)
3918         {
3919             this.show({
3920                 title : title,
3921                 msg : msg,
3922                 buttons: this.OK,
3923                 fn: fn,
3924                 closable : false,
3925                 scope : scope,
3926                 modal : true
3927             });
3928             return this;
3929         },
3930
3931         /**
3932          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3933          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3934          * You are responsible for closing the message box when the process is complete.
3935          * @param {String} msg The message box body text
3936          * @param {String} title (optional) The title bar text
3937          * @return {Roo.MessageBox} This message box
3938          */
3939         wait : function(msg, title){
3940             this.show({
3941                 title : title,
3942                 msg : msg,
3943                 buttons: false,
3944                 closable:false,
3945                 progress:true,
3946                 modal:true,
3947                 width:300,
3948                 wait:true
3949             });
3950             waitTimer = Roo.TaskMgr.start({
3951                 run: function(i){
3952                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3953                 },
3954                 interval: 1000
3955             });
3956             return this;
3957         },
3958
3959         /**
3960          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3961          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3962          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3963          * @param {String} title The title bar text
3964          * @param {String} msg The message box body text
3965          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3966          * @param {Object} scope (optional) The scope of the callback function
3967          * @return {Roo.MessageBox} This message box
3968          */
3969         confirm : function(title, msg, fn, scope){
3970             this.show({
3971                 title : title,
3972                 msg : msg,
3973                 buttons: this.YESNO,
3974                 fn: fn,
3975                 scope : scope,
3976                 modal : true
3977             });
3978             return this;
3979         },
3980
3981         /**
3982          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3983          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3984          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3985          * (could also be the top-right close button) and the text that was entered will be passed as the two
3986          * parameters to the callback.
3987          * @param {String} title The title bar text
3988          * @param {String} msg The message box body text
3989          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3990          * @param {Object} scope (optional) The scope of the callback function
3991          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3992          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3993          * @return {Roo.MessageBox} This message box
3994          */
3995         prompt : function(title, msg, fn, scope, multiline){
3996             this.show({
3997                 title : title,
3998                 msg : msg,
3999                 buttons: this.OKCANCEL,
4000                 fn: fn,
4001                 minWidth:250,
4002                 scope : scope,
4003                 prompt:true,
4004                 multiline: multiline,
4005                 modal : true
4006             });
4007             return this;
4008         },
4009
4010         /**
4011          * Button config that displays a single OK button
4012          * @type Object
4013          */
4014         OK : {ok:true},
4015         /**
4016          * Button config that displays Yes and No buttons
4017          * @type Object
4018          */
4019         YESNO : {yes:true, no:true},
4020         /**
4021          * Button config that displays OK and Cancel buttons
4022          * @type Object
4023          */
4024         OKCANCEL : {ok:true, cancel:true},
4025         /**
4026          * Button config that displays Yes, No and Cancel buttons
4027          * @type Object
4028          */
4029         YESNOCANCEL : {yes:true, no:true, cancel:true},
4030
4031         /**
4032          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4033          * @type Number
4034          */
4035         defaultTextHeight : 75,
4036         /**
4037          * The maximum width in pixels of the message box (defaults to 600)
4038          * @type Number
4039          */
4040         maxWidth : 600,
4041         /**
4042          * The minimum width in pixels of the message box (defaults to 100)
4043          * @type Number
4044          */
4045         minWidth : 100,
4046         /**
4047          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4048          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4049          * @type Number
4050          */
4051         minProgressWidth : 250,
4052         /**
4053          * An object containing the default button text strings that can be overriden for localized language support.
4054          * Supported properties are: ok, cancel, yes and no.
4055          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4056          * @type Object
4057          */
4058         buttonText : {
4059             ok : "OK",
4060             cancel : "Cancel",
4061             yes : "Yes",
4062             no : "No"
4063         }
4064     };
4065 }();
4066
4067 /**
4068  * Shorthand for {@link Roo.MessageBox}
4069  */
4070 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4071 Roo.Msg = Roo.Msg || Roo.MessageBox;
4072 /*
4073  * - LGPL
4074  *
4075  * navbar
4076  * 
4077  */
4078
4079 /**
4080  * @class Roo.bootstrap.Navbar
4081  * @extends Roo.bootstrap.Component
4082  * Bootstrap Navbar class
4083
4084  * @constructor
4085  * Create a new Navbar
4086  * @param {Object} config The config object
4087  */
4088
4089
4090 Roo.bootstrap.Navbar = function(config){
4091     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4092     this.addEvents({
4093         // raw events
4094         /**
4095          * @event beforetoggle
4096          * Fire before toggle the menu
4097          * @param {Roo.EventObject} e
4098          */
4099         "beforetoggle" : true
4100     });
4101 };
4102
4103 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4104     
4105     
4106    
4107     // private
4108     navItems : false,
4109     loadMask : false,
4110     
4111     
4112     getAutoCreate : function(){
4113         
4114         
4115         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4116         
4117     },
4118     
4119     initEvents :function ()
4120     {
4121         //Roo.log(this.el.select('.navbar-toggle',true));
4122         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4123         
4124         var mark = {
4125             tag: "div",
4126             cls:"x-dlg-mask"
4127         };
4128         
4129         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4130         
4131         var size = this.el.getSize();
4132         this.maskEl.setSize(size.width, size.height);
4133         this.maskEl.enableDisplayMode("block");
4134         this.maskEl.hide();
4135         
4136         if(this.loadMask){
4137             this.maskEl.show();
4138         }
4139     },
4140     
4141     
4142     getChildContainer : function()
4143     {
4144         if (this.el && this.el.select('.collapse').getCount()) {
4145             return this.el.select('.collapse',true).first();
4146         }
4147         
4148         return this.el;
4149     },
4150     
4151     mask : function()
4152     {
4153         this.maskEl.show();
4154     },
4155     
4156     unmask : function()
4157     {
4158         this.maskEl.hide();
4159     },
4160     onToggle : function()
4161     {
4162         
4163         if(this.fireEvent('beforetoggle', this) === false){
4164             return;
4165         }
4166         var ce = this.el.select('.navbar-collapse',true).first();
4167       
4168         if (!ce.hasClass('show')) {
4169            this.expand();
4170         } else {
4171             this.collapse();
4172         }
4173         
4174         
4175     
4176     },
4177     /**
4178      * Expand the navbar pulldown 
4179      */
4180     expand : function ()
4181     {
4182        
4183         var ce = this.el.select('.navbar-collapse',true).first();
4184         if (ce.hasClass('collapsing')) {
4185             return;
4186         }
4187         ce.dom.style.height = '';
4188                // show it...
4189         ce.addClass('in'); // old...
4190         ce.removeClass('collapse');
4191         ce.addClass('show');
4192         var h = ce.getHeight();
4193         Roo.log(h);
4194         ce.removeClass('show');
4195         // at this point we should be able to see it..
4196         ce.addClass('collapsing');
4197         
4198         ce.setHeight(0); // resize it ...
4199         ce.on('transitionend', function() {
4200             //Roo.log('done transition');
4201             ce.removeClass('collapsing');
4202             ce.addClass('show');
4203             ce.removeClass('collapse');
4204
4205             ce.dom.style.height = '';
4206         }, this, { single: true} );
4207         ce.setHeight(h);
4208         ce.dom.scrollTop = 0;
4209     },
4210     /**
4211      * Collapse the navbar pulldown 
4212      */
4213     collapse : function()
4214     {
4215          var ce = this.el.select('.navbar-collapse',true).first();
4216        
4217         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4218             // it's collapsed or collapsing..
4219             return;
4220         }
4221         ce.removeClass('in'); // old...
4222         ce.setHeight(ce.getHeight());
4223         ce.removeClass('show');
4224         ce.addClass('collapsing');
4225         
4226         ce.on('transitionend', function() {
4227             ce.dom.style.height = '';
4228             ce.removeClass('collapsing');
4229             ce.addClass('collapse');
4230         }, this, { single: true} );
4231         ce.setHeight(0);
4232     }
4233     
4234     
4235     
4236 });
4237
4238
4239
4240  
4241
4242  /*
4243  * - LGPL
4244  *
4245  * navbar
4246  * 
4247  */
4248
4249 /**
4250  * @class Roo.bootstrap.NavSimplebar
4251  * @extends Roo.bootstrap.Navbar
4252  * Bootstrap Sidebar class
4253  *
4254  * @cfg {Boolean} inverse is inverted color
4255  * 
4256  * @cfg {String} type (nav | pills | tabs)
4257  * @cfg {Boolean} arrangement stacked | justified
4258  * @cfg {String} align (left | right) alignment
4259  * 
4260  * @cfg {Boolean} main (true|false) main nav bar? default false
4261  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4262  * 
4263  * @cfg {String} tag (header|footer|nav|div) default is nav 
4264
4265  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4266  * 
4267  * 
4268  * @constructor
4269  * Create a new Sidebar
4270  * @param {Object} config The config object
4271  */
4272
4273
4274 Roo.bootstrap.NavSimplebar = function(config){
4275     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4276 };
4277
4278 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4279     
4280     inverse: false,
4281     
4282     type: false,
4283     arrangement: '',
4284     align : false,
4285     
4286     weight : 'light',
4287     
4288     main : false,
4289     
4290     
4291     tag : false,
4292     
4293     
4294     getAutoCreate : function(){
4295         
4296         
4297         var cfg = {
4298             tag : this.tag || 'div',
4299             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4300         };
4301         if (['light','white'].indexOf(this.weight) > -1) {
4302             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4303         }
4304         cfg.cls += ' bg-' + this.weight;
4305         
4306         if (this.inverse) {
4307             cfg.cls += ' navbar-inverse';
4308             
4309         }
4310         
4311         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4312         
4313         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4314             return cfg;
4315         }
4316         
4317         
4318     
4319         
4320         cfg.cn = [
4321             {
4322                 cls: 'nav nav-' + this.xtype,
4323                 tag : 'ul'
4324             }
4325         ];
4326         
4327          
4328         this.type = this.type || 'nav';
4329         if (['tabs','pills'].indexOf(this.type) != -1) {
4330             cfg.cn[0].cls += ' nav-' + this.type
4331         
4332         
4333         } else {
4334             if (this.type!=='nav') {
4335                 Roo.log('nav type must be nav/tabs/pills')
4336             }
4337             cfg.cn[0].cls += ' navbar-nav'
4338         }
4339         
4340         
4341         
4342         
4343         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4344             cfg.cn[0].cls += ' nav-' + this.arrangement;
4345         }
4346         
4347         
4348         if (this.align === 'right') {
4349             cfg.cn[0].cls += ' navbar-right';
4350         }
4351         
4352         
4353         
4354         
4355         return cfg;
4356     
4357         
4358     }
4359     
4360     
4361     
4362 });
4363
4364
4365
4366  
4367
4368  
4369        /*
4370  * - LGPL
4371  *
4372  * navbar
4373  * navbar-fixed-top
4374  * navbar-expand-md  fixed-top 
4375  */
4376
4377 /**
4378  * @class Roo.bootstrap.NavHeaderbar
4379  * @extends Roo.bootstrap.NavSimplebar
4380  * Bootstrap Sidebar class
4381  *
4382  * @cfg {String} brand what is brand
4383  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4384  * @cfg {String} brand_href href of the brand
4385  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4386  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4387  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4388  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4389  * 
4390  * @constructor
4391  * Create a new Sidebar
4392  * @param {Object} config The config object
4393  */
4394
4395
4396 Roo.bootstrap.NavHeaderbar = function(config){
4397     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4398       
4399 };
4400
4401 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4402     
4403     position: '',
4404     brand: '',
4405     brand_href: false,
4406     srButton : true,
4407     autohide : false,
4408     desktopCenter : false,
4409    
4410     
4411     getAutoCreate : function(){
4412         
4413         var   cfg = {
4414             tag: this.nav || 'nav',
4415             cls: 'navbar navbar-expand-md',
4416             role: 'navigation',
4417             cn: []
4418         };
4419         
4420         var cn = cfg.cn;
4421         if (this.desktopCenter) {
4422             cn.push({cls : 'container', cn : []});
4423             cn = cn[0].cn;
4424         }
4425         
4426         if(this.srButton){
4427             var btn = {
4428                 tag: 'button',
4429                 type: 'button',
4430                 cls: 'navbar-toggle navbar-toggler',
4431                 'data-toggle': 'collapse',
4432                 cn: [
4433                     {
4434                         tag: 'span',
4435                         cls: 'sr-only',
4436                         html: 'Toggle navigation'
4437                     },
4438                     {
4439                         tag: 'span',
4440                         cls: 'icon-bar navbar-toggler-icon'
4441                     },
4442                     {
4443                         tag: 'span',
4444                         cls: 'icon-bar'
4445                     },
4446                     {
4447                         tag: 'span',
4448                         cls: 'icon-bar'
4449                     }
4450                 ]
4451             };
4452             
4453             cn.push( Roo.bootstrap.version == 4 ? btn : {
4454                 tag: 'div',
4455                 cls: 'navbar-header',
4456                 cn: [
4457                     btn
4458                 ]
4459             });
4460         }
4461         
4462         cn.push({
4463             tag: 'div',
4464             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4465             cn : []
4466         });
4467         
4468         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4469         
4470         if (['light','white'].indexOf(this.weight) > -1) {
4471             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4472         }
4473         cfg.cls += ' bg-' + this.weight;
4474         
4475         
4476         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4477             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4478             
4479             // tag can override this..
4480             
4481             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4482         }
4483         
4484         if (this.brand !== '') {
4485             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4486             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4487                 tag: 'a',
4488                 href: this.brand_href ? this.brand_href : '#',
4489                 cls: 'navbar-brand',
4490                 cn: [
4491                 this.brand
4492                 ]
4493             });
4494         }
4495         
4496         if(this.main){
4497             cfg.cls += ' main-nav';
4498         }
4499         
4500         
4501         return cfg;
4502
4503         
4504     },
4505     getHeaderChildContainer : function()
4506     {
4507         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4508             return this.el.select('.navbar-header',true).first();
4509         }
4510         
4511         return this.getChildContainer();
4512     },
4513     
4514     
4515     initEvents : function()
4516     {
4517         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4518         
4519         if (this.autohide) {
4520             
4521             var prevScroll = 0;
4522             var ft = this.el;
4523             
4524             Roo.get(document).on('scroll',function(e) {
4525                 var ns = Roo.get(document).getScroll().top;
4526                 var os = prevScroll;
4527                 prevScroll = ns;
4528                 
4529                 if(ns > os){
4530                     ft.removeClass('slideDown');
4531                     ft.addClass('slideUp');
4532                     return;
4533                 }
4534                 ft.removeClass('slideUp');
4535                 ft.addClass('slideDown');
4536                  
4537               
4538           },this);
4539         }
4540     }    
4541     
4542 });
4543
4544
4545
4546  
4547
4548  /*
4549  * - LGPL
4550  *
4551  * navbar
4552  * 
4553  */
4554
4555 /**
4556  * @class Roo.bootstrap.NavSidebar
4557  * @extends Roo.bootstrap.Navbar
4558  * Bootstrap Sidebar class
4559  * 
4560  * @constructor
4561  * Create a new Sidebar
4562  * @param {Object} config The config object
4563  */
4564
4565
4566 Roo.bootstrap.NavSidebar = function(config){
4567     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4568 };
4569
4570 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4571     
4572     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4573     
4574     getAutoCreate : function(){
4575         
4576         
4577         return  {
4578             tag: 'div',
4579             cls: 'sidebar sidebar-nav'
4580         };
4581     
4582         
4583     }
4584     
4585     
4586     
4587 });
4588
4589
4590
4591  
4592
4593  /*
4594  * - LGPL
4595  *
4596  * nav group
4597  * 
4598  */
4599
4600 /**
4601  * @class Roo.bootstrap.NavGroup
4602  * @extends Roo.bootstrap.Component
4603  * Bootstrap NavGroup class
4604  * @cfg {String} align (left|right)
4605  * @cfg {Boolean} inverse
4606  * @cfg {String} type (nav|pills|tab) default nav
4607  * @cfg {String} navId - reference Id for navbar.
4608
4609  * 
4610  * @constructor
4611  * Create a new nav group
4612  * @param {Object} config The config object
4613  */
4614
4615 Roo.bootstrap.NavGroup = function(config){
4616     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4617     this.navItems = [];
4618    
4619     Roo.bootstrap.NavGroup.register(this);
4620      this.addEvents({
4621         /**
4622              * @event changed
4623              * Fires when the active item changes
4624              * @param {Roo.bootstrap.NavGroup} this
4625              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4626              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4627          */
4628         'changed': true
4629      });
4630     
4631 };
4632
4633 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4634     
4635     align: '',
4636     inverse: false,
4637     form: false,
4638     type: 'nav',
4639     navId : '',
4640     // private
4641     
4642     navItems : false, 
4643     
4644     getAutoCreate : function()
4645     {
4646         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4647         
4648         cfg = {
4649             tag : 'ul',
4650             cls: 'nav' 
4651         };
4652         if (Roo.bootstrap.version == 4) {
4653             if (['tabs','pills'].indexOf(this.type) != -1) {
4654                 cfg.cls += ' nav-' + this.type; 
4655             } else {
4656                 // trying to remove so header bar can right align top?
4657                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4658                     // do not use on header bar... 
4659                     cfg.cls += ' navbar-nav';
4660                 }
4661             }
4662             
4663         } else {
4664             if (['tabs','pills'].indexOf(this.type) != -1) {
4665                 cfg.cls += ' nav-' + this.type
4666             } else {
4667                 if (this.type !== 'nav') {
4668                     Roo.log('nav type must be nav/tabs/pills')
4669                 }
4670                 cfg.cls += ' navbar-nav'
4671             }
4672         }
4673         
4674         if (this.parent() && this.parent().sidebar) {
4675             cfg = {
4676                 tag: 'ul',
4677                 cls: 'dashboard-menu sidebar-menu'
4678             };
4679             
4680             return cfg;
4681         }
4682         
4683         if (this.form === true) {
4684             cfg = {
4685                 tag: 'form',
4686                 cls: 'navbar-form form-inline'
4687             };
4688             //nav navbar-right ml-md-auto
4689             if (this.align === 'right') {
4690                 cfg.cls += ' navbar-right ml-md-auto';
4691             } else {
4692                 cfg.cls += ' navbar-left';
4693             }
4694         }
4695         
4696         if (this.align === 'right') {
4697             cfg.cls += ' navbar-right ml-md-auto';
4698         } else {
4699             cfg.cls += ' mr-auto';
4700         }
4701         
4702         if (this.inverse) {
4703             cfg.cls += ' navbar-inverse';
4704             
4705         }
4706         
4707         
4708         return cfg;
4709     },
4710     /**
4711     * sets the active Navigation item
4712     * @param {Roo.bootstrap.NavItem} the new current navitem
4713     */
4714     setActiveItem : function(item)
4715     {
4716         var prev = false;
4717         Roo.each(this.navItems, function(v){
4718             if (v == item) {
4719                 return ;
4720             }
4721             if (v.isActive()) {
4722                 v.setActive(false, true);
4723                 prev = v;
4724                 
4725             }
4726             
4727         });
4728
4729         item.setActive(true, true);
4730         this.fireEvent('changed', this, item, prev);
4731         
4732         
4733     },
4734     /**
4735     * gets the active Navigation item
4736     * @return {Roo.bootstrap.NavItem} the current navitem
4737     */
4738     getActive : function()
4739     {
4740         
4741         var prev = false;
4742         Roo.each(this.navItems, function(v){
4743             
4744             if (v.isActive()) {
4745                 prev = v;
4746                 
4747             }
4748             
4749         });
4750         return prev;
4751     },
4752     
4753     indexOfNav : function()
4754     {
4755         
4756         var prev = false;
4757         Roo.each(this.navItems, function(v,i){
4758             
4759             if (v.isActive()) {
4760                 prev = i;
4761                 
4762             }
4763             
4764         });
4765         return prev;
4766     },
4767     /**
4768     * adds a Navigation item
4769     * @param {Roo.bootstrap.NavItem} the navitem to add
4770     */
4771     addItem : function(cfg)
4772     {
4773         if (this.form && Roo.bootstrap.version == 4) {
4774             cfg.tag = 'div';
4775         }
4776         var cn = new Roo.bootstrap.NavItem(cfg);
4777         this.register(cn);
4778         cn.parentId = this.id;
4779         cn.onRender(this.el, null);
4780         return cn;
4781     },
4782     /**
4783     * register a Navigation item
4784     * @param {Roo.bootstrap.NavItem} the navitem to add
4785     */
4786     register : function(item)
4787     {
4788         this.navItems.push( item);
4789         item.navId = this.navId;
4790     
4791     },
4792     
4793     /**
4794     * clear all the Navigation item
4795     */
4796    
4797     clearAll : function()
4798     {
4799         this.navItems = [];
4800         this.el.dom.innerHTML = '';
4801     },
4802     
4803     getNavItem: function(tabId)
4804     {
4805         var ret = false;
4806         Roo.each(this.navItems, function(e) {
4807             if (e.tabId == tabId) {
4808                ret =  e;
4809                return false;
4810             }
4811             return true;
4812             
4813         });
4814         return ret;
4815     },
4816     
4817     setActiveNext : function()
4818     {
4819         var i = this.indexOfNav(this.getActive());
4820         if (i > this.navItems.length) {
4821             return;
4822         }
4823         this.setActiveItem(this.navItems[i+1]);
4824     },
4825     setActivePrev : function()
4826     {
4827         var i = this.indexOfNav(this.getActive());
4828         if (i  < 1) {
4829             return;
4830         }
4831         this.setActiveItem(this.navItems[i-1]);
4832     },
4833     clearWasActive : function(except) {
4834         Roo.each(this.navItems, function(e) {
4835             if (e.tabId != except.tabId && e.was_active) {
4836                e.was_active = false;
4837                return false;
4838             }
4839             return true;
4840             
4841         });
4842     },
4843     getWasActive : function ()
4844     {
4845         var r = false;
4846         Roo.each(this.navItems, function(e) {
4847             if (e.was_active) {
4848                r = e;
4849                return false;
4850             }
4851             return true;
4852             
4853         });
4854         return r;
4855     }
4856     
4857     
4858 });
4859
4860  
4861 Roo.apply(Roo.bootstrap.NavGroup, {
4862     
4863     groups: {},
4864      /**
4865     * register a Navigation Group
4866     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4867     */
4868     register : function(navgrp)
4869     {
4870         this.groups[navgrp.navId] = navgrp;
4871         
4872     },
4873     /**
4874     * fetch a Navigation Group based on the navigation ID
4875     * @param {string} the navgroup to add
4876     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4877     */
4878     get: function(navId) {
4879         if (typeof(this.groups[navId]) == 'undefined') {
4880             return false;
4881             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4882         }
4883         return this.groups[navId] ;
4884     }
4885     
4886     
4887     
4888 });
4889
4890  /*
4891  * - LGPL
4892  *
4893  * row
4894  * 
4895  */
4896
4897 /**
4898  * @class Roo.bootstrap.NavItem
4899  * @extends Roo.bootstrap.Component
4900  * Bootstrap Navbar.NavItem class
4901  * @cfg {String} href  link to
4902  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4903
4904  * @cfg {String} html content of button
4905  * @cfg {String} badge text inside badge
4906  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4907  * @cfg {String} glyphicon DEPRICATED - use fa
4908  * @cfg {String} icon DEPRICATED - use fa
4909  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4910  * @cfg {Boolean} active Is item active
4911  * @cfg {Boolean} disabled Is item disabled
4912  
4913  * @cfg {Boolean} preventDefault (true | false) default false
4914  * @cfg {String} tabId the tab that this item activates.
4915  * @cfg {String} tagtype (a|span) render as a href or span?
4916  * @cfg {Boolean} animateRef (true|false) link to element default false  
4917   
4918  * @constructor
4919  * Create a new Navbar Item
4920  * @param {Object} config The config object
4921  */
4922 Roo.bootstrap.NavItem = function(config){
4923     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4924     this.addEvents({
4925         // raw events
4926         /**
4927          * @event click
4928          * The raw click event for the entire grid.
4929          * @param {Roo.EventObject} e
4930          */
4931         "click" : true,
4932          /**
4933             * @event changed
4934             * Fires when the active item active state changes
4935             * @param {Roo.bootstrap.NavItem} this
4936             * @param {boolean} state the new state
4937              
4938          */
4939         'changed': true,
4940         /**
4941             * @event scrollto
4942             * Fires when scroll to element
4943             * @param {Roo.bootstrap.NavItem} this
4944             * @param {Object} options
4945             * @param {Roo.EventObject} e
4946              
4947          */
4948         'scrollto': true
4949     });
4950    
4951 };
4952
4953 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4954     
4955     href: false,
4956     html: '',
4957     badge: '',
4958     icon: false,
4959     fa : false,
4960     glyphicon: false,
4961     active: false,
4962     preventDefault : false,
4963     tabId : false,
4964     tagtype : 'a',
4965     tag: 'li',
4966     disabled : false,
4967     animateRef : false,
4968     was_active : false,
4969     button_weight : '',
4970     button_outline : false,
4971     
4972     navLink: false,
4973     
4974     getAutoCreate : function(){
4975          
4976         var cfg = {
4977             tag: this.tag,
4978             cls: 'nav-item'
4979         };
4980         
4981         if (this.active) {
4982             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4983         }
4984         if (this.disabled) {
4985             cfg.cls += ' disabled';
4986         }
4987         
4988         // BS4 only?
4989         if (this.button_weight.length) {
4990             cfg.tag = this.href ? 'a' : 'button';
4991             cfg.html = this.html || '';
4992             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4993             if (this.href) {
4994                 cfg.href = this.href;
4995             }
4996             if (this.fa) {
4997                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4998             }
4999             
5000             // menu .. should add dropdown-menu class - so no need for carat..
5001             
5002             if (this.badge !== '') {
5003                  
5004                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5005             }
5006             return cfg;
5007         }
5008         
5009         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5010             cfg.cn = [
5011                 {
5012                     tag: this.tagtype,
5013                     href : this.href || "#",
5014                     html: this.html || ''
5015                 }
5016             ];
5017             if (this.tagtype == 'a') {
5018                 cfg.cn[0].cls = 'nav-link';
5019             }
5020             if (this.icon) {
5021                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5022             }
5023             if (this.fa) {
5024                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5025             }
5026             if(this.glyphicon) {
5027                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5028             }
5029             
5030             if (this.menu) {
5031                 
5032                 cfg.cn[0].html += " <span class='caret'></span>";
5033              
5034             }
5035             
5036             if (this.badge !== '') {
5037                  
5038                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5039             }
5040         }
5041         
5042         
5043         
5044         return cfg;
5045     },
5046     onRender : function(ct, position)
5047     {
5048        // Roo.log("Call onRender: " + this.xtype);
5049         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5050             this.tag = 'div';
5051         }
5052         
5053         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5054         this.navLink = this.el.select('.nav-link',true).first();
5055         return ret;
5056     },
5057       
5058     
5059     initEvents: function() 
5060     {
5061         if (typeof (this.menu) != 'undefined') {
5062             this.menu.parentType = this.xtype;
5063             this.menu.triggerEl = this.el;
5064             this.menu = this.addxtype(Roo.apply({}, this.menu));
5065         }
5066         
5067         this.el.select('a',true).on('click', this.onClick, this);
5068         
5069         if(this.tagtype == 'span'){
5070             this.el.select('span',true).on('click', this.onClick, this);
5071         }
5072        
5073         // at this point parent should be available..
5074         this.parent().register(this);
5075     },
5076     
5077     onClick : function(e)
5078     {
5079         if (e.getTarget('.dropdown-menu-item')) {
5080             // did you click on a menu itemm.... - then don't trigger onclick..
5081             return;
5082         }
5083         
5084         if(
5085                 this.preventDefault || 
5086                 this.href == '#' 
5087         ){
5088             Roo.log("NavItem - prevent Default?");
5089             e.preventDefault();
5090         }
5091         
5092         if (this.disabled) {
5093             return;
5094         }
5095         
5096         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5097         if (tg && tg.transition) {
5098             Roo.log("waiting for the transitionend");
5099             return;
5100         }
5101         
5102         
5103         
5104         //Roo.log("fire event clicked");
5105         if(this.fireEvent('click', this, e) === false){
5106             return;
5107         };
5108         
5109         if(this.tagtype == 'span'){
5110             return;
5111         }
5112         
5113         //Roo.log(this.href);
5114         var ael = this.el.select('a',true).first();
5115         //Roo.log(ael);
5116         
5117         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5118             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5119             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5120                 return; // ignore... - it's a 'hash' to another page.
5121             }
5122             Roo.log("NavItem - prevent Default?");
5123             e.preventDefault();
5124             this.scrollToElement(e);
5125         }
5126         
5127         
5128         var p =  this.parent();
5129    
5130         if (['tabs','pills'].indexOf(p.type)!==-1) {
5131             if (typeof(p.setActiveItem) !== 'undefined') {
5132                 p.setActiveItem(this);
5133             }
5134         }
5135         
5136         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5137         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5138             // remove the collapsed menu expand...
5139             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5140         }
5141     },
5142     
5143     isActive: function () {
5144         return this.active
5145     },
5146     setActive : function(state, fire, is_was_active)
5147     {
5148         if (this.active && !state && this.navId) {
5149             this.was_active = true;
5150             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5151             if (nv) {
5152                 nv.clearWasActive(this);
5153             }
5154             
5155         }
5156         this.active = state;
5157         
5158         if (!state ) {
5159             this.el.removeClass('active');
5160             this.navLink ? this.navLink.removeClass('active') : false;
5161         } else if (!this.el.hasClass('active')) {
5162             
5163             this.el.addClass('active');
5164             if (Roo.bootstrap.version == 4 && this.navLink ) {
5165                 this.navLink.addClass('active');
5166             }
5167             
5168         }
5169         if (fire) {
5170             this.fireEvent('changed', this, state);
5171         }
5172         
5173         // show a panel if it's registered and related..
5174         
5175         if (!this.navId || !this.tabId || !state || is_was_active) {
5176             return;
5177         }
5178         
5179         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5180         if (!tg) {
5181             return;
5182         }
5183         var pan = tg.getPanelByName(this.tabId);
5184         if (!pan) {
5185             return;
5186         }
5187         // if we can not flip to new panel - go back to old nav highlight..
5188         if (false == tg.showPanel(pan)) {
5189             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5190             if (nv) {
5191                 var onav = nv.getWasActive();
5192                 if (onav) {
5193                     onav.setActive(true, false, true);
5194                 }
5195             }
5196             
5197         }
5198         
5199         
5200         
5201     },
5202      // this should not be here...
5203     setDisabled : function(state)
5204     {
5205         this.disabled = state;
5206         if (!state ) {
5207             this.el.removeClass('disabled');
5208         } else if (!this.el.hasClass('disabled')) {
5209             this.el.addClass('disabled');
5210         }
5211         
5212     },
5213     
5214     /**
5215      * Fetch the element to display the tooltip on.
5216      * @return {Roo.Element} defaults to this.el
5217      */
5218     tooltipEl : function()
5219     {
5220         return this.el.select('' + this.tagtype + '', true).first();
5221     },
5222     
5223     scrollToElement : function(e)
5224     {
5225         var c = document.body;
5226         
5227         /*
5228          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5229          */
5230         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5231             c = document.documentElement;
5232         }
5233         
5234         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5235         
5236         if(!target){
5237             return;
5238         }
5239
5240         var o = target.calcOffsetsTo(c);
5241         
5242         var options = {
5243             target : target,
5244             value : o[1]
5245         };
5246         
5247         this.fireEvent('scrollto', this, options, e);
5248         
5249         Roo.get(c).scrollTo('top', options.value, true);
5250         
5251         return;
5252     }
5253 });
5254  
5255
5256  /*
5257  * - LGPL
5258  *
5259  * sidebar item
5260  *
5261  *  li
5262  *    <span> icon </span>
5263  *    <span> text </span>
5264  *    <span>badge </span>
5265  */
5266
5267 /**
5268  * @class Roo.bootstrap.NavSidebarItem
5269  * @extends Roo.bootstrap.NavItem
5270  * Bootstrap Navbar.NavSidebarItem class
5271  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5272  * {Boolean} open is the menu open
5273  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5274  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5275  * {String} buttonSize (sm|md|lg)the extra classes for the button
5276  * {Boolean} showArrow show arrow next to the text (default true)
5277  * @constructor
5278  * Create a new Navbar Button
5279  * @param {Object} config The config object
5280  */
5281 Roo.bootstrap.NavSidebarItem = function(config){
5282     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5283     this.addEvents({
5284         // raw events
5285         /**
5286          * @event click
5287          * The raw click event for the entire grid.
5288          * @param {Roo.EventObject} e
5289          */
5290         "click" : true,
5291          /**
5292             * @event changed
5293             * Fires when the active item active state changes
5294             * @param {Roo.bootstrap.NavSidebarItem} this
5295             * @param {boolean} state the new state
5296              
5297          */
5298         'changed': true
5299     });
5300    
5301 };
5302
5303 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5304     
5305     badgeWeight : 'default',
5306     
5307     open: false,
5308     
5309     buttonView : false,
5310     
5311     buttonWeight : 'default',
5312     
5313     buttonSize : 'md',
5314     
5315     showArrow : true,
5316     
5317     getAutoCreate : function(){
5318         
5319         
5320         var a = {
5321                 tag: 'a',
5322                 href : this.href || '#',
5323                 cls: '',
5324                 html : '',
5325                 cn : []
5326         };
5327         
5328         if(this.buttonView){
5329             a = {
5330                 tag: 'button',
5331                 href : this.href || '#',
5332                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5333                 html : this.html,
5334                 cn : []
5335             };
5336         }
5337         
5338         var cfg = {
5339             tag: 'li',
5340             cls: '',
5341             cn: [ a ]
5342         };
5343         
5344         if (this.active) {
5345             cfg.cls += ' active';
5346         }
5347         
5348         if (this.disabled) {
5349             cfg.cls += ' disabled';
5350         }
5351         if (this.open) {
5352             cfg.cls += ' open x-open';
5353         }
5354         // left icon..
5355         if (this.glyphicon || this.icon) {
5356             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5357             a.cn.push({ tag : 'i', cls : c }) ;
5358         }
5359         
5360         if(!this.buttonView){
5361             var span = {
5362                 tag: 'span',
5363                 html : this.html || ''
5364             };
5365
5366             a.cn.push(span);
5367             
5368         }
5369         
5370         if (this.badge !== '') {
5371             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5372         }
5373         
5374         if (this.menu) {
5375             
5376             if(this.showArrow){
5377                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5378             }
5379             
5380             a.cls += ' dropdown-toggle treeview' ;
5381         }
5382         
5383         return cfg;
5384     },
5385     
5386     initEvents : function()
5387     { 
5388         if (typeof (this.menu) != 'undefined') {
5389             this.menu.parentType = this.xtype;
5390             this.menu.triggerEl = this.el;
5391             this.menu = this.addxtype(Roo.apply({}, this.menu));
5392         }
5393         
5394         this.el.on('click', this.onClick, this);
5395         
5396         if(this.badge !== ''){
5397             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5398         }
5399         
5400     },
5401     
5402     onClick : function(e)
5403     {
5404         if(this.disabled){
5405             e.preventDefault();
5406             return;
5407         }
5408         
5409         if(this.preventDefault){
5410             e.preventDefault();
5411         }
5412         
5413         this.fireEvent('click', this, e);
5414     },
5415     
5416     disable : function()
5417     {
5418         this.setDisabled(true);
5419     },
5420     
5421     enable : function()
5422     {
5423         this.setDisabled(false);
5424     },
5425     
5426     setDisabled : function(state)
5427     {
5428         if(this.disabled == state){
5429             return;
5430         }
5431         
5432         this.disabled = state;
5433         
5434         if (state) {
5435             this.el.addClass('disabled');
5436             return;
5437         }
5438         
5439         this.el.removeClass('disabled');
5440         
5441         return;
5442     },
5443     
5444     setActive : function(state)
5445     {
5446         if(this.active == state){
5447             return;
5448         }
5449         
5450         this.active = state;
5451         
5452         if (state) {
5453             this.el.addClass('active');
5454             return;
5455         }
5456         
5457         this.el.removeClass('active');
5458         
5459         return;
5460     },
5461     
5462     isActive: function () 
5463     {
5464         return this.active;
5465     },
5466     
5467     setBadge : function(str)
5468     {
5469         if(!this.badgeEl){
5470             return;
5471         }
5472         
5473         this.badgeEl.dom.innerHTML = str;
5474     }
5475     
5476    
5477      
5478  
5479 });
5480  
5481
5482  /*
5483  * - LGPL
5484  *
5485  * row
5486  * 
5487  */
5488
5489 /**
5490  * @class Roo.bootstrap.Row
5491  * @extends Roo.bootstrap.Component
5492  * Bootstrap Row class (contains columns...)
5493  * 
5494  * @constructor
5495  * Create a new Row
5496  * @param {Object} config The config object
5497  */
5498
5499 Roo.bootstrap.Row = function(config){
5500     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5501 };
5502
5503 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5504     
5505     getAutoCreate : function(){
5506        return {
5507             cls: 'row clearfix'
5508        };
5509     }
5510     
5511     
5512 });
5513
5514  
5515
5516  /*
5517  * - LGPL
5518  *
5519  * element
5520  * 
5521  */
5522
5523 /**
5524  * @class Roo.bootstrap.Element
5525  * @extends Roo.bootstrap.Component
5526  * Bootstrap Element class
5527  * @cfg {String} html contents of the element
5528  * @cfg {String} tag tag of the element
5529  * @cfg {String} cls class of the element
5530  * @cfg {Boolean} preventDefault (true|false) default false
5531  * @cfg {Boolean} clickable (true|false) default false
5532  * 
5533  * @constructor
5534  * Create a new Element
5535  * @param {Object} config The config object
5536  */
5537
5538 Roo.bootstrap.Element = function(config){
5539     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5540     
5541     this.addEvents({
5542         // raw events
5543         /**
5544          * @event click
5545          * When a element is chick
5546          * @param {Roo.bootstrap.Element} this
5547          * @param {Roo.EventObject} e
5548          */
5549         "click" : true
5550     });
5551 };
5552
5553 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5554     
5555     tag: 'div',
5556     cls: '',
5557     html: '',
5558     preventDefault: false, 
5559     clickable: false,
5560     
5561     getAutoCreate : function(){
5562         
5563         var cfg = {
5564             tag: this.tag,
5565             // cls: this.cls, double assign in parent class Component.js :: onRender
5566             html: this.html
5567         };
5568         
5569         return cfg;
5570     },
5571     
5572     initEvents: function() 
5573     {
5574         Roo.bootstrap.Element.superclass.initEvents.call(this);
5575         
5576         if(this.clickable){
5577             this.el.on('click', this.onClick, this);
5578         }
5579         
5580     },
5581     
5582     onClick : function(e)
5583     {
5584         if(this.preventDefault){
5585             e.preventDefault();
5586         }
5587         
5588         this.fireEvent('click', this, e);
5589     },
5590     
5591     getValue : function()
5592     {
5593         return this.el.dom.innerHTML;
5594     },
5595     
5596     setValue : function(value)
5597     {
5598         this.el.dom.innerHTML = value;
5599     }
5600    
5601 });
5602
5603  
5604
5605  /*
5606  * - LGPL
5607  *
5608  * pagination
5609  * 
5610  */
5611
5612 /**
5613  * @class Roo.bootstrap.Pagination
5614  * @extends Roo.bootstrap.Component
5615  * Bootstrap Pagination class
5616  * @cfg {String} size xs | sm | md | lg
5617  * @cfg {Boolean} inverse false | true
5618  * 
5619  * @constructor
5620  * Create a new Pagination
5621  * @param {Object} config The config object
5622  */
5623
5624 Roo.bootstrap.Pagination = function(config){
5625     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5626 };
5627
5628 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5629     
5630     cls: false,
5631     size: false,
5632     inverse: false,
5633     
5634     getAutoCreate : function(){
5635         var cfg = {
5636             tag: 'ul',
5637                 cls: 'pagination'
5638         };
5639         if (this.inverse) {
5640             cfg.cls += ' inverse';
5641         }
5642         if (this.html) {
5643             cfg.html=this.html;
5644         }
5645         if (this.cls) {
5646             cfg.cls += " " + this.cls;
5647         }
5648         return cfg;
5649     }
5650    
5651 });
5652
5653  
5654
5655  /*
5656  * - LGPL
5657  *
5658  * Pagination item
5659  * 
5660  */
5661
5662
5663 /**
5664  * @class Roo.bootstrap.PaginationItem
5665  * @extends Roo.bootstrap.Component
5666  * Bootstrap PaginationItem class
5667  * @cfg {String} html text
5668  * @cfg {String} href the link
5669  * @cfg {Boolean} preventDefault (true | false) default true
5670  * @cfg {Boolean} active (true | false) default false
5671  * @cfg {Boolean} disabled default false
5672  * 
5673  * 
5674  * @constructor
5675  * Create a new PaginationItem
5676  * @param {Object} config The config object
5677  */
5678
5679
5680 Roo.bootstrap.PaginationItem = function(config){
5681     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5682     this.addEvents({
5683         // raw events
5684         /**
5685          * @event click
5686          * The raw click event for the entire grid.
5687          * @param {Roo.EventObject} e
5688          */
5689         "click" : true
5690     });
5691 };
5692
5693 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5694     
5695     href : false,
5696     html : false,
5697     preventDefault: true,
5698     active : false,
5699     cls : false,
5700     disabled: false,
5701     
5702     getAutoCreate : function(){
5703         var cfg= {
5704             tag: 'li',
5705             cn: [
5706                 {
5707                     tag : 'a',
5708                     href : this.href ? this.href : '#',
5709                     html : this.html ? this.html : ''
5710                 }
5711             ]
5712         };
5713         
5714         if(this.cls){
5715             cfg.cls = this.cls;
5716         }
5717         
5718         if(this.disabled){
5719             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5720         }
5721         
5722         if(this.active){
5723             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5724         }
5725         
5726         return cfg;
5727     },
5728     
5729     initEvents: function() {
5730         
5731         this.el.on('click', this.onClick, this);
5732         
5733     },
5734     onClick : function(e)
5735     {
5736         Roo.log('PaginationItem on click ');
5737         if(this.preventDefault){
5738             e.preventDefault();
5739         }
5740         
5741         if(this.disabled){
5742             return;
5743         }
5744         
5745         this.fireEvent('click', this, e);
5746     }
5747    
5748 });
5749
5750  
5751
5752  /*
5753  * - LGPL
5754  *
5755  * slider
5756  * 
5757  */
5758
5759
5760 /**
5761  * @class Roo.bootstrap.Slider
5762  * @extends Roo.bootstrap.Component
5763  * Bootstrap Slider class
5764  *    
5765  * @constructor
5766  * Create a new Slider
5767  * @param {Object} config The config object
5768  */
5769
5770 Roo.bootstrap.Slider = function(config){
5771     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5772 };
5773
5774 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5775     
5776     getAutoCreate : function(){
5777         
5778         var cfg = {
5779             tag: 'div',
5780             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5781             cn: [
5782                 {
5783                     tag: 'a',
5784                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5785                 }
5786             ]
5787         };
5788         
5789         return cfg;
5790     }
5791    
5792 });
5793
5794  /*
5795  * Based on:
5796  * Ext JS Library 1.1.1
5797  * Copyright(c) 2006-2007, Ext JS, LLC.
5798  *
5799  * Originally Released Under LGPL - original licence link has changed is not relivant.
5800  *
5801  * Fork - LGPL
5802  * <script type="text/javascript">
5803  */
5804  
5805
5806 /**
5807  * @class Roo.grid.ColumnModel
5808  * @extends Roo.util.Observable
5809  * This is the default implementation of a ColumnModel used by the Grid. It defines
5810  * the columns in the grid.
5811  * <br>Usage:<br>
5812  <pre><code>
5813  var colModel = new Roo.grid.ColumnModel([
5814         {header: "Ticker", width: 60, sortable: true, locked: true},
5815         {header: "Company Name", width: 150, sortable: true},
5816         {header: "Market Cap.", width: 100, sortable: true},
5817         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5818         {header: "Employees", width: 100, sortable: true, resizable: false}
5819  ]);
5820  </code></pre>
5821  * <p>
5822  
5823  * The config options listed for this class are options which may appear in each
5824  * individual column definition.
5825  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5826  * @constructor
5827  * @param {Object} config An Array of column config objects. See this class's
5828  * config objects for details.
5829 */
5830 Roo.grid.ColumnModel = function(config){
5831         /**
5832      * The config passed into the constructor
5833      */
5834     this.config = config;
5835     this.lookup = {};
5836
5837     // if no id, create one
5838     // if the column does not have a dataIndex mapping,
5839     // map it to the order it is in the config
5840     for(var i = 0, len = config.length; i < len; i++){
5841         var c = config[i];
5842         if(typeof c.dataIndex == "undefined"){
5843             c.dataIndex = i;
5844         }
5845         if(typeof c.renderer == "string"){
5846             c.renderer = Roo.util.Format[c.renderer];
5847         }
5848         if(typeof c.id == "undefined"){
5849             c.id = Roo.id();
5850         }
5851         if(c.editor && c.editor.xtype){
5852             c.editor  = Roo.factory(c.editor, Roo.grid);
5853         }
5854         if(c.editor && c.editor.isFormField){
5855             c.editor = new Roo.grid.GridEditor(c.editor);
5856         }
5857         this.lookup[c.id] = c;
5858     }
5859
5860     /**
5861      * The width of columns which have no width specified (defaults to 100)
5862      * @type Number
5863      */
5864     this.defaultWidth = 100;
5865
5866     /**
5867      * Default sortable of columns which have no sortable specified (defaults to false)
5868      * @type Boolean
5869      */
5870     this.defaultSortable = false;
5871
5872     this.addEvents({
5873         /**
5874              * @event widthchange
5875              * Fires when the width of a column changes.
5876              * @param {ColumnModel} this
5877              * @param {Number} columnIndex The column index
5878              * @param {Number} newWidth The new width
5879              */
5880             "widthchange": true,
5881         /**
5882              * @event headerchange
5883              * Fires when the text of a header changes.
5884              * @param {ColumnModel} this
5885              * @param {Number} columnIndex The column index
5886              * @param {Number} newText The new header text
5887              */
5888             "headerchange": true,
5889         /**
5890              * @event hiddenchange
5891              * Fires when a column is hidden or "unhidden".
5892              * @param {ColumnModel} this
5893              * @param {Number} columnIndex The column index
5894              * @param {Boolean} hidden true if hidden, false otherwise
5895              */
5896             "hiddenchange": true,
5897             /**
5898          * @event columnmoved
5899          * Fires when a column is moved.
5900          * @param {ColumnModel} this
5901          * @param {Number} oldIndex
5902          * @param {Number} newIndex
5903          */
5904         "columnmoved" : true,
5905         /**
5906          * @event columlockchange
5907          * Fires when a column's locked state is changed
5908          * @param {ColumnModel} this
5909          * @param {Number} colIndex
5910          * @param {Boolean} locked true if locked
5911          */
5912         "columnlockchange" : true
5913     });
5914     Roo.grid.ColumnModel.superclass.constructor.call(this);
5915 };
5916 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5917     /**
5918      * @cfg {String} header The header text to display in the Grid view.
5919      */
5920     /**
5921      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5922      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5923      * specified, the column's index is used as an index into the Record's data Array.
5924      */
5925     /**
5926      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5927      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5928      */
5929     /**
5930      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5931      * Defaults to the value of the {@link #defaultSortable} property.
5932      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5933      */
5934     /**
5935      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5936      */
5937     /**
5938      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5939      */
5940     /**
5941      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5942      */
5943     /**
5944      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5945      */
5946     /**
5947      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5948      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5949      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5950      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5951      */
5952        /**
5953      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5954      */
5955     /**
5956      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5957      */
5958     /**
5959      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5960      */
5961     /**
5962      * @cfg {String} cursor (Optional)
5963      */
5964     /**
5965      * @cfg {String} tooltip (Optional)
5966      */
5967     /**
5968      * @cfg {Number} xs (Optional)
5969      */
5970     /**
5971      * @cfg {Number} sm (Optional)
5972      */
5973     /**
5974      * @cfg {Number} md (Optional)
5975      */
5976     /**
5977      * @cfg {Number} lg (Optional)
5978      */
5979     /**
5980      * Returns the id of the column at the specified index.
5981      * @param {Number} index The column index
5982      * @return {String} the id
5983      */
5984     getColumnId : function(index){
5985         return this.config[index].id;
5986     },
5987
5988     /**
5989      * Returns the column for a specified id.
5990      * @param {String} id The column id
5991      * @return {Object} the column
5992      */
5993     getColumnById : function(id){
5994         return this.lookup[id];
5995     },
5996
5997     
5998     /**
5999      * Returns the column for a specified dataIndex.
6000      * @param {String} dataIndex The column dataIndex
6001      * @return {Object|Boolean} the column or false if not found
6002      */
6003     getColumnByDataIndex: function(dataIndex){
6004         var index = this.findColumnIndex(dataIndex);
6005         return index > -1 ? this.config[index] : false;
6006     },
6007     
6008     /**
6009      * Returns the index for a specified column id.
6010      * @param {String} id The column id
6011      * @return {Number} the index, or -1 if not found
6012      */
6013     getIndexById : function(id){
6014         for(var i = 0, len = this.config.length; i < len; i++){
6015             if(this.config[i].id == id){
6016                 return i;
6017             }
6018         }
6019         return -1;
6020     },
6021     
6022     /**
6023      * Returns the index for a specified column dataIndex.
6024      * @param {String} dataIndex The column dataIndex
6025      * @return {Number} the index, or -1 if not found
6026      */
6027     
6028     findColumnIndex : function(dataIndex){
6029         for(var i = 0, len = this.config.length; i < len; i++){
6030             if(this.config[i].dataIndex == dataIndex){
6031                 return i;
6032             }
6033         }
6034         return -1;
6035     },
6036     
6037     
6038     moveColumn : function(oldIndex, newIndex){
6039         var c = this.config[oldIndex];
6040         this.config.splice(oldIndex, 1);
6041         this.config.splice(newIndex, 0, c);
6042         this.dataMap = null;
6043         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6044     },
6045
6046     isLocked : function(colIndex){
6047         return this.config[colIndex].locked === true;
6048     },
6049
6050     setLocked : function(colIndex, value, suppressEvent){
6051         if(this.isLocked(colIndex) == value){
6052             return;
6053         }
6054         this.config[colIndex].locked = value;
6055         if(!suppressEvent){
6056             this.fireEvent("columnlockchange", this, colIndex, value);
6057         }
6058     },
6059
6060     getTotalLockedWidth : function(){
6061         var totalWidth = 0;
6062         for(var i = 0; i < this.config.length; i++){
6063             if(this.isLocked(i) && !this.isHidden(i)){
6064                 this.totalWidth += this.getColumnWidth(i);
6065             }
6066         }
6067         return totalWidth;
6068     },
6069
6070     getLockedCount : function(){
6071         for(var i = 0, len = this.config.length; i < len; i++){
6072             if(!this.isLocked(i)){
6073                 return i;
6074             }
6075         }
6076         
6077         return this.config.length;
6078     },
6079
6080     /**
6081      * Returns the number of columns.
6082      * @return {Number}
6083      */
6084     getColumnCount : function(visibleOnly){
6085         if(visibleOnly === true){
6086             var c = 0;
6087             for(var i = 0, len = this.config.length; i < len; i++){
6088                 if(!this.isHidden(i)){
6089                     c++;
6090                 }
6091             }
6092             return c;
6093         }
6094         return this.config.length;
6095     },
6096
6097     /**
6098      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6099      * @param {Function} fn
6100      * @param {Object} scope (optional)
6101      * @return {Array} result
6102      */
6103     getColumnsBy : function(fn, scope){
6104         var r = [];
6105         for(var i = 0, len = this.config.length; i < len; i++){
6106             var c = this.config[i];
6107             if(fn.call(scope||this, c, i) === true){
6108                 r[r.length] = c;
6109             }
6110         }
6111         return r;
6112     },
6113
6114     /**
6115      * Returns true if the specified column is sortable.
6116      * @param {Number} col The column index
6117      * @return {Boolean}
6118      */
6119     isSortable : function(col){
6120         if(typeof this.config[col].sortable == "undefined"){
6121             return this.defaultSortable;
6122         }
6123         return this.config[col].sortable;
6124     },
6125
6126     /**
6127      * Returns the rendering (formatting) function defined for the column.
6128      * @param {Number} col The column index.
6129      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6130      */
6131     getRenderer : function(col){
6132         if(!this.config[col].renderer){
6133             return Roo.grid.ColumnModel.defaultRenderer;
6134         }
6135         return this.config[col].renderer;
6136     },
6137
6138     /**
6139      * Sets the rendering (formatting) function for a column.
6140      * @param {Number} col The column index
6141      * @param {Function} fn The function to use to process the cell's raw data
6142      * to return HTML markup for the grid view. The render function is called with
6143      * the following parameters:<ul>
6144      * <li>Data value.</li>
6145      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6146      * <li>css A CSS style string to apply to the table cell.</li>
6147      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6148      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6149      * <li>Row index</li>
6150      * <li>Column index</li>
6151      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6152      */
6153     setRenderer : function(col, fn){
6154         this.config[col].renderer = fn;
6155     },
6156
6157     /**
6158      * Returns the width for the specified column.
6159      * @param {Number} col The column index
6160      * @return {Number}
6161      */
6162     getColumnWidth : function(col){
6163         return this.config[col].width * 1 || this.defaultWidth;
6164     },
6165
6166     /**
6167      * Sets the width for a column.
6168      * @param {Number} col The column index
6169      * @param {Number} width The new width
6170      */
6171     setColumnWidth : function(col, width, suppressEvent){
6172         this.config[col].width = width;
6173         this.totalWidth = null;
6174         if(!suppressEvent){
6175              this.fireEvent("widthchange", this, col, width);
6176         }
6177     },
6178
6179     /**
6180      * Returns the total width of all columns.
6181      * @param {Boolean} includeHidden True to include hidden column widths
6182      * @return {Number}
6183      */
6184     getTotalWidth : function(includeHidden){
6185         if(!this.totalWidth){
6186             this.totalWidth = 0;
6187             for(var i = 0, len = this.config.length; i < len; i++){
6188                 if(includeHidden || !this.isHidden(i)){
6189                     this.totalWidth += this.getColumnWidth(i);
6190                 }
6191             }
6192         }
6193         return this.totalWidth;
6194     },
6195
6196     /**
6197      * Returns the header for the specified column.
6198      * @param {Number} col The column index
6199      * @return {String}
6200      */
6201     getColumnHeader : function(col){
6202         return this.config[col].header;
6203     },
6204
6205     /**
6206      * Sets the header for a column.
6207      * @param {Number} col The column index
6208      * @param {String} header The new header
6209      */
6210     setColumnHeader : function(col, header){
6211         this.config[col].header = header;
6212         this.fireEvent("headerchange", this, col, header);
6213     },
6214
6215     /**
6216      * Returns the tooltip for the specified column.
6217      * @param {Number} col The column index
6218      * @return {String}
6219      */
6220     getColumnTooltip : function(col){
6221             return this.config[col].tooltip;
6222     },
6223     /**
6224      * Sets the tooltip for a column.
6225      * @param {Number} col The column index
6226      * @param {String} tooltip The new tooltip
6227      */
6228     setColumnTooltip : function(col, tooltip){
6229             this.config[col].tooltip = tooltip;
6230     },
6231
6232     /**
6233      * Returns the dataIndex for the specified column.
6234      * @param {Number} col The column index
6235      * @return {Number}
6236      */
6237     getDataIndex : function(col){
6238         return this.config[col].dataIndex;
6239     },
6240
6241     /**
6242      * Sets the dataIndex for a column.
6243      * @param {Number} col The column index
6244      * @param {Number} dataIndex The new dataIndex
6245      */
6246     setDataIndex : function(col, dataIndex){
6247         this.config[col].dataIndex = dataIndex;
6248     },
6249
6250     
6251     
6252     /**
6253      * Returns true if the cell is editable.
6254      * @param {Number} colIndex The column index
6255      * @param {Number} rowIndex The row index - this is nto actually used..?
6256      * @return {Boolean}
6257      */
6258     isCellEditable : function(colIndex, rowIndex){
6259         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6260     },
6261
6262     /**
6263      * Returns the editor defined for the cell/column.
6264      * return false or null to disable editing.
6265      * @param {Number} colIndex The column index
6266      * @param {Number} rowIndex The row index
6267      * @return {Object}
6268      */
6269     getCellEditor : function(colIndex, rowIndex){
6270         return this.config[colIndex].editor;
6271     },
6272
6273     /**
6274      * Sets if a column is editable.
6275      * @param {Number} col The column index
6276      * @param {Boolean} editable True if the column is editable
6277      */
6278     setEditable : function(col, editable){
6279         this.config[col].editable = editable;
6280     },
6281
6282
6283     /**
6284      * Returns true if the column is hidden.
6285      * @param {Number} colIndex The column index
6286      * @return {Boolean}
6287      */
6288     isHidden : function(colIndex){
6289         return this.config[colIndex].hidden;
6290     },
6291
6292
6293     /**
6294      * Returns true if the column width cannot be changed
6295      */
6296     isFixed : function(colIndex){
6297         return this.config[colIndex].fixed;
6298     },
6299
6300     /**
6301      * Returns true if the column can be resized
6302      * @return {Boolean}
6303      */
6304     isResizable : function(colIndex){
6305         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6306     },
6307     /**
6308      * Sets if a column is hidden.
6309      * @param {Number} colIndex The column index
6310      * @param {Boolean} hidden True if the column is hidden
6311      */
6312     setHidden : function(colIndex, hidden){
6313         this.config[colIndex].hidden = hidden;
6314         this.totalWidth = null;
6315         this.fireEvent("hiddenchange", this, colIndex, hidden);
6316     },
6317
6318     /**
6319      * Sets the editor for a column.
6320      * @param {Number} col The column index
6321      * @param {Object} editor The editor object
6322      */
6323     setEditor : function(col, editor){
6324         this.config[col].editor = editor;
6325     }
6326 });
6327
6328 Roo.grid.ColumnModel.defaultRenderer = function(value)
6329 {
6330     if(typeof value == "object") {
6331         return value;
6332     }
6333         if(typeof value == "string" && value.length < 1){
6334             return "&#160;";
6335         }
6336     
6337         return String.format("{0}", value);
6338 };
6339
6340 // Alias for backwards compatibility
6341 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6342 /*
6343  * Based on:
6344  * Ext JS Library 1.1.1
6345  * Copyright(c) 2006-2007, Ext JS, LLC.
6346  *
6347  * Originally Released Under LGPL - original licence link has changed is not relivant.
6348  *
6349  * Fork - LGPL
6350  * <script type="text/javascript">
6351  */
6352  
6353 /**
6354  * @class Roo.LoadMask
6355  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6356  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6357  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6358  * element's UpdateManager load indicator and will be destroyed after the initial load.
6359  * @constructor
6360  * Create a new LoadMask
6361  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6362  * @param {Object} config The config object
6363  */
6364 Roo.LoadMask = function(el, config){
6365     this.el = Roo.get(el);
6366     Roo.apply(this, config);
6367     if(this.store){
6368         this.store.on('beforeload', this.onBeforeLoad, this);
6369         this.store.on('load', this.onLoad, this);
6370         this.store.on('loadexception', this.onLoadException, this);
6371         this.removeMask = false;
6372     }else{
6373         var um = this.el.getUpdateManager();
6374         um.showLoadIndicator = false; // disable the default indicator
6375         um.on('beforeupdate', this.onBeforeLoad, this);
6376         um.on('update', this.onLoad, this);
6377         um.on('failure', this.onLoad, this);
6378         this.removeMask = true;
6379     }
6380 };
6381
6382 Roo.LoadMask.prototype = {
6383     /**
6384      * @cfg {Boolean} removeMask
6385      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6386      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6387      */
6388     /**
6389      * @cfg {String} msg
6390      * The text to display in a centered loading message box (defaults to 'Loading...')
6391      */
6392     msg : 'Loading...',
6393     /**
6394      * @cfg {String} msgCls
6395      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6396      */
6397     msgCls : 'x-mask-loading',
6398
6399     /**
6400      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6401      * @type Boolean
6402      */
6403     disabled: false,
6404
6405     /**
6406      * Disables the mask to prevent it from being displayed
6407      */
6408     disable : function(){
6409        this.disabled = true;
6410     },
6411
6412     /**
6413      * Enables the mask so that it can be displayed
6414      */
6415     enable : function(){
6416         this.disabled = false;
6417     },
6418     
6419     onLoadException : function()
6420     {
6421         Roo.log(arguments);
6422         
6423         if (typeof(arguments[3]) != 'undefined') {
6424             Roo.MessageBox.alert("Error loading",arguments[3]);
6425         } 
6426         /*
6427         try {
6428             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6429                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6430             }   
6431         } catch(e) {
6432             
6433         }
6434         */
6435     
6436         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6437     },
6438     // private
6439     onLoad : function()
6440     {
6441         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6442     },
6443
6444     // private
6445     onBeforeLoad : function(){
6446         if(!this.disabled){
6447             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6448         }
6449     },
6450
6451     // private
6452     destroy : function(){
6453         if(this.store){
6454             this.store.un('beforeload', this.onBeforeLoad, this);
6455             this.store.un('load', this.onLoad, this);
6456             this.store.un('loadexception', this.onLoadException, this);
6457         }else{
6458             var um = this.el.getUpdateManager();
6459             um.un('beforeupdate', this.onBeforeLoad, this);
6460             um.un('update', this.onLoad, this);
6461             um.un('failure', this.onLoad, this);
6462         }
6463     }
6464 };/*
6465  * - LGPL
6466  *
6467  * table
6468  * 
6469  */
6470
6471 /**
6472  * @class Roo.bootstrap.Table
6473  * @extends Roo.bootstrap.Component
6474  * Bootstrap Table class
6475  * @cfg {String} cls table class
6476  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6477  * @cfg {String} bgcolor Specifies the background color for a table
6478  * @cfg {Number} border Specifies whether the table cells should have borders or not
6479  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6480  * @cfg {Number} cellspacing Specifies the space between cells
6481  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6482  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6483  * @cfg {String} sortable Specifies that the table should be sortable
6484  * @cfg {String} summary Specifies a summary of the content of a table
6485  * @cfg {Number} width Specifies the width of a table
6486  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6487  * 
6488  * @cfg {boolean} striped Should the rows be alternative striped
6489  * @cfg {boolean} bordered Add borders to the table
6490  * @cfg {boolean} hover Add hover highlighting
6491  * @cfg {boolean} condensed Format condensed
6492  * @cfg {boolean} responsive Format condensed
6493  * @cfg {Boolean} loadMask (true|false) default false
6494  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6495  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6496  * @cfg {Boolean} rowSelection (true|false) default false
6497  * @cfg {Boolean} cellSelection (true|false) default false
6498  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6499  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6500  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6501  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6502  
6503  * 
6504  * @constructor
6505  * Create a new Table
6506  * @param {Object} config The config object
6507  */
6508
6509 Roo.bootstrap.Table = function(config){
6510     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6511     
6512   
6513     
6514     // BC...
6515     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6516     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6517     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6518     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6519     
6520     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6521     if (this.sm) {
6522         this.sm.grid = this;
6523         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6524         this.sm = this.selModel;
6525         this.sm.xmodule = this.xmodule || false;
6526     }
6527     
6528     if (this.cm && typeof(this.cm.config) == 'undefined') {
6529         this.colModel = new Roo.grid.ColumnModel(this.cm);
6530         this.cm = this.colModel;
6531         this.cm.xmodule = this.xmodule || false;
6532     }
6533     if (this.store) {
6534         this.store= Roo.factory(this.store, Roo.data);
6535         this.ds = this.store;
6536         this.ds.xmodule = this.xmodule || false;
6537          
6538     }
6539     if (this.footer && this.store) {
6540         this.footer.dataSource = this.ds;
6541         this.footer = Roo.factory(this.footer);
6542     }
6543     
6544     /** @private */
6545     this.addEvents({
6546         /**
6547          * @event cellclick
6548          * Fires when a cell is clicked
6549          * @param {Roo.bootstrap.Table} this
6550          * @param {Roo.Element} el
6551          * @param {Number} rowIndex
6552          * @param {Number} columnIndex
6553          * @param {Roo.EventObject} e
6554          */
6555         "cellclick" : true,
6556         /**
6557          * @event celldblclick
6558          * Fires when a cell is double clicked
6559          * @param {Roo.bootstrap.Table} this
6560          * @param {Roo.Element} el
6561          * @param {Number} rowIndex
6562          * @param {Number} columnIndex
6563          * @param {Roo.EventObject} e
6564          */
6565         "celldblclick" : true,
6566         /**
6567          * @event rowclick
6568          * Fires when a row is clicked
6569          * @param {Roo.bootstrap.Table} this
6570          * @param {Roo.Element} el
6571          * @param {Number} rowIndex
6572          * @param {Roo.EventObject} e
6573          */
6574         "rowclick" : true,
6575         /**
6576          * @event rowdblclick
6577          * Fires when a row is double clicked
6578          * @param {Roo.bootstrap.Table} this
6579          * @param {Roo.Element} el
6580          * @param {Number} rowIndex
6581          * @param {Roo.EventObject} e
6582          */
6583         "rowdblclick" : true,
6584         /**
6585          * @event mouseover
6586          * Fires when a mouseover occur
6587          * @param {Roo.bootstrap.Table} this
6588          * @param {Roo.Element} el
6589          * @param {Number} rowIndex
6590          * @param {Number} columnIndex
6591          * @param {Roo.EventObject} e
6592          */
6593         "mouseover" : true,
6594         /**
6595          * @event mouseout
6596          * Fires when a mouseout occur
6597          * @param {Roo.bootstrap.Table} this
6598          * @param {Roo.Element} el
6599          * @param {Number} rowIndex
6600          * @param {Number} columnIndex
6601          * @param {Roo.EventObject} e
6602          */
6603         "mouseout" : true,
6604         /**
6605          * @event rowclass
6606          * Fires when a row is rendered, so you can change add a style to it.
6607          * @param {Roo.bootstrap.Table} this
6608          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6609          */
6610         'rowclass' : true,
6611           /**
6612          * @event rowsrendered
6613          * Fires when all the  rows have been rendered
6614          * @param {Roo.bootstrap.Table} this
6615          */
6616         'rowsrendered' : true,
6617         /**
6618          * @event contextmenu
6619          * The raw contextmenu event for the entire grid.
6620          * @param {Roo.EventObject} e
6621          */
6622         "contextmenu" : true,
6623         /**
6624          * @event rowcontextmenu
6625          * Fires when a row is right clicked
6626          * @param {Roo.bootstrap.Table} this
6627          * @param {Number} rowIndex
6628          * @param {Roo.EventObject} e
6629          */
6630         "rowcontextmenu" : true,
6631         /**
6632          * @event cellcontextmenu
6633          * Fires when a cell is right clicked
6634          * @param {Roo.bootstrap.Table} this
6635          * @param {Number} rowIndex
6636          * @param {Number} cellIndex
6637          * @param {Roo.EventObject} e
6638          */
6639          "cellcontextmenu" : true,
6640          /**
6641          * @event headercontextmenu
6642          * Fires when a header is right clicked
6643          * @param {Roo.bootstrap.Table} this
6644          * @param {Number} columnIndex
6645          * @param {Roo.EventObject} e
6646          */
6647         "headercontextmenu" : true
6648     });
6649 };
6650
6651 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6652     
6653     cls: false,
6654     align: false,
6655     bgcolor: false,
6656     border: false,
6657     cellpadding: false,
6658     cellspacing: false,
6659     frame: false,
6660     rules: false,
6661     sortable: false,
6662     summary: false,
6663     width: false,
6664     striped : false,
6665     scrollBody : false,
6666     bordered: false,
6667     hover:  false,
6668     condensed : false,
6669     responsive : false,
6670     sm : false,
6671     cm : false,
6672     store : false,
6673     loadMask : false,
6674     footerShow : true,
6675     headerShow : true,
6676   
6677     rowSelection : false,
6678     cellSelection : false,
6679     layout : false,
6680     
6681     // Roo.Element - the tbody
6682     mainBody: false,
6683     // Roo.Element - thead element
6684     mainHead: false,
6685     
6686     container: false, // used by gridpanel...
6687     
6688     lazyLoad : false,
6689     
6690     CSS : Roo.util.CSS,
6691     
6692     auto_hide_footer : false,
6693     
6694     getAutoCreate : function()
6695     {
6696         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6697         
6698         cfg = {
6699             tag: 'table',
6700             cls : 'table',
6701             cn : []
6702         };
6703         if (this.scrollBody) {
6704             cfg.cls += ' table-body-fixed';
6705         }    
6706         if (this.striped) {
6707             cfg.cls += ' table-striped';
6708         }
6709         
6710         if (this.hover) {
6711             cfg.cls += ' table-hover';
6712         }
6713         if (this.bordered) {
6714             cfg.cls += ' table-bordered';
6715         }
6716         if (this.condensed) {
6717             cfg.cls += ' table-condensed';
6718         }
6719         if (this.responsive) {
6720             cfg.cls += ' table-responsive';
6721         }
6722         
6723         if (this.cls) {
6724             cfg.cls+=  ' ' +this.cls;
6725         }
6726         
6727         // this lot should be simplifed...
6728         var _t = this;
6729         var cp = [
6730             'align',
6731             'bgcolor',
6732             'border',
6733             'cellpadding',
6734             'cellspacing',
6735             'frame',
6736             'rules',
6737             'sortable',
6738             'summary',
6739             'width'
6740         ].forEach(function(k) {
6741             if (_t[k]) {
6742                 cfg[k] = _t[k];
6743             }
6744         });
6745         
6746         
6747         if (this.layout) {
6748             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6749         }
6750         
6751         if(this.store || this.cm){
6752             if(this.headerShow){
6753                 cfg.cn.push(this.renderHeader());
6754             }
6755             
6756             cfg.cn.push(this.renderBody());
6757             
6758             if(this.footerShow){
6759                 cfg.cn.push(this.renderFooter());
6760             }
6761             // where does this come from?
6762             //cfg.cls+=  ' TableGrid';
6763         }
6764         
6765         return { cn : [ cfg ] };
6766     },
6767     
6768     initEvents : function()
6769     {   
6770         if(!this.store || !this.cm){
6771             return;
6772         }
6773         if (this.selModel) {
6774             this.selModel.initEvents();
6775         }
6776         
6777         
6778         //Roo.log('initEvents with ds!!!!');
6779         
6780         this.mainBody = this.el.select('tbody', true).first();
6781         this.mainHead = this.el.select('thead', true).first();
6782         this.mainFoot = this.el.select('tfoot', true).first();
6783         
6784         
6785         
6786         var _this = this;
6787         
6788         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6789             e.on('click', _this.sort, _this);
6790         });
6791         
6792         this.mainBody.on("click", this.onClick, this);
6793         this.mainBody.on("dblclick", this.onDblClick, this);
6794         
6795         // why is this done????? = it breaks dialogs??
6796         //this.parent().el.setStyle('position', 'relative');
6797         
6798         
6799         if (this.footer) {
6800             this.footer.parentId = this.id;
6801             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6802             
6803             if(this.lazyLoad){
6804                 this.el.select('tfoot tr td').first().addClass('hide');
6805             }
6806         } 
6807         
6808         if(this.loadMask) {
6809             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6810         }
6811         
6812         this.store.on('load', this.onLoad, this);
6813         this.store.on('beforeload', this.onBeforeLoad, this);
6814         this.store.on('update', this.onUpdate, this);
6815         this.store.on('add', this.onAdd, this);
6816         this.store.on("clear", this.clear, this);
6817         
6818         this.el.on("contextmenu", this.onContextMenu, this);
6819         
6820         this.mainBody.on('scroll', this.onBodyScroll, this);
6821         
6822         this.cm.on("headerchange", this.onHeaderChange, this);
6823         
6824         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6825         
6826     },
6827     
6828     onContextMenu : function(e, t)
6829     {
6830         this.processEvent("contextmenu", e);
6831     },
6832     
6833     processEvent : function(name, e)
6834     {
6835         if (name != 'touchstart' ) {
6836             this.fireEvent(name, e);    
6837         }
6838         
6839         var t = e.getTarget();
6840         
6841         var cell = Roo.get(t);
6842         
6843         if(!cell){
6844             return;
6845         }
6846         
6847         if(cell.findParent('tfoot', false, true)){
6848             return;
6849         }
6850         
6851         if(cell.findParent('thead', false, true)){
6852             
6853             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6854                 cell = Roo.get(t).findParent('th', false, true);
6855                 if (!cell) {
6856                     Roo.log("failed to find th in thead?");
6857                     Roo.log(e.getTarget());
6858                     return;
6859                 }
6860             }
6861             
6862             var cellIndex = cell.dom.cellIndex;
6863             
6864             var ename = name == 'touchstart' ? 'click' : name;
6865             this.fireEvent("header" + ename, this, cellIndex, e);
6866             
6867             return;
6868         }
6869         
6870         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6871             cell = Roo.get(t).findParent('td', false, true);
6872             if (!cell) {
6873                 Roo.log("failed to find th in tbody?");
6874                 Roo.log(e.getTarget());
6875                 return;
6876             }
6877         }
6878         
6879         var row = cell.findParent('tr', false, true);
6880         var cellIndex = cell.dom.cellIndex;
6881         var rowIndex = row.dom.rowIndex - 1;
6882         
6883         if(row !== false){
6884             
6885             this.fireEvent("row" + name, this, rowIndex, e);
6886             
6887             if(cell !== false){
6888             
6889                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6890             }
6891         }
6892         
6893     },
6894     
6895     onMouseover : function(e, el)
6896     {
6897         var cell = Roo.get(el);
6898         
6899         if(!cell){
6900             return;
6901         }
6902         
6903         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6904             cell = cell.findParent('td', false, true);
6905         }
6906         
6907         var row = cell.findParent('tr', false, true);
6908         var cellIndex = cell.dom.cellIndex;
6909         var rowIndex = row.dom.rowIndex - 1; // start from 0
6910         
6911         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6912         
6913     },
6914     
6915     onMouseout : function(e, el)
6916     {
6917         var cell = Roo.get(el);
6918         
6919         if(!cell){
6920             return;
6921         }
6922         
6923         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6924             cell = cell.findParent('td', false, true);
6925         }
6926         
6927         var row = cell.findParent('tr', false, true);
6928         var cellIndex = cell.dom.cellIndex;
6929         var rowIndex = row.dom.rowIndex - 1; // start from 0
6930         
6931         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6932         
6933     },
6934     
6935     onClick : function(e, el)
6936     {
6937         var cell = Roo.get(el);
6938         
6939         if(!cell || (!this.cellSelection && !this.rowSelection)){
6940             return;
6941         }
6942         
6943         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6944             cell = cell.findParent('td', false, true);
6945         }
6946         
6947         if(!cell || typeof(cell) == 'undefined'){
6948             return;
6949         }
6950         
6951         var row = cell.findParent('tr', false, true);
6952         
6953         if(!row || typeof(row) == 'undefined'){
6954             return;
6955         }
6956         
6957         var cellIndex = cell.dom.cellIndex;
6958         var rowIndex = this.getRowIndex(row);
6959         
6960         // why??? - should these not be based on SelectionModel?
6961         if(this.cellSelection){
6962             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6963         }
6964         
6965         if(this.rowSelection){
6966             this.fireEvent('rowclick', this, row, rowIndex, e);
6967         }
6968         
6969         
6970     },
6971         
6972     onDblClick : function(e,el)
6973     {
6974         var cell = Roo.get(el);
6975         
6976         if(!cell || (!this.cellSelection && !this.rowSelection)){
6977             return;
6978         }
6979         
6980         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6981             cell = cell.findParent('td', false, true);
6982         }
6983         
6984         if(!cell || typeof(cell) == 'undefined'){
6985             return;
6986         }
6987         
6988         var row = cell.findParent('tr', false, true);
6989         
6990         if(!row || typeof(row) == 'undefined'){
6991             return;
6992         }
6993         
6994         var cellIndex = cell.dom.cellIndex;
6995         var rowIndex = this.getRowIndex(row);
6996         
6997         if(this.cellSelection){
6998             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6999         }
7000         
7001         if(this.rowSelection){
7002             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7003         }
7004     },
7005     
7006     sort : function(e,el)
7007     {
7008         var col = Roo.get(el);
7009         
7010         if(!col.hasClass('sortable')){
7011             return;
7012         }
7013         
7014         var sort = col.attr('sort');
7015         var dir = 'ASC';
7016         
7017         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7018             dir = 'DESC';
7019         }
7020         
7021         this.store.sortInfo = {field : sort, direction : dir};
7022         
7023         if (this.footer) {
7024             Roo.log("calling footer first");
7025             this.footer.onClick('first');
7026         } else {
7027         
7028             this.store.load({ params : { start : 0 } });
7029         }
7030     },
7031     
7032     renderHeader : function()
7033     {
7034         var header = {
7035             tag: 'thead',
7036             cn : []
7037         };
7038         
7039         var cm = this.cm;
7040         this.totalWidth = 0;
7041         
7042         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7043             
7044             var config = cm.config[i];
7045             
7046             var c = {
7047                 tag: 'th',
7048                 cls : 'x-hcol-' + i,
7049                 style : '',
7050                 html: cm.getColumnHeader(i)
7051             };
7052             
7053             var hh = '';
7054             
7055             if(typeof(config.sortable) != 'undefined' && config.sortable){
7056                 c.cls = 'sortable';
7057                 c.html = '<i class="glyphicon"></i>' + c.html;
7058             }
7059             
7060             // could use BS4 hidden-..-down 
7061             
7062             if(typeof(config.lgHeader) != 'undefined'){
7063                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7064             }
7065             
7066             if(typeof(config.mdHeader) != 'undefined'){
7067                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7068             }
7069             
7070             if(typeof(config.smHeader) != 'undefined'){
7071                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7072             }
7073             
7074             if(typeof(config.xsHeader) != 'undefined'){
7075                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7076             }
7077             
7078             if(hh.length){
7079                 c.html = hh;
7080             }
7081             
7082             if(typeof(config.tooltip) != 'undefined'){
7083                 c.tooltip = config.tooltip;
7084             }
7085             
7086             if(typeof(config.colspan) != 'undefined'){
7087                 c.colspan = config.colspan;
7088             }
7089             
7090             if(typeof(config.hidden) != 'undefined' && config.hidden){
7091                 c.style += ' display:none;';
7092             }
7093             
7094             if(typeof(config.dataIndex) != 'undefined'){
7095                 c.sort = config.dataIndex;
7096             }
7097             
7098            
7099             
7100             if(typeof(config.align) != 'undefined' && config.align.length){
7101                 c.style += ' text-align:' + config.align + ';';
7102             }
7103             
7104             if(typeof(config.width) != 'undefined'){
7105                 c.style += ' width:' + config.width + 'px;';
7106                 this.totalWidth += config.width;
7107             } else {
7108                 this.totalWidth += 100; // assume minimum of 100 per column?
7109             }
7110             
7111             if(typeof(config.cls) != 'undefined'){
7112                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7113             }
7114             
7115             ['xs','sm','md','lg'].map(function(size){
7116                 
7117                 if(typeof(config[size]) == 'undefined'){
7118                     return;
7119                 }
7120                  
7121                 if (!config[size]) { // 0 = hidden
7122                     // BS 4 '0' is treated as hide that column and below.
7123                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7124                     return;
7125                 }
7126                 
7127                 c.cls += ' col-' + size + '-' + config[size] + (
7128                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7129                 );
7130                 
7131                 
7132             });
7133             
7134             header.cn.push(c)
7135         }
7136         
7137         return header;
7138     },
7139     
7140     renderBody : function()
7141     {
7142         var body = {
7143             tag: 'tbody',
7144             cn : [
7145                 {
7146                     tag: 'tr',
7147                     cn : [
7148                         {
7149                             tag : 'td',
7150                             colspan :  this.cm.getColumnCount()
7151                         }
7152                     ]
7153                 }
7154             ]
7155         };
7156         
7157         return body;
7158     },
7159     
7160     renderFooter : function()
7161     {
7162         var footer = {
7163             tag: 'tfoot',
7164             cn : [
7165                 {
7166                     tag: 'tr',
7167                     cn : [
7168                         {
7169                             tag : 'td',
7170                             colspan :  this.cm.getColumnCount()
7171                         }
7172                     ]
7173                 }
7174             ]
7175         };
7176         
7177         return footer;
7178     },
7179     
7180     
7181     
7182     onLoad : function()
7183     {
7184 //        Roo.log('ds onload');
7185         this.clear();
7186         
7187         var _this = this;
7188         var cm = this.cm;
7189         var ds = this.store;
7190         
7191         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7192             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7193             if (_this.store.sortInfo) {
7194                     
7195                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7196                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7197                 }
7198                 
7199                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7200                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7201                 }
7202             }
7203         });
7204         
7205         var tbody =  this.mainBody;
7206               
7207         if(ds.getCount() > 0){
7208             ds.data.each(function(d,rowIndex){
7209                 var row =  this.renderRow(cm, ds, rowIndex);
7210                 
7211                 tbody.createChild(row);
7212                 
7213                 var _this = this;
7214                 
7215                 if(row.cellObjects.length){
7216                     Roo.each(row.cellObjects, function(r){
7217                         _this.renderCellObject(r);
7218                     })
7219                 }
7220                 
7221             }, this);
7222         }
7223         
7224         var tfoot = this.el.select('tfoot', true).first();
7225         
7226         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7227             
7228             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7229             
7230             var total = this.ds.getTotalCount();
7231             
7232             if(this.footer.pageSize < total){
7233                 this.mainFoot.show();
7234             }
7235         }
7236         
7237         Roo.each(this.el.select('tbody td', true).elements, function(e){
7238             e.on('mouseover', _this.onMouseover, _this);
7239         });
7240         
7241         Roo.each(this.el.select('tbody td', true).elements, function(e){
7242             e.on('mouseout', _this.onMouseout, _this);
7243         });
7244         this.fireEvent('rowsrendered', this);
7245         
7246         this.autoSize();
7247     },
7248     
7249     
7250     onUpdate : function(ds,record)
7251     {
7252         this.refreshRow(record);
7253         this.autoSize();
7254     },
7255     
7256     onRemove : function(ds, record, index, isUpdate){
7257         if(isUpdate !== true){
7258             this.fireEvent("beforerowremoved", this, index, record);
7259         }
7260         var bt = this.mainBody.dom;
7261         
7262         var rows = this.el.select('tbody > tr', true).elements;
7263         
7264         if(typeof(rows[index]) != 'undefined'){
7265             bt.removeChild(rows[index].dom);
7266         }
7267         
7268 //        if(bt.rows[index]){
7269 //            bt.removeChild(bt.rows[index]);
7270 //        }
7271         
7272         if(isUpdate !== true){
7273             //this.stripeRows(index);
7274             //this.syncRowHeights(index, index);
7275             //this.layout();
7276             this.fireEvent("rowremoved", this, index, record);
7277         }
7278     },
7279     
7280     onAdd : function(ds, records, rowIndex)
7281     {
7282         //Roo.log('on Add called');
7283         // - note this does not handle multiple adding very well..
7284         var bt = this.mainBody.dom;
7285         for (var i =0 ; i < records.length;i++) {
7286             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7287             //Roo.log(records[i]);
7288             //Roo.log(this.store.getAt(rowIndex+i));
7289             this.insertRow(this.store, rowIndex + i, false);
7290             return;
7291         }
7292         
7293     },
7294     
7295     
7296     refreshRow : function(record){
7297         var ds = this.store, index;
7298         if(typeof record == 'number'){
7299             index = record;
7300             record = ds.getAt(index);
7301         }else{
7302             index = ds.indexOf(record);
7303         }
7304         this.insertRow(ds, index, true);
7305         this.autoSize();
7306         this.onRemove(ds, record, index+1, true);
7307         this.autoSize();
7308         //this.syncRowHeights(index, index);
7309         //this.layout();
7310         this.fireEvent("rowupdated", this, index, record);
7311     },
7312     
7313     insertRow : function(dm, rowIndex, isUpdate){
7314         
7315         if(!isUpdate){
7316             this.fireEvent("beforerowsinserted", this, rowIndex);
7317         }
7318             //var s = this.getScrollState();
7319         var row = this.renderRow(this.cm, this.store, rowIndex);
7320         // insert before rowIndex..
7321         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7322         
7323         var _this = this;
7324                 
7325         if(row.cellObjects.length){
7326             Roo.each(row.cellObjects, function(r){
7327                 _this.renderCellObject(r);
7328             })
7329         }
7330             
7331         if(!isUpdate){
7332             this.fireEvent("rowsinserted", this, rowIndex);
7333             //this.syncRowHeights(firstRow, lastRow);
7334             //this.stripeRows(firstRow);
7335             //this.layout();
7336         }
7337         
7338     },
7339     
7340     
7341     getRowDom : function(rowIndex)
7342     {
7343         var rows = this.el.select('tbody > tr', true).elements;
7344         
7345         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7346         
7347     },
7348     // returns the object tree for a tr..
7349   
7350     
7351     renderRow : function(cm, ds, rowIndex) 
7352     {
7353         var d = ds.getAt(rowIndex);
7354         
7355         var row = {
7356             tag : 'tr',
7357             cls : 'x-row-' + rowIndex,
7358             cn : []
7359         };
7360             
7361         var cellObjects = [];
7362         
7363         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7364             var config = cm.config[i];
7365             
7366             var renderer = cm.getRenderer(i);
7367             var value = '';
7368             var id = false;
7369             
7370             if(typeof(renderer) !== 'undefined'){
7371                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7372             }
7373             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7374             // and are rendered into the cells after the row is rendered - using the id for the element.
7375             
7376             if(typeof(value) === 'object'){
7377                 id = Roo.id();
7378                 cellObjects.push({
7379                     container : id,
7380                     cfg : value 
7381                 })
7382             }
7383             
7384             var rowcfg = {
7385                 record: d,
7386                 rowIndex : rowIndex,
7387                 colIndex : i,
7388                 rowClass : ''
7389             };
7390
7391             this.fireEvent('rowclass', this, rowcfg);
7392             
7393             var td = {
7394                 tag: 'td',
7395                 cls : rowcfg.rowClass + ' x-col-' + i,
7396                 style: '',
7397                 html: (typeof(value) === 'object') ? '' : value
7398             };
7399             
7400             if (id) {
7401                 td.id = id;
7402             }
7403             
7404             if(typeof(config.colspan) != 'undefined'){
7405                 td.colspan = config.colspan;
7406             }
7407             
7408             if(typeof(config.hidden) != 'undefined' && config.hidden){
7409                 td.style += ' display:none;';
7410             }
7411             
7412             if(typeof(config.align) != 'undefined' && config.align.length){
7413                 td.style += ' text-align:' + config.align + ';';
7414             }
7415             if(typeof(config.valign) != 'undefined' && config.valign.length){
7416                 td.style += ' vertical-align:' + config.valign + ';';
7417             }
7418             
7419             if(typeof(config.width) != 'undefined'){
7420                 td.style += ' width:' +  config.width + 'px;';
7421             }
7422             
7423             if(typeof(config.cursor) != 'undefined'){
7424                 td.style += ' cursor:' +  config.cursor + ';';
7425             }
7426             
7427             if(typeof(config.cls) != 'undefined'){
7428                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7429             }
7430             
7431             ['xs','sm','md','lg'].map(function(size){
7432                 
7433                 if(typeof(config[size]) == 'undefined'){
7434                     return;
7435                 }
7436                 
7437                 
7438                   
7439                 if (!config[size]) { // 0 = hidden
7440                     // BS 4 '0' is treated as hide that column and below.
7441                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7442                     return;
7443                 }
7444                 
7445                 td.cls += ' col-' + size + '-' + config[size] + (
7446                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7447                 );
7448                  
7449
7450             });
7451             
7452             row.cn.push(td);
7453            
7454         }
7455         
7456         row.cellObjects = cellObjects;
7457         
7458         return row;
7459           
7460     },
7461     
7462     
7463     
7464     onBeforeLoad : function()
7465     {
7466         
7467     },
7468      /**
7469      * Remove all rows
7470      */
7471     clear : function()
7472     {
7473         this.el.select('tbody', true).first().dom.innerHTML = '';
7474     },
7475     /**
7476      * Show or hide a row.
7477      * @param {Number} rowIndex to show or hide
7478      * @param {Boolean} state hide
7479      */
7480     setRowVisibility : function(rowIndex, state)
7481     {
7482         var bt = this.mainBody.dom;
7483         
7484         var rows = this.el.select('tbody > tr', true).elements;
7485         
7486         if(typeof(rows[rowIndex]) == 'undefined'){
7487             return;
7488         }
7489         rows[rowIndex].dom.style.display = state ? '' : 'none';
7490     },
7491     
7492     
7493     getSelectionModel : function(){
7494         if(!this.selModel){
7495             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7496         }
7497         return this.selModel;
7498     },
7499     /*
7500      * Render the Roo.bootstrap object from renderder
7501      */
7502     renderCellObject : function(r)
7503     {
7504         var _this = this;
7505         
7506         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7507         
7508         var t = r.cfg.render(r.container);
7509         
7510         if(r.cfg.cn){
7511             Roo.each(r.cfg.cn, function(c){
7512                 var child = {
7513                     container: t.getChildContainer(),
7514                     cfg: c
7515                 };
7516                 _this.renderCellObject(child);
7517             })
7518         }
7519     },
7520     
7521     getRowIndex : function(row)
7522     {
7523         var rowIndex = -1;
7524         
7525         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7526             if(el != row){
7527                 return;
7528             }
7529             
7530             rowIndex = index;
7531         });
7532         
7533         return rowIndex;
7534     },
7535      /**
7536      * Returns the grid's underlying element = used by panel.Grid
7537      * @return {Element} The element
7538      */
7539     getGridEl : function(){
7540         return this.el;
7541     },
7542      /**
7543      * Forces a resize - used by panel.Grid
7544      * @return {Element} The element
7545      */
7546     autoSize : function()
7547     {
7548         //var ctr = Roo.get(this.container.dom.parentElement);
7549         var ctr = Roo.get(this.el.dom);
7550         
7551         var thd = this.getGridEl().select('thead',true).first();
7552         var tbd = this.getGridEl().select('tbody', true).first();
7553         var tfd = this.getGridEl().select('tfoot', true).first();
7554         
7555         var cw = ctr.getWidth();
7556         
7557         if (tbd) {
7558             
7559             tbd.setWidth(ctr.getWidth());
7560             // if the body has a max height - and then scrolls - we should perhaps set up the height here
7561             // this needs fixing for various usage - currently only hydra job advers I think..
7562             //tdb.setHeight(
7563             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7564             //); 
7565             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7566             cw -= barsize;
7567         }
7568         cw = Math.max(cw, this.totalWidth);
7569         this.getGridEl().select('tr',true).setWidth(cw);
7570         // resize 'expandable coloumn?
7571         
7572         return; // we doe not have a view in this design..
7573         
7574     },
7575     onBodyScroll: function()
7576     {
7577         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7578         if(this.mainHead){
7579             this.mainHead.setStyle({
7580                 'position' : 'relative',
7581                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7582             });
7583         }
7584         
7585         if(this.lazyLoad){
7586             
7587             var scrollHeight = this.mainBody.dom.scrollHeight;
7588             
7589             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7590             
7591             var height = this.mainBody.getHeight();
7592             
7593             if(scrollHeight - height == scrollTop) {
7594                 
7595                 var total = this.ds.getTotalCount();
7596                 
7597                 if(this.footer.cursor + this.footer.pageSize < total){
7598                     
7599                     this.footer.ds.load({
7600                         params : {
7601                             start : this.footer.cursor + this.footer.pageSize,
7602                             limit : this.footer.pageSize
7603                         },
7604                         add : true
7605                     });
7606                 }
7607             }
7608             
7609         }
7610     },
7611     
7612     onHeaderChange : function()
7613     {
7614         var header = this.renderHeader();
7615         var table = this.el.select('table', true).first();
7616         
7617         this.mainHead.remove();
7618         this.mainHead = table.createChild(header, this.mainBody, false);
7619     },
7620     
7621     onHiddenChange : function(colModel, colIndex, hidden)
7622     {
7623         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7624         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7625         
7626         this.CSS.updateRule(thSelector, "display", "");
7627         this.CSS.updateRule(tdSelector, "display", "");
7628         
7629         if(hidden){
7630             this.CSS.updateRule(thSelector, "display", "none");
7631             this.CSS.updateRule(tdSelector, "display", "none");
7632         }
7633         
7634         this.onHeaderChange();
7635         this.onLoad();
7636     },
7637     
7638     setColumnWidth: function(col_index, width)
7639     {
7640         // width = "md-2 xs-2..."
7641         if(!this.colModel.config[col_index]) {
7642             return;
7643         }
7644         
7645         var w = width.split(" ");
7646         
7647         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7648         
7649         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7650         
7651         
7652         for(var j = 0; j < w.length; j++) {
7653             
7654             if(!w[j]) {
7655                 continue;
7656             }
7657             
7658             var size_cls = w[j].split("-");
7659             
7660             if(!Number.isInteger(size_cls[1] * 1)) {
7661                 continue;
7662             }
7663             
7664             if(!this.colModel.config[col_index][size_cls[0]]) {
7665                 continue;
7666             }
7667             
7668             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7669                 continue;
7670             }
7671             
7672             h_row[0].classList.replace(
7673                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7674                 "col-"+size_cls[0]+"-"+size_cls[1]
7675             );
7676             
7677             for(var i = 0; i < rows.length; i++) {
7678                 
7679                 var size_cls = w[j].split("-");
7680                 
7681                 if(!Number.isInteger(size_cls[1] * 1)) {
7682                     continue;
7683                 }
7684                 
7685                 if(!this.colModel.config[col_index][size_cls[0]]) {
7686                     continue;
7687                 }
7688                 
7689                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7690                     continue;
7691                 }
7692                 
7693                 rows[i].classList.replace(
7694                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7695                     "col-"+size_cls[0]+"-"+size_cls[1]
7696                 );
7697             }
7698             
7699             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7700         }
7701     }
7702 });
7703
7704  
7705
7706  /*
7707  * - LGPL
7708  *
7709  * table cell
7710  * 
7711  */
7712
7713 /**
7714  * @class Roo.bootstrap.TableCell
7715  * @extends Roo.bootstrap.Component
7716  * Bootstrap TableCell class
7717  * @cfg {String} html cell contain text
7718  * @cfg {String} cls cell class
7719  * @cfg {String} tag cell tag (td|th) default td
7720  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7721  * @cfg {String} align Aligns the content in a cell
7722  * @cfg {String} axis Categorizes cells
7723  * @cfg {String} bgcolor Specifies the background color of a cell
7724  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7725  * @cfg {Number} colspan Specifies the number of columns a cell should span
7726  * @cfg {String} headers Specifies one or more header cells a cell is related to
7727  * @cfg {Number} height Sets the height of a cell
7728  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7729  * @cfg {Number} rowspan Sets the number of rows a cell should span
7730  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7731  * @cfg {String} valign Vertical aligns the content in a cell
7732  * @cfg {Number} width Specifies the width of a cell
7733  * 
7734  * @constructor
7735  * Create a new TableCell
7736  * @param {Object} config The config object
7737  */
7738
7739 Roo.bootstrap.TableCell = function(config){
7740     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7741 };
7742
7743 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7744     
7745     html: false,
7746     cls: false,
7747     tag: false,
7748     abbr: false,
7749     align: false,
7750     axis: false,
7751     bgcolor: false,
7752     charoff: false,
7753     colspan: false,
7754     headers: false,
7755     height: false,
7756     nowrap: false,
7757     rowspan: false,
7758     scope: false,
7759     valign: false,
7760     width: false,
7761     
7762     
7763     getAutoCreate : function(){
7764         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7765         
7766         cfg = {
7767             tag: 'td'
7768         };
7769         
7770         if(this.tag){
7771             cfg.tag = this.tag;
7772         }
7773         
7774         if (this.html) {
7775             cfg.html=this.html
7776         }
7777         if (this.cls) {
7778             cfg.cls=this.cls
7779         }
7780         if (this.abbr) {
7781             cfg.abbr=this.abbr
7782         }
7783         if (this.align) {
7784             cfg.align=this.align
7785         }
7786         if (this.axis) {
7787             cfg.axis=this.axis
7788         }
7789         if (this.bgcolor) {
7790             cfg.bgcolor=this.bgcolor
7791         }
7792         if (this.charoff) {
7793             cfg.charoff=this.charoff
7794         }
7795         if (this.colspan) {
7796             cfg.colspan=this.colspan
7797         }
7798         if (this.headers) {
7799             cfg.headers=this.headers
7800         }
7801         if (this.height) {
7802             cfg.height=this.height
7803         }
7804         if (this.nowrap) {
7805             cfg.nowrap=this.nowrap
7806         }
7807         if (this.rowspan) {
7808             cfg.rowspan=this.rowspan
7809         }
7810         if (this.scope) {
7811             cfg.scope=this.scope
7812         }
7813         if (this.valign) {
7814             cfg.valign=this.valign
7815         }
7816         if (this.width) {
7817             cfg.width=this.width
7818         }
7819         
7820         
7821         return cfg;
7822     }
7823    
7824 });
7825
7826  
7827
7828  /*
7829  * - LGPL
7830  *
7831  * table row
7832  * 
7833  */
7834
7835 /**
7836  * @class Roo.bootstrap.TableRow
7837  * @extends Roo.bootstrap.Component
7838  * Bootstrap TableRow class
7839  * @cfg {String} cls row class
7840  * @cfg {String} align Aligns the content in a table row
7841  * @cfg {String} bgcolor Specifies a background color for a table row
7842  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7843  * @cfg {String} valign Vertical aligns the content in a table row
7844  * 
7845  * @constructor
7846  * Create a new TableRow
7847  * @param {Object} config The config object
7848  */
7849
7850 Roo.bootstrap.TableRow = function(config){
7851     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7852 };
7853
7854 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7855     
7856     cls: false,
7857     align: false,
7858     bgcolor: false,
7859     charoff: false,
7860     valign: false,
7861     
7862     getAutoCreate : function(){
7863         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7864         
7865         cfg = {
7866             tag: 'tr'
7867         };
7868             
7869         if(this.cls){
7870             cfg.cls = this.cls;
7871         }
7872         if(this.align){
7873             cfg.align = this.align;
7874         }
7875         if(this.bgcolor){
7876             cfg.bgcolor = this.bgcolor;
7877         }
7878         if(this.charoff){
7879             cfg.charoff = this.charoff;
7880         }
7881         if(this.valign){
7882             cfg.valign = this.valign;
7883         }
7884         
7885         return cfg;
7886     }
7887    
7888 });
7889
7890  
7891
7892  /*
7893  * - LGPL
7894  *
7895  * table body
7896  * 
7897  */
7898
7899 /**
7900  * @class Roo.bootstrap.TableBody
7901  * @extends Roo.bootstrap.Component
7902  * Bootstrap TableBody class
7903  * @cfg {String} cls element class
7904  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7905  * @cfg {String} align Aligns the content inside the element
7906  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7907  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7908  * 
7909  * @constructor
7910  * Create a new TableBody
7911  * @param {Object} config The config object
7912  */
7913
7914 Roo.bootstrap.TableBody = function(config){
7915     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7916 };
7917
7918 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7919     
7920     cls: false,
7921     tag: false,
7922     align: false,
7923     charoff: false,
7924     valign: false,
7925     
7926     getAutoCreate : function(){
7927         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7928         
7929         cfg = {
7930             tag: 'tbody'
7931         };
7932             
7933         if (this.cls) {
7934             cfg.cls=this.cls
7935         }
7936         if(this.tag){
7937             cfg.tag = this.tag;
7938         }
7939         
7940         if(this.align){
7941             cfg.align = this.align;
7942         }
7943         if(this.charoff){
7944             cfg.charoff = this.charoff;
7945         }
7946         if(this.valign){
7947             cfg.valign = this.valign;
7948         }
7949         
7950         return cfg;
7951     }
7952     
7953     
7954 //    initEvents : function()
7955 //    {
7956 //        
7957 //        if(!this.store){
7958 //            return;
7959 //        }
7960 //        
7961 //        this.store = Roo.factory(this.store, Roo.data);
7962 //        this.store.on('load', this.onLoad, this);
7963 //        
7964 //        this.store.load();
7965 //        
7966 //    },
7967 //    
7968 //    onLoad: function () 
7969 //    {   
7970 //        this.fireEvent('load', this);
7971 //    }
7972 //    
7973 //   
7974 });
7975
7976  
7977
7978  /*
7979  * Based on:
7980  * Ext JS Library 1.1.1
7981  * Copyright(c) 2006-2007, Ext JS, LLC.
7982  *
7983  * Originally Released Under LGPL - original licence link has changed is not relivant.
7984  *
7985  * Fork - LGPL
7986  * <script type="text/javascript">
7987  */
7988
7989 // as we use this in bootstrap.
7990 Roo.namespace('Roo.form');
7991  /**
7992  * @class Roo.form.Action
7993  * Internal Class used to handle form actions
7994  * @constructor
7995  * @param {Roo.form.BasicForm} el The form element or its id
7996  * @param {Object} config Configuration options
7997  */
7998
7999  
8000  
8001 // define the action interface
8002 Roo.form.Action = function(form, options){
8003     this.form = form;
8004     this.options = options || {};
8005 };
8006 /**
8007  * Client Validation Failed
8008  * @const 
8009  */
8010 Roo.form.Action.CLIENT_INVALID = 'client';
8011 /**
8012  * Server Validation Failed
8013  * @const 
8014  */
8015 Roo.form.Action.SERVER_INVALID = 'server';
8016  /**
8017  * Connect to Server Failed
8018  * @const 
8019  */
8020 Roo.form.Action.CONNECT_FAILURE = 'connect';
8021 /**
8022  * Reading Data from Server Failed
8023  * @const 
8024  */
8025 Roo.form.Action.LOAD_FAILURE = 'load';
8026
8027 Roo.form.Action.prototype = {
8028     type : 'default',
8029     failureType : undefined,
8030     response : undefined,
8031     result : undefined,
8032
8033     // interface method
8034     run : function(options){
8035
8036     },
8037
8038     // interface method
8039     success : function(response){
8040
8041     },
8042
8043     // interface method
8044     handleResponse : function(response){
8045
8046     },
8047
8048     // default connection failure
8049     failure : function(response){
8050         
8051         this.response = response;
8052         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8053         this.form.afterAction(this, false);
8054     },
8055
8056     processResponse : function(response){
8057         this.response = response;
8058         if(!response.responseText){
8059             return true;
8060         }
8061         this.result = this.handleResponse(response);
8062         return this.result;
8063     },
8064
8065     // utility functions used internally
8066     getUrl : function(appendParams){
8067         var url = this.options.url || this.form.url || this.form.el.dom.action;
8068         if(appendParams){
8069             var p = this.getParams();
8070             if(p){
8071                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8072             }
8073         }
8074         return url;
8075     },
8076
8077     getMethod : function(){
8078         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8079     },
8080
8081     getParams : function(){
8082         var bp = this.form.baseParams;
8083         var p = this.options.params;
8084         if(p){
8085             if(typeof p == "object"){
8086                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8087             }else if(typeof p == 'string' && bp){
8088                 p += '&' + Roo.urlEncode(bp);
8089             }
8090         }else if(bp){
8091             p = Roo.urlEncode(bp);
8092         }
8093         return p;
8094     },
8095
8096     createCallback : function(){
8097         return {
8098             success: this.success,
8099             failure: this.failure,
8100             scope: this,
8101             timeout: (this.form.timeout*1000),
8102             upload: this.form.fileUpload ? this.success : undefined
8103         };
8104     }
8105 };
8106
8107 Roo.form.Action.Submit = function(form, options){
8108     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8109 };
8110
8111 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8112     type : 'submit',
8113
8114     haveProgress : false,
8115     uploadComplete : false,
8116     
8117     // uploadProgress indicator.
8118     uploadProgress : function()
8119     {
8120         if (!this.form.progressUrl) {
8121             return;
8122         }
8123         
8124         if (!this.haveProgress) {
8125             Roo.MessageBox.progress("Uploading", "Uploading");
8126         }
8127         if (this.uploadComplete) {
8128            Roo.MessageBox.hide();
8129            return;
8130         }
8131         
8132         this.haveProgress = true;
8133    
8134         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8135         
8136         var c = new Roo.data.Connection();
8137         c.request({
8138             url : this.form.progressUrl,
8139             params: {
8140                 id : uid
8141             },
8142             method: 'GET',
8143             success : function(req){
8144                //console.log(data);
8145                 var rdata = false;
8146                 var edata;
8147                 try  {
8148                    rdata = Roo.decode(req.responseText)
8149                 } catch (e) {
8150                     Roo.log("Invalid data from server..");
8151                     Roo.log(edata);
8152                     return;
8153                 }
8154                 if (!rdata || !rdata.success) {
8155                     Roo.log(rdata);
8156                     Roo.MessageBox.alert(Roo.encode(rdata));
8157                     return;
8158                 }
8159                 var data = rdata.data;
8160                 
8161                 if (this.uploadComplete) {
8162                    Roo.MessageBox.hide();
8163                    return;
8164                 }
8165                    
8166                 if (data){
8167                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8168                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8169                     );
8170                 }
8171                 this.uploadProgress.defer(2000,this);
8172             },
8173        
8174             failure: function(data) {
8175                 Roo.log('progress url failed ');
8176                 Roo.log(data);
8177             },
8178             scope : this
8179         });
8180            
8181     },
8182     
8183     
8184     run : function()
8185     {
8186         // run get Values on the form, so it syncs any secondary forms.
8187         this.form.getValues();
8188         
8189         var o = this.options;
8190         var method = this.getMethod();
8191         var isPost = method == 'POST';
8192         if(o.clientValidation === false || this.form.isValid()){
8193             
8194             if (this.form.progressUrl) {
8195                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8196                     (new Date() * 1) + '' + Math.random());
8197                     
8198             } 
8199             
8200             
8201             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8202                 form:this.form.el.dom,
8203                 url:this.getUrl(!isPost),
8204                 method: method,
8205                 params:isPost ? this.getParams() : null,
8206                 isUpload: this.form.fileUpload,
8207                 formData : this.form.formData
8208             }));
8209             
8210             this.uploadProgress();
8211
8212         }else if (o.clientValidation !== false){ // client validation failed
8213             this.failureType = Roo.form.Action.CLIENT_INVALID;
8214             this.form.afterAction(this, false);
8215         }
8216     },
8217
8218     success : function(response)
8219     {
8220         this.uploadComplete= true;
8221         if (this.haveProgress) {
8222             Roo.MessageBox.hide();
8223         }
8224         
8225         
8226         var result = this.processResponse(response);
8227         if(result === true || result.success){
8228             this.form.afterAction(this, true);
8229             return;
8230         }
8231         if(result.errors){
8232             this.form.markInvalid(result.errors);
8233             this.failureType = Roo.form.Action.SERVER_INVALID;
8234         }
8235         this.form.afterAction(this, false);
8236     },
8237     failure : function(response)
8238     {
8239         this.uploadComplete= true;
8240         if (this.haveProgress) {
8241             Roo.MessageBox.hide();
8242         }
8243         
8244         this.response = response;
8245         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8246         this.form.afterAction(this, false);
8247     },
8248     
8249     handleResponse : function(response){
8250         if(this.form.errorReader){
8251             var rs = this.form.errorReader.read(response);
8252             var errors = [];
8253             if(rs.records){
8254                 for(var i = 0, len = rs.records.length; i < len; i++) {
8255                     var r = rs.records[i];
8256                     errors[i] = r.data;
8257                 }
8258             }
8259             if(errors.length < 1){
8260                 errors = null;
8261             }
8262             return {
8263                 success : rs.success,
8264                 errors : errors
8265             };
8266         }
8267         var ret = false;
8268         try {
8269             ret = Roo.decode(response.responseText);
8270         } catch (e) {
8271             ret = {
8272                 success: false,
8273                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8274                 errors : []
8275             };
8276         }
8277         return ret;
8278         
8279     }
8280 });
8281
8282
8283 Roo.form.Action.Load = function(form, options){
8284     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8285     this.reader = this.form.reader;
8286 };
8287
8288 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8289     type : 'load',
8290
8291     run : function(){
8292         
8293         Roo.Ajax.request(Roo.apply(
8294                 this.createCallback(), {
8295                     method:this.getMethod(),
8296                     url:this.getUrl(false),
8297                     params:this.getParams()
8298         }));
8299     },
8300
8301     success : function(response){
8302         
8303         var result = this.processResponse(response);
8304         if(result === true || !result.success || !result.data){
8305             this.failureType = Roo.form.Action.LOAD_FAILURE;
8306             this.form.afterAction(this, false);
8307             return;
8308         }
8309         this.form.clearInvalid();
8310         this.form.setValues(result.data);
8311         this.form.afterAction(this, true);
8312     },
8313
8314     handleResponse : function(response){
8315         if(this.form.reader){
8316             var rs = this.form.reader.read(response);
8317             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8318             return {
8319                 success : rs.success,
8320                 data : data
8321             };
8322         }
8323         return Roo.decode(response.responseText);
8324     }
8325 });
8326
8327 Roo.form.Action.ACTION_TYPES = {
8328     'load' : Roo.form.Action.Load,
8329     'submit' : Roo.form.Action.Submit
8330 };/*
8331  * - LGPL
8332  *
8333  * form
8334  *
8335  */
8336
8337 /**
8338  * @class Roo.bootstrap.Form
8339  * @extends Roo.bootstrap.Component
8340  * Bootstrap Form class
8341  * @cfg {String} method  GET | POST (default POST)
8342  * @cfg {String} labelAlign top | left (default top)
8343  * @cfg {String} align left  | right - for navbars
8344  * @cfg {Boolean} loadMask load mask when submit (default true)
8345
8346  *
8347  * @constructor
8348  * Create a new Form
8349  * @param {Object} config The config object
8350  */
8351
8352
8353 Roo.bootstrap.Form = function(config){
8354     
8355     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8356     
8357     Roo.bootstrap.Form.popover.apply();
8358     
8359     this.addEvents({
8360         /**
8361          * @event clientvalidation
8362          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8363          * @param {Form} this
8364          * @param {Boolean} valid true if the form has passed client-side validation
8365          */
8366         clientvalidation: true,
8367         /**
8368          * @event beforeaction
8369          * Fires before any action is performed. Return false to cancel the action.
8370          * @param {Form} this
8371          * @param {Action} action The action to be performed
8372          */
8373         beforeaction: true,
8374         /**
8375          * @event actionfailed
8376          * Fires when an action fails.
8377          * @param {Form} this
8378          * @param {Action} action The action that failed
8379          */
8380         actionfailed : true,
8381         /**
8382          * @event actioncomplete
8383          * Fires when an action is completed.
8384          * @param {Form} this
8385          * @param {Action} action The action that completed
8386          */
8387         actioncomplete : true
8388     });
8389 };
8390
8391 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8392
8393      /**
8394      * @cfg {String} method
8395      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8396      */
8397     method : 'POST',
8398     /**
8399      * @cfg {String} url
8400      * The URL to use for form actions if one isn't supplied in the action options.
8401      */
8402     /**
8403      * @cfg {Boolean} fileUpload
8404      * Set to true if this form is a file upload.
8405      */
8406
8407     /**
8408      * @cfg {Object} baseParams
8409      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8410      */
8411
8412     /**
8413      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8414      */
8415     timeout: 30,
8416     /**
8417      * @cfg {Sting} align (left|right) for navbar forms
8418      */
8419     align : 'left',
8420
8421     // private
8422     activeAction : null,
8423
8424     /**
8425      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8426      * element by passing it or its id or mask the form itself by passing in true.
8427      * @type Mixed
8428      */
8429     waitMsgTarget : false,
8430
8431     loadMask : true,
8432     
8433     /**
8434      * @cfg {Boolean} errorMask (true|false) default false
8435      */
8436     errorMask : false,
8437     
8438     /**
8439      * @cfg {Number} maskOffset Default 100
8440      */
8441     maskOffset : 100,
8442     
8443     /**
8444      * @cfg {Boolean} maskBody
8445      */
8446     maskBody : false,
8447
8448     getAutoCreate : function(){
8449
8450         var cfg = {
8451             tag: 'form',
8452             method : this.method || 'POST',
8453             id : this.id || Roo.id(),
8454             cls : ''
8455         };
8456         if (this.parent().xtype.match(/^Nav/)) {
8457             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8458
8459         }
8460
8461         if (this.labelAlign == 'left' ) {
8462             cfg.cls += ' form-horizontal';
8463         }
8464
8465
8466         return cfg;
8467     },
8468     initEvents : function()
8469     {
8470         this.el.on('submit', this.onSubmit, this);
8471         // this was added as random key presses on the form where triggering form submit.
8472         this.el.on('keypress', function(e) {
8473             if (e.getCharCode() != 13) {
8474                 return true;
8475             }
8476             // we might need to allow it for textareas.. and some other items.
8477             // check e.getTarget().
8478
8479             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8480                 return true;
8481             }
8482
8483             Roo.log("keypress blocked");
8484
8485             e.preventDefault();
8486             return false;
8487         });
8488         
8489     },
8490     // private
8491     onSubmit : function(e){
8492         e.stopEvent();
8493     },
8494
8495      /**
8496      * Returns true if client-side validation on the form is successful.
8497      * @return Boolean
8498      */
8499     isValid : function(){
8500         var items = this.getItems();
8501         var valid = true;
8502         var target = false;
8503         
8504         items.each(function(f){
8505             
8506             if(f.validate()){
8507                 return;
8508             }
8509             
8510             Roo.log('invalid field: ' + f.name);
8511             
8512             valid = false;
8513
8514             if(!target && f.el.isVisible(true)){
8515                 target = f;
8516             }
8517            
8518         });
8519         
8520         if(this.errorMask && !valid){
8521             Roo.bootstrap.Form.popover.mask(this, target);
8522         }
8523         
8524         return valid;
8525     },
8526     
8527     /**
8528      * Returns true if any fields in this form have changed since their original load.
8529      * @return Boolean
8530      */
8531     isDirty : function(){
8532         var dirty = false;
8533         var items = this.getItems();
8534         items.each(function(f){
8535            if(f.isDirty()){
8536                dirty = true;
8537                return false;
8538            }
8539            return true;
8540         });
8541         return dirty;
8542     },
8543      /**
8544      * Performs a predefined action (submit or load) or custom actions you define on this form.
8545      * @param {String} actionName The name of the action type
8546      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8547      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8548      * accept other config options):
8549      * <pre>
8550 Property          Type             Description
8551 ----------------  ---------------  ----------------------------------------------------------------------------------
8552 url               String           The url for the action (defaults to the form's url)
8553 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8554 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8555 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8556                                    validate the form on the client (defaults to false)
8557      * </pre>
8558      * @return {BasicForm} this
8559      */
8560     doAction : function(action, options){
8561         if(typeof action == 'string'){
8562             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8563         }
8564         if(this.fireEvent('beforeaction', this, action) !== false){
8565             this.beforeAction(action);
8566             action.run.defer(100, action);
8567         }
8568         return this;
8569     },
8570
8571     // private
8572     beforeAction : function(action){
8573         var o = action.options;
8574         
8575         if(this.loadMask){
8576             
8577             if(this.maskBody){
8578                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8579             } else {
8580                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8581             }
8582         }
8583         // not really supported yet.. ??
8584
8585         //if(this.waitMsgTarget === true){
8586         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8587         //}else if(this.waitMsgTarget){
8588         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8589         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8590         //}else {
8591         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8592        // }
8593
8594     },
8595
8596     // private
8597     afterAction : function(action, success){
8598         this.activeAction = null;
8599         var o = action.options;
8600
8601         if(this.loadMask){
8602             
8603             if(this.maskBody){
8604                 Roo.get(document.body).unmask();
8605             } else {
8606                 this.el.unmask();
8607             }
8608         }
8609         
8610         //if(this.waitMsgTarget === true){
8611 //            this.el.unmask();
8612         //}else if(this.waitMsgTarget){
8613         //    this.waitMsgTarget.unmask();
8614         //}else{
8615         //    Roo.MessageBox.updateProgress(1);
8616         //    Roo.MessageBox.hide();
8617        // }
8618         //
8619         if(success){
8620             if(o.reset){
8621                 this.reset();
8622             }
8623             Roo.callback(o.success, o.scope, [this, action]);
8624             this.fireEvent('actioncomplete', this, action);
8625
8626         }else{
8627
8628             // failure condition..
8629             // we have a scenario where updates need confirming.
8630             // eg. if a locking scenario exists..
8631             // we look for { errors : { needs_confirm : true }} in the response.
8632             if (
8633                 (typeof(action.result) != 'undefined')  &&
8634                 (typeof(action.result.errors) != 'undefined')  &&
8635                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8636            ){
8637                 var _t = this;
8638                 Roo.log("not supported yet");
8639                  /*
8640
8641                 Roo.MessageBox.confirm(
8642                     "Change requires confirmation",
8643                     action.result.errorMsg,
8644                     function(r) {
8645                         if (r != 'yes') {
8646                             return;
8647                         }
8648                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8649                     }
8650
8651                 );
8652                 */
8653
8654
8655                 return;
8656             }
8657
8658             Roo.callback(o.failure, o.scope, [this, action]);
8659             // show an error message if no failed handler is set..
8660             if (!this.hasListener('actionfailed')) {
8661                 Roo.log("need to add dialog support");
8662                 /*
8663                 Roo.MessageBox.alert("Error",
8664                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8665                         action.result.errorMsg :
8666                         "Saving Failed, please check your entries or try again"
8667                 );
8668                 */
8669             }
8670
8671             this.fireEvent('actionfailed', this, action);
8672         }
8673
8674     },
8675     /**
8676      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8677      * @param {String} id The value to search for
8678      * @return Field
8679      */
8680     findField : function(id){
8681         var items = this.getItems();
8682         var field = items.get(id);
8683         if(!field){
8684              items.each(function(f){
8685                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8686                     field = f;
8687                     return false;
8688                 }
8689                 return true;
8690             });
8691         }
8692         return field || null;
8693     },
8694      /**
8695      * Mark fields in this form invalid in bulk.
8696      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8697      * @return {BasicForm} this
8698      */
8699     markInvalid : function(errors){
8700         if(errors instanceof Array){
8701             for(var i = 0, len = errors.length; i < len; i++){
8702                 var fieldError = errors[i];
8703                 var f = this.findField(fieldError.id);
8704                 if(f){
8705                     f.markInvalid(fieldError.msg);
8706                 }
8707             }
8708         }else{
8709             var field, id;
8710             for(id in errors){
8711                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8712                     field.markInvalid(errors[id]);
8713                 }
8714             }
8715         }
8716         //Roo.each(this.childForms || [], function (f) {
8717         //    f.markInvalid(errors);
8718         //});
8719
8720         return this;
8721     },
8722
8723     /**
8724      * Set values for fields in this form in bulk.
8725      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8726      * @return {BasicForm} this
8727      */
8728     setValues : function(values){
8729         if(values instanceof Array){ // array of objects
8730             for(var i = 0, len = values.length; i < len; i++){
8731                 var v = values[i];
8732                 var f = this.findField(v.id);
8733                 if(f){
8734                     f.setValue(v.value);
8735                     if(this.trackResetOnLoad){
8736                         f.originalValue = f.getValue();
8737                     }
8738                 }
8739             }
8740         }else{ // object hash
8741             var field, id;
8742             for(id in values){
8743                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8744
8745                     if (field.setFromData &&
8746                         field.valueField &&
8747                         field.displayField &&
8748                         // combos' with local stores can
8749                         // be queried via setValue()
8750                         // to set their value..
8751                         (field.store && !field.store.isLocal)
8752                         ) {
8753                         // it's a combo
8754                         var sd = { };
8755                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8756                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8757                         field.setFromData(sd);
8758
8759                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8760                         
8761                         field.setFromData(values);
8762                         
8763                     } else {
8764                         field.setValue(values[id]);
8765                     }
8766
8767
8768                     if(this.trackResetOnLoad){
8769                         field.originalValue = field.getValue();
8770                     }
8771                 }
8772             }
8773         }
8774
8775         //Roo.each(this.childForms || [], function (f) {
8776         //    f.setValues(values);
8777         //});
8778
8779         return this;
8780     },
8781
8782     /**
8783      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8784      * they are returned as an array.
8785      * @param {Boolean} asString
8786      * @return {Object}
8787      */
8788     getValues : function(asString){
8789         //if (this.childForms) {
8790             // copy values from the child forms
8791         //    Roo.each(this.childForms, function (f) {
8792         //        this.setValues(f.getValues());
8793         //    }, this);
8794         //}
8795
8796
8797
8798         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8799         if(asString === true){
8800             return fs;
8801         }
8802         return Roo.urlDecode(fs);
8803     },
8804
8805     /**
8806      * Returns the fields in this form as an object with key/value pairs.
8807      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8808      * @return {Object}
8809      */
8810     getFieldValues : function(with_hidden)
8811     {
8812         var items = this.getItems();
8813         var ret = {};
8814         items.each(function(f){
8815             
8816             if (!f.getName()) {
8817                 return;
8818             }
8819             
8820             var v = f.getValue();
8821             
8822             if (f.inputType =='radio') {
8823                 if (typeof(ret[f.getName()]) == 'undefined') {
8824                     ret[f.getName()] = ''; // empty..
8825                 }
8826
8827                 if (!f.el.dom.checked) {
8828                     return;
8829
8830                 }
8831                 v = f.el.dom.value;
8832
8833             }
8834             
8835             if(f.xtype == 'MoneyField'){
8836                 ret[f.currencyName] = f.getCurrency();
8837             }
8838
8839             // not sure if this supported any more..
8840             if ((typeof(v) == 'object') && f.getRawValue) {
8841                 v = f.getRawValue() ; // dates..
8842             }
8843             // combo boxes where name != hiddenName...
8844             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8845                 ret[f.name] = f.getRawValue();
8846             }
8847             ret[f.getName()] = v;
8848         });
8849
8850         return ret;
8851     },
8852
8853     /**
8854      * Clears all invalid messages in this form.
8855      * @return {BasicForm} this
8856      */
8857     clearInvalid : function(){
8858         var items = this.getItems();
8859
8860         items.each(function(f){
8861            f.clearInvalid();
8862         });
8863
8864         return this;
8865     },
8866
8867     /**
8868      * Resets this form.
8869      * @return {BasicForm} this
8870      */
8871     reset : function(){
8872         var items = this.getItems();
8873         items.each(function(f){
8874             f.reset();
8875         });
8876
8877         Roo.each(this.childForms || [], function (f) {
8878             f.reset();
8879         });
8880
8881
8882         return this;
8883     },
8884     
8885     getItems : function()
8886     {
8887         var r=new Roo.util.MixedCollection(false, function(o){
8888             return o.id || (o.id = Roo.id());
8889         });
8890         var iter = function(el) {
8891             if (el.inputEl) {
8892                 r.add(el);
8893             }
8894             if (!el.items) {
8895                 return;
8896             }
8897             Roo.each(el.items,function(e) {
8898                 iter(e);
8899             });
8900         };
8901
8902         iter(this);
8903         return r;
8904     },
8905     
8906     hideFields : function(items)
8907     {
8908         Roo.each(items, function(i){
8909             
8910             var f = this.findField(i);
8911             
8912             if(!f){
8913                 return;
8914             }
8915             
8916             f.hide();
8917             
8918         }, this);
8919     },
8920     
8921     showFields : function(items)
8922     {
8923         Roo.each(items, function(i){
8924             
8925             var f = this.findField(i);
8926             
8927             if(!f){
8928                 return;
8929             }
8930             
8931             f.show();
8932             
8933         }, this);
8934     }
8935
8936 });
8937
8938 Roo.apply(Roo.bootstrap.Form, {
8939     
8940     popover : {
8941         
8942         padding : 5,
8943         
8944         isApplied : false,
8945         
8946         isMasked : false,
8947         
8948         form : false,
8949         
8950         target : false,
8951         
8952         toolTip : false,
8953         
8954         intervalID : false,
8955         
8956         maskEl : false,
8957         
8958         apply : function()
8959         {
8960             if(this.isApplied){
8961                 return;
8962             }
8963             
8964             this.maskEl = {
8965                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8966                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8967                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8968                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8969             };
8970             
8971             this.maskEl.top.enableDisplayMode("block");
8972             this.maskEl.left.enableDisplayMode("block");
8973             this.maskEl.bottom.enableDisplayMode("block");
8974             this.maskEl.right.enableDisplayMode("block");
8975             
8976             this.toolTip = new Roo.bootstrap.Tooltip({
8977                 cls : 'roo-form-error-popover',
8978                 alignment : {
8979                     'left' : ['r-l', [-2,0], 'right'],
8980                     'right' : ['l-r', [2,0], 'left'],
8981                     'bottom' : ['tl-bl', [0,2], 'top'],
8982                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8983                 }
8984             });
8985             
8986             this.toolTip.render(Roo.get(document.body));
8987
8988             this.toolTip.el.enableDisplayMode("block");
8989             
8990             Roo.get(document.body).on('click', function(){
8991                 this.unmask();
8992             }, this);
8993             
8994             Roo.get(document.body).on('touchstart', function(){
8995                 this.unmask();
8996             }, this);
8997             
8998             this.isApplied = true
8999         },
9000         
9001         mask : function(form, target)
9002         {
9003             this.form = form;
9004             
9005             this.target = target;
9006             
9007             if(!this.form.errorMask || !target.el){
9008                 return;
9009             }
9010             
9011             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9012             
9013             Roo.log(scrollable);
9014             
9015             var ot = this.target.el.calcOffsetsTo(scrollable);
9016             
9017             var scrollTo = ot[1] - this.form.maskOffset;
9018             
9019             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9020             
9021             scrollable.scrollTo('top', scrollTo);
9022             
9023             var box = this.target.el.getBox();
9024             Roo.log(box);
9025             var zIndex = Roo.bootstrap.Modal.zIndex++;
9026
9027             
9028             this.maskEl.top.setStyle('position', 'absolute');
9029             this.maskEl.top.setStyle('z-index', zIndex);
9030             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9031             this.maskEl.top.setLeft(0);
9032             this.maskEl.top.setTop(0);
9033             this.maskEl.top.show();
9034             
9035             this.maskEl.left.setStyle('position', 'absolute');
9036             this.maskEl.left.setStyle('z-index', zIndex);
9037             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9038             this.maskEl.left.setLeft(0);
9039             this.maskEl.left.setTop(box.y - this.padding);
9040             this.maskEl.left.show();
9041
9042             this.maskEl.bottom.setStyle('position', 'absolute');
9043             this.maskEl.bottom.setStyle('z-index', zIndex);
9044             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9045             this.maskEl.bottom.setLeft(0);
9046             this.maskEl.bottom.setTop(box.bottom + this.padding);
9047             this.maskEl.bottom.show();
9048
9049             this.maskEl.right.setStyle('position', 'absolute');
9050             this.maskEl.right.setStyle('z-index', zIndex);
9051             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9052             this.maskEl.right.setLeft(box.right + this.padding);
9053             this.maskEl.right.setTop(box.y - this.padding);
9054             this.maskEl.right.show();
9055
9056             this.toolTip.bindEl = this.target.el;
9057
9058             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9059
9060             var tip = this.target.blankText;
9061
9062             if(this.target.getValue() !== '' ) {
9063                 
9064                 if (this.target.invalidText.length) {
9065                     tip = this.target.invalidText;
9066                 } else if (this.target.regexText.length){
9067                     tip = this.target.regexText;
9068                 }
9069             }
9070
9071             this.toolTip.show(tip);
9072
9073             this.intervalID = window.setInterval(function() {
9074                 Roo.bootstrap.Form.popover.unmask();
9075             }, 10000);
9076
9077             window.onwheel = function(){ return false;};
9078             
9079             (function(){ this.isMasked = true; }).defer(500, this);
9080             
9081         },
9082         
9083         unmask : function()
9084         {
9085             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9086                 return;
9087             }
9088             
9089             this.maskEl.top.setStyle('position', 'absolute');
9090             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9091             this.maskEl.top.hide();
9092
9093             this.maskEl.left.setStyle('position', 'absolute');
9094             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9095             this.maskEl.left.hide();
9096
9097             this.maskEl.bottom.setStyle('position', 'absolute');
9098             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9099             this.maskEl.bottom.hide();
9100
9101             this.maskEl.right.setStyle('position', 'absolute');
9102             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9103             this.maskEl.right.hide();
9104             
9105             this.toolTip.hide();
9106             
9107             this.toolTip.el.hide();
9108             
9109             window.onwheel = function(){ return true;};
9110             
9111             if(this.intervalID){
9112                 window.clearInterval(this.intervalID);
9113                 this.intervalID = false;
9114             }
9115             
9116             this.isMasked = false;
9117             
9118         }
9119         
9120     }
9121     
9122 });
9123
9124 /*
9125  * Based on:
9126  * Ext JS Library 1.1.1
9127  * Copyright(c) 2006-2007, Ext JS, LLC.
9128  *
9129  * Originally Released Under LGPL - original licence link has changed is not relivant.
9130  *
9131  * Fork - LGPL
9132  * <script type="text/javascript">
9133  */
9134 /**
9135  * @class Roo.form.VTypes
9136  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9137  * @singleton
9138  */
9139 Roo.form.VTypes = function(){
9140     // closure these in so they are only created once.
9141     var alpha = /^[a-zA-Z_]+$/;
9142     var alphanum = /^[a-zA-Z0-9_]+$/;
9143     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9144     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9145
9146     // All these messages and functions are configurable
9147     return {
9148         /**
9149          * The function used to validate email addresses
9150          * @param {String} value The email address
9151          */
9152         'email' : function(v){
9153             return email.test(v);
9154         },
9155         /**
9156          * The error text to display when the email validation function returns false
9157          * @type String
9158          */
9159         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9160         /**
9161          * The keystroke filter mask to be applied on email input
9162          * @type RegExp
9163          */
9164         'emailMask' : /[a-z0-9_\.\-@]/i,
9165
9166         /**
9167          * The function used to validate URLs
9168          * @param {String} value The URL
9169          */
9170         'url' : function(v){
9171             return url.test(v);
9172         },
9173         /**
9174          * The error text to display when the url validation function returns false
9175          * @type String
9176          */
9177         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9178         
9179         /**
9180          * The function used to validate alpha values
9181          * @param {String} value The value
9182          */
9183         'alpha' : function(v){
9184             return alpha.test(v);
9185         },
9186         /**
9187          * The error text to display when the alpha validation function returns false
9188          * @type String
9189          */
9190         'alphaText' : 'This field should only contain letters and _',
9191         /**
9192          * The keystroke filter mask to be applied on alpha input
9193          * @type RegExp
9194          */
9195         'alphaMask' : /[a-z_]/i,
9196
9197         /**
9198          * The function used to validate alphanumeric values
9199          * @param {String} value The value
9200          */
9201         'alphanum' : function(v){
9202             return alphanum.test(v);
9203         },
9204         /**
9205          * The error text to display when the alphanumeric validation function returns false
9206          * @type String
9207          */
9208         'alphanumText' : 'This field should only contain letters, numbers and _',
9209         /**
9210          * The keystroke filter mask to be applied on alphanumeric input
9211          * @type RegExp
9212          */
9213         'alphanumMask' : /[a-z0-9_]/i
9214     };
9215 }();/*
9216  * - LGPL
9217  *
9218  * Input
9219  * 
9220  */
9221
9222 /**
9223  * @class Roo.bootstrap.Input
9224  * @extends Roo.bootstrap.Component
9225  * Bootstrap Input class
9226  * @cfg {Boolean} disabled is it disabled
9227  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9228  * @cfg {String} name name of the input
9229  * @cfg {string} fieldLabel - the label associated
9230  * @cfg {string} placeholder - placeholder to put in text.
9231  * @cfg {string}  before - input group add on before
9232  * @cfg {string} after - input group add on after
9233  * @cfg {string} size - (lg|sm) or leave empty..
9234  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9235  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9236  * @cfg {Number} md colspan out of 12 for computer-sized screens
9237  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9238  * @cfg {string} value default value of the input
9239  * @cfg {Number} labelWidth set the width of label 
9240  * @cfg {Number} labellg set the width of label (1-12)
9241  * @cfg {Number} labelmd set the width of label (1-12)
9242  * @cfg {Number} labelsm set the width of label (1-12)
9243  * @cfg {Number} labelxs set the width of label (1-12)
9244  * @cfg {String} labelAlign (top|left)
9245  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9246  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9247  * @cfg {String} indicatorpos (left|right) default left
9248  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9249  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9250
9251  * @cfg {String} align (left|center|right) Default left
9252  * @cfg {Boolean} forceFeedback (true|false) Default false
9253  * 
9254  * @constructor
9255  * Create a new Input
9256  * @param {Object} config The config object
9257  */
9258
9259 Roo.bootstrap.Input = function(config){
9260     
9261     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9262     
9263     this.addEvents({
9264         /**
9265          * @event focus
9266          * Fires when this field receives input focus.
9267          * @param {Roo.form.Field} this
9268          */
9269         focus : true,
9270         /**
9271          * @event blur
9272          * Fires when this field loses input focus.
9273          * @param {Roo.form.Field} this
9274          */
9275         blur : true,
9276         /**
9277          * @event specialkey
9278          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9279          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9280          * @param {Roo.form.Field} this
9281          * @param {Roo.EventObject} e The event object
9282          */
9283         specialkey : true,
9284         /**
9285          * @event change
9286          * Fires just before the field blurs if the field value has changed.
9287          * @param {Roo.form.Field} this
9288          * @param {Mixed} newValue The new value
9289          * @param {Mixed} oldValue The original value
9290          */
9291         change : true,
9292         /**
9293          * @event invalid
9294          * Fires after the field has been marked as invalid.
9295          * @param {Roo.form.Field} this
9296          * @param {String} msg The validation message
9297          */
9298         invalid : true,
9299         /**
9300          * @event valid
9301          * Fires after the field has been validated with no errors.
9302          * @param {Roo.form.Field} this
9303          */
9304         valid : true,
9305          /**
9306          * @event keyup
9307          * Fires after the key up
9308          * @param {Roo.form.Field} this
9309          * @param {Roo.EventObject}  e The event Object
9310          */
9311         keyup : true
9312     });
9313 };
9314
9315 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9316      /**
9317      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9318       automatic validation (defaults to "keyup").
9319      */
9320     validationEvent : "keyup",
9321      /**
9322      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9323      */
9324     validateOnBlur : true,
9325     /**
9326      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9327      */
9328     validationDelay : 250,
9329      /**
9330      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9331      */
9332     focusClass : "x-form-focus",  // not needed???
9333     
9334        
9335     /**
9336      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9337      */
9338     invalidClass : "has-warning",
9339     
9340     /**
9341      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9342      */
9343     validClass : "has-success",
9344     
9345     /**
9346      * @cfg {Boolean} hasFeedback (true|false) default true
9347      */
9348     hasFeedback : true,
9349     
9350     /**
9351      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9352      */
9353     invalidFeedbackClass : "glyphicon-warning-sign",
9354     
9355     /**
9356      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9357      */
9358     validFeedbackClass : "glyphicon-ok",
9359     
9360     /**
9361      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9362      */
9363     selectOnFocus : false,
9364     
9365      /**
9366      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9367      */
9368     maskRe : null,
9369        /**
9370      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9371      */
9372     vtype : null,
9373     
9374       /**
9375      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9376      */
9377     disableKeyFilter : false,
9378     
9379        /**
9380      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9381      */
9382     disabled : false,
9383      /**
9384      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9385      */
9386     allowBlank : true,
9387     /**
9388      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9389      */
9390     blankText : "Please complete this mandatory field",
9391     
9392      /**
9393      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9394      */
9395     minLength : 0,
9396     /**
9397      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9398      */
9399     maxLength : Number.MAX_VALUE,
9400     /**
9401      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9402      */
9403     minLengthText : "The minimum length for this field is {0}",
9404     /**
9405      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9406      */
9407     maxLengthText : "The maximum length for this field is {0}",
9408   
9409     
9410     /**
9411      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9412      * If available, this function will be called only after the basic validators all return true, and will be passed the
9413      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9414      */
9415     validator : null,
9416     /**
9417      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9418      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9419      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9420      */
9421     regex : null,
9422     /**
9423      * @cfg {String} regexText -- Depricated - use Invalid Text
9424      */
9425     regexText : "",
9426     
9427     /**
9428      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9429      */
9430     invalidText : "",
9431     
9432     
9433     
9434     autocomplete: false,
9435     
9436     
9437     fieldLabel : '',
9438     inputType : 'text',
9439     
9440     name : false,
9441     placeholder: false,
9442     before : false,
9443     after : false,
9444     size : false,
9445     hasFocus : false,
9446     preventMark: false,
9447     isFormField : true,
9448     value : '',
9449     labelWidth : 2,
9450     labelAlign : false,
9451     readOnly : false,
9452     align : false,
9453     formatedValue : false,
9454     forceFeedback : false,
9455     
9456     indicatorpos : 'left',
9457     
9458     labellg : 0,
9459     labelmd : 0,
9460     labelsm : 0,
9461     labelxs : 0,
9462     
9463     capture : '',
9464     accept : '',
9465     
9466     parentLabelAlign : function()
9467     {
9468         var parent = this;
9469         while (parent.parent()) {
9470             parent = parent.parent();
9471             if (typeof(parent.labelAlign) !='undefined') {
9472                 return parent.labelAlign;
9473             }
9474         }
9475         return 'left';
9476         
9477     },
9478     
9479     getAutoCreate : function()
9480     {
9481         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9482         
9483         var id = Roo.id();
9484         
9485         var cfg = {};
9486         
9487         if(this.inputType != 'hidden'){
9488             cfg.cls = 'form-group' //input-group
9489         }
9490         
9491         var input =  {
9492             tag: 'input',
9493             id : id,
9494             type : this.inputType,
9495             value : this.value,
9496             cls : 'form-control',
9497             placeholder : this.placeholder || '',
9498             autocomplete : this.autocomplete || 'new-password'
9499         };
9500         
9501         if(this.capture.length){
9502             input.capture = this.capture;
9503         }
9504         
9505         if(this.accept.length){
9506             input.accept = this.accept + "/*";
9507         }
9508         
9509         if(this.align){
9510             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9511         }
9512         
9513         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9514             input.maxLength = this.maxLength;
9515         }
9516         
9517         if (this.disabled) {
9518             input.disabled=true;
9519         }
9520         
9521         if (this.readOnly) {
9522             input.readonly=true;
9523         }
9524         
9525         if (this.name) {
9526             input.name = this.name;
9527         }
9528         
9529         if (this.size) {
9530             input.cls += ' input-' + this.size;
9531         }
9532         
9533         var settings=this;
9534         ['xs','sm','md','lg'].map(function(size){
9535             if (settings[size]) {
9536                 cfg.cls += ' col-' + size + '-' + settings[size];
9537             }
9538         });
9539         
9540         var inputblock = input;
9541         
9542         var feedback = {
9543             tag: 'span',
9544             cls: 'glyphicon form-control-feedback'
9545         };
9546             
9547         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9548             
9549             inputblock = {
9550                 cls : 'has-feedback',
9551                 cn :  [
9552                     input,
9553                     feedback
9554                 ] 
9555             };  
9556         }
9557         
9558         if (this.before || this.after) {
9559             
9560             inputblock = {
9561                 cls : 'input-group',
9562                 cn :  [] 
9563             };
9564             
9565             if (this.before && typeof(this.before) == 'string') {
9566                 
9567                 inputblock.cn.push({
9568                     tag :'span',
9569                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9570                     html : this.before
9571                 });
9572             }
9573             if (this.before && typeof(this.before) == 'object') {
9574                 this.before = Roo.factory(this.before);
9575                 
9576                 inputblock.cn.push({
9577                     tag :'span',
9578                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9579                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9580                 });
9581             }
9582             
9583             inputblock.cn.push(input);
9584             
9585             if (this.after && typeof(this.after) == 'string') {
9586                 inputblock.cn.push({
9587                     tag :'span',
9588                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9589                     html : this.after
9590                 });
9591             }
9592             if (this.after && typeof(this.after) == 'object') {
9593                 this.after = Roo.factory(this.after);
9594                 
9595                 inputblock.cn.push({
9596                     tag :'span',
9597                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9598                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9599                 });
9600             }
9601             
9602             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9603                 inputblock.cls += ' has-feedback';
9604                 inputblock.cn.push(feedback);
9605             }
9606         };
9607         var indicator = {
9608             tag : 'i',
9609             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9610             tooltip : 'This field is required'
9611         };
9612         if (Roo.bootstrap.version == 4) {
9613             indicator = {
9614                 tag : 'i',
9615                 style : 'display-none'
9616             };
9617         }
9618         if (align ==='left' && this.fieldLabel.length) {
9619             
9620             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9621             
9622             cfg.cn = [
9623                 indicator,
9624                 {
9625                     tag: 'label',
9626                     'for' :  id,
9627                     cls : 'control-label col-form-label',
9628                     html : this.fieldLabel
9629
9630                 },
9631                 {
9632                     cls : "", 
9633                     cn: [
9634                         inputblock
9635                     ]
9636                 }
9637             ];
9638             
9639             var labelCfg = cfg.cn[1];
9640             var contentCfg = cfg.cn[2];
9641             
9642             if(this.indicatorpos == 'right'){
9643                 cfg.cn = [
9644                     {
9645                         tag: 'label',
9646                         'for' :  id,
9647                         cls : 'control-label col-form-label',
9648                         cn : [
9649                             {
9650                                 tag : 'span',
9651                                 html : this.fieldLabel
9652                             },
9653                             indicator
9654                         ]
9655                     },
9656                     {
9657                         cls : "",
9658                         cn: [
9659                             inputblock
9660                         ]
9661                     }
9662
9663                 ];
9664                 
9665                 labelCfg = cfg.cn[0];
9666                 contentCfg = cfg.cn[1];
9667             
9668             }
9669             
9670             if(this.labelWidth > 12){
9671                 labelCfg.style = "width: " + this.labelWidth + 'px';
9672             }
9673             
9674             if(this.labelWidth < 13 && this.labelmd == 0){
9675                 this.labelmd = this.labelWidth;
9676             }
9677             
9678             if(this.labellg > 0){
9679                 labelCfg.cls += ' col-lg-' + this.labellg;
9680                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9681             }
9682             
9683             if(this.labelmd > 0){
9684                 labelCfg.cls += ' col-md-' + this.labelmd;
9685                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9686             }
9687             
9688             if(this.labelsm > 0){
9689                 labelCfg.cls += ' col-sm-' + this.labelsm;
9690                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9691             }
9692             
9693             if(this.labelxs > 0){
9694                 labelCfg.cls += ' col-xs-' + this.labelxs;
9695                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9696             }
9697             
9698             
9699         } else if ( this.fieldLabel.length) {
9700                 
9701             cfg.cn = [
9702                 {
9703                     tag : 'i',
9704                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9705                     tooltip : 'This field is required'
9706                 },
9707                 {
9708                     tag: 'label',
9709                    //cls : 'input-group-addon',
9710                     html : this.fieldLabel
9711
9712                 },
9713
9714                inputblock
9715
9716            ];
9717            
9718            if(this.indicatorpos == 'right'){
9719                 
9720                 cfg.cn = [
9721                     {
9722                         tag: 'label',
9723                        //cls : 'input-group-addon',
9724                         html : this.fieldLabel
9725
9726                     },
9727                     {
9728                         tag : 'i',
9729                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9730                         tooltip : 'This field is required'
9731                     },
9732
9733                    inputblock
9734
9735                ];
9736
9737             }
9738
9739         } else {
9740             
9741             cfg.cn = [
9742
9743                     inputblock
9744
9745             ];
9746                 
9747                 
9748         };
9749         
9750         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9751            cfg.cls += ' navbar-form';
9752         }
9753         
9754         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9755             // on BS4 we do this only if not form 
9756             cfg.cls += ' navbar-form';
9757             cfg.tag = 'li';
9758         }
9759         
9760         return cfg;
9761         
9762     },
9763     /**
9764      * return the real input element.
9765      */
9766     inputEl: function ()
9767     {
9768         return this.el.select('input.form-control',true).first();
9769     },
9770     
9771     tooltipEl : function()
9772     {
9773         return this.inputEl();
9774     },
9775     
9776     indicatorEl : function()
9777     {
9778         if (Roo.bootstrap.version == 4) {
9779             return false; // not enabled in v4 yet.
9780         }
9781         
9782         var indicator = this.el.select('i.roo-required-indicator',true).first();
9783         
9784         if(!indicator){
9785             return false;
9786         }
9787         
9788         return indicator;
9789         
9790     },
9791     
9792     setDisabled : function(v)
9793     {
9794         var i  = this.inputEl().dom;
9795         if (!v) {
9796             i.removeAttribute('disabled');
9797             return;
9798             
9799         }
9800         i.setAttribute('disabled','true');
9801     },
9802     initEvents : function()
9803     {
9804           
9805         this.inputEl().on("keydown" , this.fireKey,  this);
9806         this.inputEl().on("focus", this.onFocus,  this);
9807         this.inputEl().on("blur", this.onBlur,  this);
9808         
9809         this.inputEl().relayEvent('keyup', this);
9810         
9811         this.indicator = this.indicatorEl();
9812         
9813         if(this.indicator){
9814             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9815         }
9816  
9817         // reference to original value for reset
9818         this.originalValue = this.getValue();
9819         //Roo.form.TextField.superclass.initEvents.call(this);
9820         if(this.validationEvent == 'keyup'){
9821             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9822             this.inputEl().on('keyup', this.filterValidation, this);
9823         }
9824         else if(this.validationEvent !== false){
9825             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9826         }
9827         
9828         if(this.selectOnFocus){
9829             this.on("focus", this.preFocus, this);
9830             
9831         }
9832         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9833             this.inputEl().on("keypress", this.filterKeys, this);
9834         } else {
9835             this.inputEl().relayEvent('keypress', this);
9836         }
9837        /* if(this.grow){
9838             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9839             this.el.on("click", this.autoSize,  this);
9840         }
9841         */
9842         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9843             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9844         }
9845         
9846         if (typeof(this.before) == 'object') {
9847             this.before.render(this.el.select('.roo-input-before',true).first());
9848         }
9849         if (typeof(this.after) == 'object') {
9850             this.after.render(this.el.select('.roo-input-after',true).first());
9851         }
9852         
9853         this.inputEl().on('change', this.onChange, this);
9854         
9855     },
9856     filterValidation : function(e){
9857         if(!e.isNavKeyPress()){
9858             this.validationTask.delay(this.validationDelay);
9859         }
9860     },
9861      /**
9862      * Validates the field value
9863      * @return {Boolean} True if the value is valid, else false
9864      */
9865     validate : function(){
9866         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9867         if(this.disabled || this.validateValue(this.getRawValue())){
9868             this.markValid();
9869             return true;
9870         }
9871         
9872         this.markInvalid();
9873         return false;
9874     },
9875     
9876     
9877     /**
9878      * Validates a value according to the field's validation rules and marks the field as invalid
9879      * if the validation fails
9880      * @param {Mixed} value The value to validate
9881      * @return {Boolean} True if the value is valid, else false
9882      */
9883     validateValue : function(value)
9884     {
9885         if(this.getVisibilityEl().hasClass('hidden')){
9886             return true;
9887         }
9888         
9889         if(value.length < 1)  { // if it's blank
9890             if(this.allowBlank){
9891                 return true;
9892             }
9893             return false;
9894         }
9895         
9896         if(value.length < this.minLength){
9897             return false;
9898         }
9899         if(value.length > this.maxLength){
9900             return false;
9901         }
9902         if(this.vtype){
9903             var vt = Roo.form.VTypes;
9904             if(!vt[this.vtype](value, this)){
9905                 return false;
9906             }
9907         }
9908         if(typeof this.validator == "function"){
9909             var msg = this.validator(value);
9910             if(msg !== true){
9911                 return false;
9912             }
9913             if (typeof(msg) == 'string') {
9914                 this.invalidText = msg;
9915             }
9916         }
9917         
9918         if(this.regex && !this.regex.test(value)){
9919             return false;
9920         }
9921         
9922         return true;
9923     },
9924     
9925      // private
9926     fireKey : function(e){
9927         //Roo.log('field ' + e.getKey());
9928         if(e.isNavKeyPress()){
9929             this.fireEvent("specialkey", this, e);
9930         }
9931     },
9932     focus : function (selectText){
9933         if(this.rendered){
9934             this.inputEl().focus();
9935             if(selectText === true){
9936                 this.inputEl().dom.select();
9937             }
9938         }
9939         return this;
9940     } ,
9941     
9942     onFocus : function(){
9943         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9944            // this.el.addClass(this.focusClass);
9945         }
9946         if(!this.hasFocus){
9947             this.hasFocus = true;
9948             this.startValue = this.getValue();
9949             this.fireEvent("focus", this);
9950         }
9951     },
9952     
9953     beforeBlur : Roo.emptyFn,
9954
9955     
9956     // private
9957     onBlur : function(){
9958         this.beforeBlur();
9959         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9960             //this.el.removeClass(this.focusClass);
9961         }
9962         this.hasFocus = false;
9963         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9964             this.validate();
9965         }
9966         var v = this.getValue();
9967         if(String(v) !== String(this.startValue)){
9968             this.fireEvent('change', this, v, this.startValue);
9969         }
9970         this.fireEvent("blur", this);
9971     },
9972     
9973     onChange : function(e)
9974     {
9975         var v = this.getValue();
9976         if(String(v) !== String(this.startValue)){
9977             this.fireEvent('change', this, v, this.startValue);
9978         }
9979         
9980     },
9981     
9982     /**
9983      * Resets the current field value to the originally loaded value and clears any validation messages
9984      */
9985     reset : function(){
9986         this.setValue(this.originalValue);
9987         this.validate();
9988     },
9989      /**
9990      * Returns the name of the field
9991      * @return {Mixed} name The name field
9992      */
9993     getName: function(){
9994         return this.name;
9995     },
9996      /**
9997      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9998      * @return {Mixed} value The field value
9999      */
10000     getValue : function(){
10001         
10002         var v = this.inputEl().getValue();
10003         
10004         return v;
10005     },
10006     /**
10007      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10008      * @return {Mixed} value The field value
10009      */
10010     getRawValue : function(){
10011         var v = this.inputEl().getValue();
10012         
10013         return v;
10014     },
10015     
10016     /**
10017      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10018      * @param {Mixed} value The value to set
10019      */
10020     setRawValue : function(v){
10021         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10022     },
10023     
10024     selectText : function(start, end){
10025         var v = this.getRawValue();
10026         if(v.length > 0){
10027             start = start === undefined ? 0 : start;
10028             end = end === undefined ? v.length : end;
10029             var d = this.inputEl().dom;
10030             if(d.setSelectionRange){
10031                 d.setSelectionRange(start, end);
10032             }else if(d.createTextRange){
10033                 var range = d.createTextRange();
10034                 range.moveStart("character", start);
10035                 range.moveEnd("character", v.length-end);
10036                 range.select();
10037             }
10038         }
10039     },
10040     
10041     /**
10042      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10043      * @param {Mixed} value The value to set
10044      */
10045     setValue : function(v){
10046         this.value = v;
10047         if(this.rendered){
10048             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10049             this.validate();
10050         }
10051     },
10052     
10053     /*
10054     processValue : function(value){
10055         if(this.stripCharsRe){
10056             var newValue = value.replace(this.stripCharsRe, '');
10057             if(newValue !== value){
10058                 this.setRawValue(newValue);
10059                 return newValue;
10060             }
10061         }
10062         return value;
10063     },
10064   */
10065     preFocus : function(){
10066         
10067         if(this.selectOnFocus){
10068             this.inputEl().dom.select();
10069         }
10070     },
10071     filterKeys : function(e){
10072         var k = e.getKey();
10073         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10074             return;
10075         }
10076         var c = e.getCharCode(), cc = String.fromCharCode(c);
10077         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10078             return;
10079         }
10080         if(!this.maskRe.test(cc)){
10081             e.stopEvent();
10082         }
10083     },
10084      /**
10085      * Clear any invalid styles/messages for this field
10086      */
10087     clearInvalid : function(){
10088         
10089         if(!this.el || this.preventMark){ // not rendered
10090             return;
10091         }
10092         
10093         
10094         this.el.removeClass([this.invalidClass, 'is-invalid']);
10095         
10096         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10097             
10098             var feedback = this.el.select('.form-control-feedback', true).first();
10099             
10100             if(feedback){
10101                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10102             }
10103             
10104         }
10105         
10106         if(this.indicator){
10107             this.indicator.removeClass('visible');
10108             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10109         }
10110         
10111         this.fireEvent('valid', this);
10112     },
10113     
10114      /**
10115      * Mark this field as valid
10116      */
10117     markValid : function()
10118     {
10119         if(!this.el  || this.preventMark){ // not rendered...
10120             return;
10121         }
10122         
10123         this.el.removeClass([this.invalidClass, this.validClass]);
10124         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10125
10126         var feedback = this.el.select('.form-control-feedback', true).first();
10127             
10128         if(feedback){
10129             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10130         }
10131         
10132         if(this.indicator){
10133             this.indicator.removeClass('visible');
10134             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10135         }
10136         
10137         if(this.disabled){
10138             return;
10139         }
10140         
10141         if(this.allowBlank && !this.getRawValue().length){
10142             return;
10143         }
10144         if (Roo.bootstrap.version == 3) {
10145             this.el.addClass(this.validClass);
10146         } else {
10147             this.inputEl().addClass('is-valid');
10148         }
10149
10150         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10151             
10152             var feedback = this.el.select('.form-control-feedback', true).first();
10153             
10154             if(feedback){
10155                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10156                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10157             }
10158             
10159         }
10160         
10161         this.fireEvent('valid', this);
10162     },
10163     
10164      /**
10165      * Mark this field as invalid
10166      * @param {String} msg The validation message
10167      */
10168     markInvalid : function(msg)
10169     {
10170         if(!this.el  || this.preventMark){ // not rendered
10171             return;
10172         }
10173         
10174         this.el.removeClass([this.invalidClass, this.validClass]);
10175         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10176         
10177         var feedback = this.el.select('.form-control-feedback', true).first();
10178             
10179         if(feedback){
10180             this.el.select('.form-control-feedback', true).first().removeClass(
10181                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10182         }
10183
10184         if(this.disabled){
10185             return;
10186         }
10187         
10188         if(this.allowBlank && !this.getRawValue().length){
10189             return;
10190         }
10191         
10192         if(this.indicator){
10193             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10194             this.indicator.addClass('visible');
10195         }
10196         if (Roo.bootstrap.version == 3) {
10197             this.el.addClass(this.invalidClass);
10198         } else {
10199             this.inputEl().addClass('is-invalid');
10200         }
10201         
10202         
10203         
10204         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10205             
10206             var feedback = this.el.select('.form-control-feedback', true).first();
10207             
10208             if(feedback){
10209                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10210                 
10211                 if(this.getValue().length || this.forceFeedback){
10212                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10213                 }
10214                 
10215             }
10216             
10217         }
10218         
10219         this.fireEvent('invalid', this, msg);
10220     },
10221     // private
10222     SafariOnKeyDown : function(event)
10223     {
10224         // this is a workaround for a password hang bug on chrome/ webkit.
10225         if (this.inputEl().dom.type != 'password') {
10226             return;
10227         }
10228         
10229         var isSelectAll = false;
10230         
10231         if(this.inputEl().dom.selectionEnd > 0){
10232             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10233         }
10234         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10235             event.preventDefault();
10236             this.setValue('');
10237             return;
10238         }
10239         
10240         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10241             
10242             event.preventDefault();
10243             // this is very hacky as keydown always get's upper case.
10244             //
10245             var cc = String.fromCharCode(event.getCharCode());
10246             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10247             
10248         }
10249     },
10250     adjustWidth : function(tag, w){
10251         tag = tag.toLowerCase();
10252         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10253             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10254                 if(tag == 'input'){
10255                     return w + 2;
10256                 }
10257                 if(tag == 'textarea'){
10258                     return w-2;
10259                 }
10260             }else if(Roo.isOpera){
10261                 if(tag == 'input'){
10262                     return w + 2;
10263                 }
10264                 if(tag == 'textarea'){
10265                     return w-2;
10266                 }
10267             }
10268         }
10269         return w;
10270     },
10271     
10272     setFieldLabel : function(v)
10273     {
10274         if(!this.rendered){
10275             return;
10276         }
10277         
10278         if(this.indicatorEl()){
10279             var ar = this.el.select('label > span',true);
10280             
10281             if (ar.elements.length) {
10282                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10283                 this.fieldLabel = v;
10284                 return;
10285             }
10286             
10287             var br = this.el.select('label',true);
10288             
10289             if(br.elements.length) {
10290                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10291                 this.fieldLabel = v;
10292                 return;
10293             }
10294             
10295             Roo.log('Cannot Found any of label > span || label in input');
10296             return;
10297         }
10298         
10299         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10300         this.fieldLabel = v;
10301         
10302         
10303     }
10304 });
10305
10306  
10307 /*
10308  * - LGPL
10309  *
10310  * Input
10311  * 
10312  */
10313
10314 /**
10315  * @class Roo.bootstrap.TextArea
10316  * @extends Roo.bootstrap.Input
10317  * Bootstrap TextArea class
10318  * @cfg {Number} cols Specifies the visible width of a text area
10319  * @cfg {Number} rows Specifies the visible number of lines in a text area
10320  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10321  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10322  * @cfg {string} html text
10323  * 
10324  * @constructor
10325  * Create a new TextArea
10326  * @param {Object} config The config object
10327  */
10328
10329 Roo.bootstrap.TextArea = function(config){
10330     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10331    
10332 };
10333
10334 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10335      
10336     cols : false,
10337     rows : 5,
10338     readOnly : false,
10339     warp : 'soft',
10340     resize : false,
10341     value: false,
10342     html: false,
10343     
10344     getAutoCreate : function(){
10345         
10346         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10347         
10348         var id = Roo.id();
10349         
10350         var cfg = {};
10351         
10352         if(this.inputType != 'hidden'){
10353             cfg.cls = 'form-group' //input-group
10354         }
10355         
10356         var input =  {
10357             tag: 'textarea',
10358             id : id,
10359             warp : this.warp,
10360             rows : this.rows,
10361             value : this.value || '',
10362             html: this.html || '',
10363             cls : 'form-control',
10364             placeholder : this.placeholder || '' 
10365             
10366         };
10367         
10368         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10369             input.maxLength = this.maxLength;
10370         }
10371         
10372         if(this.resize){
10373             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10374         }
10375         
10376         if(this.cols){
10377             input.cols = this.cols;
10378         }
10379         
10380         if (this.readOnly) {
10381             input.readonly = true;
10382         }
10383         
10384         if (this.name) {
10385             input.name = this.name;
10386         }
10387         
10388         if (this.size) {
10389             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10390         }
10391         
10392         var settings=this;
10393         ['xs','sm','md','lg'].map(function(size){
10394             if (settings[size]) {
10395                 cfg.cls += ' col-' + size + '-' + settings[size];
10396             }
10397         });
10398         
10399         var inputblock = input;
10400         
10401         if(this.hasFeedback && !this.allowBlank){
10402             
10403             var feedback = {
10404                 tag: 'span',
10405                 cls: 'glyphicon form-control-feedback'
10406             };
10407
10408             inputblock = {
10409                 cls : 'has-feedback',
10410                 cn :  [
10411                     input,
10412                     feedback
10413                 ] 
10414             };  
10415         }
10416         
10417         
10418         if (this.before || this.after) {
10419             
10420             inputblock = {
10421                 cls : 'input-group',
10422                 cn :  [] 
10423             };
10424             if (this.before) {
10425                 inputblock.cn.push({
10426                     tag :'span',
10427                     cls : 'input-group-addon',
10428                     html : this.before
10429                 });
10430             }
10431             
10432             inputblock.cn.push(input);
10433             
10434             if(this.hasFeedback && !this.allowBlank){
10435                 inputblock.cls += ' has-feedback';
10436                 inputblock.cn.push(feedback);
10437             }
10438             
10439             if (this.after) {
10440                 inputblock.cn.push({
10441                     tag :'span',
10442                     cls : 'input-group-addon',
10443                     html : this.after
10444                 });
10445             }
10446             
10447         }
10448         
10449         if (align ==='left' && this.fieldLabel.length) {
10450             cfg.cn = [
10451                 {
10452                     tag: 'label',
10453                     'for' :  id,
10454                     cls : 'control-label',
10455                     html : this.fieldLabel
10456                 },
10457                 {
10458                     cls : "",
10459                     cn: [
10460                         inputblock
10461                     ]
10462                 }
10463
10464             ];
10465             
10466             if(this.labelWidth > 12){
10467                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10468             }
10469
10470             if(this.labelWidth < 13 && this.labelmd == 0){
10471                 this.labelmd = this.labelWidth;
10472             }
10473
10474             if(this.labellg > 0){
10475                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10476                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10477             }
10478
10479             if(this.labelmd > 0){
10480                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10481                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10482             }
10483
10484             if(this.labelsm > 0){
10485                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10486                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10487             }
10488
10489             if(this.labelxs > 0){
10490                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10491                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10492             }
10493             
10494         } else if ( this.fieldLabel.length) {
10495             cfg.cn = [
10496
10497                {
10498                    tag: 'label',
10499                    //cls : 'input-group-addon',
10500                    html : this.fieldLabel
10501
10502                },
10503
10504                inputblock
10505
10506            ];
10507
10508         } else {
10509
10510             cfg.cn = [
10511
10512                 inputblock
10513
10514             ];
10515                 
10516         }
10517         
10518         if (this.disabled) {
10519             input.disabled=true;
10520         }
10521         
10522         return cfg;
10523         
10524     },
10525     /**
10526      * return the real textarea element.
10527      */
10528     inputEl: function ()
10529     {
10530         return this.el.select('textarea.form-control',true).first();
10531     },
10532     
10533     /**
10534      * Clear any invalid styles/messages for this field
10535      */
10536     clearInvalid : function()
10537     {
10538         
10539         if(!this.el || this.preventMark){ // not rendered
10540             return;
10541         }
10542         
10543         var label = this.el.select('label', true).first();
10544         var icon = this.el.select('i.fa-star', true).first();
10545         
10546         if(label && icon){
10547             icon.remove();
10548         }
10549         this.el.removeClass( this.validClass);
10550         this.inputEl().removeClass('is-invalid');
10551          
10552         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10553             
10554             var feedback = this.el.select('.form-control-feedback', true).first();
10555             
10556             if(feedback){
10557                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10558             }
10559             
10560         }
10561         
10562         this.fireEvent('valid', this);
10563     },
10564     
10565      /**
10566      * Mark this field as valid
10567      */
10568     markValid : function()
10569     {
10570         if(!this.el  || this.preventMark){ // not rendered
10571             return;
10572         }
10573         
10574         this.el.removeClass([this.invalidClass, this.validClass]);
10575         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10576         
10577         var feedback = this.el.select('.form-control-feedback', true).first();
10578             
10579         if(feedback){
10580             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10581         }
10582
10583         if(this.disabled || this.allowBlank){
10584             return;
10585         }
10586         
10587         var label = this.el.select('label', true).first();
10588         var icon = this.el.select('i.fa-star', true).first();
10589         
10590         if(label && icon){
10591             icon.remove();
10592         }
10593         if (Roo.bootstrap.version == 3) {
10594             this.el.addClass(this.validClass);
10595         } else {
10596             this.inputEl().addClass('is-valid');
10597         }
10598         
10599         
10600         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10601             
10602             var feedback = this.el.select('.form-control-feedback', true).first();
10603             
10604             if(feedback){
10605                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10606                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10607             }
10608             
10609         }
10610         
10611         this.fireEvent('valid', this);
10612     },
10613     
10614      /**
10615      * Mark this field as invalid
10616      * @param {String} msg The validation message
10617      */
10618     markInvalid : function(msg)
10619     {
10620         if(!this.el  || this.preventMark){ // not rendered
10621             return;
10622         }
10623         
10624         this.el.removeClass([this.invalidClass, this.validClass]);
10625         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10626         
10627         var feedback = this.el.select('.form-control-feedback', true).first();
10628             
10629         if(feedback){
10630             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10631         }
10632
10633         if(this.disabled || this.allowBlank){
10634             return;
10635         }
10636         
10637         var label = this.el.select('label', true).first();
10638         var icon = this.el.select('i.fa-star', true).first();
10639         
10640         if(!this.getValue().length && label && !icon){
10641             this.el.createChild({
10642                 tag : 'i',
10643                 cls : 'text-danger fa fa-lg fa-star',
10644                 tooltip : 'This field is required',
10645                 style : 'margin-right:5px;'
10646             }, label, true);
10647         }
10648         
10649         if (Roo.bootstrap.version == 3) {
10650             this.el.addClass(this.invalidClass);
10651         } else {
10652             this.inputEl().addClass('is-invalid');
10653         }
10654         
10655         // fixme ... this may be depricated need to test..
10656         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10657             
10658             var feedback = this.el.select('.form-control-feedback', true).first();
10659             
10660             if(feedback){
10661                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10662                 
10663                 if(this.getValue().length || this.forceFeedback){
10664                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10665                 }
10666                 
10667             }
10668             
10669         }
10670         
10671         this.fireEvent('invalid', this, msg);
10672     }
10673 });
10674
10675  
10676 /*
10677  * - LGPL
10678  *
10679  * trigger field - base class for combo..
10680  * 
10681  */
10682  
10683 /**
10684  * @class Roo.bootstrap.TriggerField
10685  * @extends Roo.bootstrap.Input
10686  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10687  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10688  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10689  * for which you can provide a custom implementation.  For example:
10690  * <pre><code>
10691 var trigger = new Roo.bootstrap.TriggerField();
10692 trigger.onTriggerClick = myTriggerFn;
10693 trigger.applyTo('my-field');
10694 </code></pre>
10695  *
10696  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10697  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10698  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10699  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10700  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10701
10702  * @constructor
10703  * Create a new TriggerField.
10704  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10705  * to the base TextField)
10706  */
10707 Roo.bootstrap.TriggerField = function(config){
10708     this.mimicing = false;
10709     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10710 };
10711
10712 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10713     /**
10714      * @cfg {String} triggerClass A CSS class to apply to the trigger
10715      */
10716      /**
10717      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10718      */
10719     hideTrigger:false,
10720
10721     /**
10722      * @cfg {Boolean} removable (true|false) special filter default false
10723      */
10724     removable : false,
10725     
10726     /** @cfg {Boolean} grow @hide */
10727     /** @cfg {Number} growMin @hide */
10728     /** @cfg {Number} growMax @hide */
10729
10730     /**
10731      * @hide 
10732      * @method
10733      */
10734     autoSize: Roo.emptyFn,
10735     // private
10736     monitorTab : true,
10737     // private
10738     deferHeight : true,
10739
10740     
10741     actionMode : 'wrap',
10742     
10743     caret : false,
10744     
10745     
10746     getAutoCreate : function(){
10747        
10748         var align = this.labelAlign || this.parentLabelAlign();
10749         
10750         var id = Roo.id();
10751         
10752         var cfg = {
10753             cls: 'form-group' //input-group
10754         };
10755         
10756         
10757         var input =  {
10758             tag: 'input',
10759             id : id,
10760             type : this.inputType,
10761             cls : 'form-control',
10762             autocomplete: 'new-password',
10763             placeholder : this.placeholder || '' 
10764             
10765         };
10766         if (this.name) {
10767             input.name = this.name;
10768         }
10769         if (this.size) {
10770             input.cls += ' input-' + this.size;
10771         }
10772         
10773         if (this.disabled) {
10774             input.disabled=true;
10775         }
10776         
10777         var inputblock = input;
10778         
10779         if(this.hasFeedback && !this.allowBlank){
10780             
10781             var feedback = {
10782                 tag: 'span',
10783                 cls: 'glyphicon form-control-feedback'
10784             };
10785             
10786             if(this.removable && !this.editable && !this.tickable){
10787                 inputblock = {
10788                     cls : 'has-feedback',
10789                     cn :  [
10790                         inputblock,
10791                         {
10792                             tag: 'button',
10793                             html : 'x',
10794                             cls : 'roo-combo-removable-btn close'
10795                         },
10796                         feedback
10797                     ] 
10798                 };
10799             } else {
10800                 inputblock = {
10801                     cls : 'has-feedback',
10802                     cn :  [
10803                         inputblock,
10804                         feedback
10805                     ] 
10806                 };
10807             }
10808
10809         } else {
10810             if(this.removable && !this.editable && !this.tickable){
10811                 inputblock = {
10812                     cls : 'roo-removable',
10813                     cn :  [
10814                         inputblock,
10815                         {
10816                             tag: 'button',
10817                             html : 'x',
10818                             cls : 'roo-combo-removable-btn close'
10819                         }
10820                     ] 
10821                 };
10822             }
10823         }
10824         
10825         if (this.before || this.after) {
10826             
10827             inputblock = {
10828                 cls : 'input-group',
10829                 cn :  [] 
10830             };
10831             if (this.before) {
10832                 inputblock.cn.push({
10833                     tag :'span',
10834                     cls : 'input-group-addon input-group-prepend input-group-text',
10835                     html : this.before
10836                 });
10837             }
10838             
10839             inputblock.cn.push(input);
10840             
10841             if(this.hasFeedback && !this.allowBlank){
10842                 inputblock.cls += ' has-feedback';
10843                 inputblock.cn.push(feedback);
10844             }
10845             
10846             if (this.after) {
10847                 inputblock.cn.push({
10848                     tag :'span',
10849                     cls : 'input-group-addon input-group-append input-group-text',
10850                     html : this.after
10851                 });
10852             }
10853             
10854         };
10855         
10856       
10857         
10858         var ibwrap = inputblock;
10859         
10860         if(this.multiple){
10861             ibwrap = {
10862                 tag: 'ul',
10863                 cls: 'roo-select2-choices',
10864                 cn:[
10865                     {
10866                         tag: 'li',
10867                         cls: 'roo-select2-search-field',
10868                         cn: [
10869
10870                             inputblock
10871                         ]
10872                     }
10873                 ]
10874             };
10875                 
10876         }
10877         
10878         var combobox = {
10879             cls: 'roo-select2-container input-group',
10880             cn: [
10881                  {
10882                     tag: 'input',
10883                     type : 'hidden',
10884                     cls: 'form-hidden-field'
10885                 },
10886                 ibwrap
10887             ]
10888         };
10889         
10890         if(!this.multiple && this.showToggleBtn){
10891             
10892             var caret = {
10893                         tag: 'span',
10894                         cls: 'caret'
10895              };
10896             if (this.caret != false) {
10897                 caret = {
10898                      tag: 'i',
10899                      cls: 'fa fa-' + this.caret
10900                 };
10901                 
10902             }
10903             
10904             combobox.cn.push({
10905                 tag :'span',
10906                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10907                 cn : [
10908                     Roo.bootstrap.version == 3 ? caret : '',
10909                     {
10910                         tag: 'span',
10911                         cls: 'combobox-clear',
10912                         cn  : [
10913                             {
10914                                 tag : 'i',
10915                                 cls: 'icon-remove'
10916                             }
10917                         ]
10918                     }
10919                 ]
10920
10921             })
10922         }
10923         
10924         if(this.multiple){
10925             combobox.cls += ' roo-select2-container-multi';
10926         }
10927          var indicator = {
10928             tag : 'i',
10929             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10930             tooltip : 'This field is required'
10931         };
10932         if (Roo.bootstrap.version == 4) {
10933             indicator = {
10934                 tag : 'i',
10935                 style : 'display:none'
10936             };
10937         }
10938         
10939         
10940         if (align ==='left' && this.fieldLabel.length) {
10941             
10942             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10943
10944             cfg.cn = [
10945                 indicator,
10946                 {
10947                     tag: 'label',
10948                     'for' :  id,
10949                     cls : 'control-label',
10950                     html : this.fieldLabel
10951
10952                 },
10953                 {
10954                     cls : "", 
10955                     cn: [
10956                         combobox
10957                     ]
10958                 }
10959
10960             ];
10961             
10962             var labelCfg = cfg.cn[1];
10963             var contentCfg = cfg.cn[2];
10964             
10965             if(this.indicatorpos == 'right'){
10966                 cfg.cn = [
10967                     {
10968                         tag: 'label',
10969                         'for' :  id,
10970                         cls : 'control-label',
10971                         cn : [
10972                             {
10973                                 tag : 'span',
10974                                 html : this.fieldLabel
10975                             },
10976                             indicator
10977                         ]
10978                     },
10979                     {
10980                         cls : "", 
10981                         cn: [
10982                             combobox
10983                         ]
10984                     }
10985
10986                 ];
10987                 
10988                 labelCfg = cfg.cn[0];
10989                 contentCfg = cfg.cn[1];
10990             }
10991             
10992             if(this.labelWidth > 12){
10993                 labelCfg.style = "width: " + this.labelWidth + 'px';
10994             }
10995             
10996             if(this.labelWidth < 13 && this.labelmd == 0){
10997                 this.labelmd = this.labelWidth;
10998             }
10999             
11000             if(this.labellg > 0){
11001                 labelCfg.cls += ' col-lg-' + this.labellg;
11002                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11003             }
11004             
11005             if(this.labelmd > 0){
11006                 labelCfg.cls += ' col-md-' + this.labelmd;
11007                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11008             }
11009             
11010             if(this.labelsm > 0){
11011                 labelCfg.cls += ' col-sm-' + this.labelsm;
11012                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11013             }
11014             
11015             if(this.labelxs > 0){
11016                 labelCfg.cls += ' col-xs-' + this.labelxs;
11017                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11018             }
11019             
11020         } else if ( this.fieldLabel.length) {
11021 //                Roo.log(" label");
11022             cfg.cn = [
11023                 indicator,
11024                {
11025                    tag: 'label',
11026                    //cls : 'input-group-addon',
11027                    html : this.fieldLabel
11028
11029                },
11030
11031                combobox
11032
11033             ];
11034             
11035             if(this.indicatorpos == 'right'){
11036                 
11037                 cfg.cn = [
11038                     {
11039                        tag: 'label',
11040                        cn : [
11041                            {
11042                                tag : 'span',
11043                                html : this.fieldLabel
11044                            },
11045                            indicator
11046                        ]
11047
11048                     },
11049                     combobox
11050
11051                 ];
11052
11053             }
11054
11055         } else {
11056             
11057 //                Roo.log(" no label && no align");
11058                 cfg = combobox
11059                      
11060                 
11061         }
11062         
11063         var settings=this;
11064         ['xs','sm','md','lg'].map(function(size){
11065             if (settings[size]) {
11066                 cfg.cls += ' col-' + size + '-' + settings[size];
11067             }
11068         });
11069         
11070         return cfg;
11071         
11072     },
11073     
11074     
11075     
11076     // private
11077     onResize : function(w, h){
11078 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11079 //        if(typeof w == 'number'){
11080 //            var x = w - this.trigger.getWidth();
11081 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11082 //            this.trigger.setStyle('left', x+'px');
11083 //        }
11084     },
11085
11086     // private
11087     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11088
11089     // private
11090     getResizeEl : function(){
11091         return this.inputEl();
11092     },
11093
11094     // private
11095     getPositionEl : function(){
11096         return this.inputEl();
11097     },
11098
11099     // private
11100     alignErrorIcon : function(){
11101         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11102     },
11103
11104     // private
11105     initEvents : function(){
11106         
11107         this.createList();
11108         
11109         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11110         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11111         if(!this.multiple && this.showToggleBtn){
11112             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11113             if(this.hideTrigger){
11114                 this.trigger.setDisplayed(false);
11115             }
11116             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11117         }
11118         
11119         if(this.multiple){
11120             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11121         }
11122         
11123         if(this.removable && !this.editable && !this.tickable){
11124             var close = this.closeTriggerEl();
11125             
11126             if(close){
11127                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11128                 close.on('click', this.removeBtnClick, this, close);
11129             }
11130         }
11131         
11132         //this.trigger.addClassOnOver('x-form-trigger-over');
11133         //this.trigger.addClassOnClick('x-form-trigger-click');
11134         
11135         //if(!this.width){
11136         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11137         //}
11138     },
11139     
11140     closeTriggerEl : function()
11141     {
11142         var close = this.el.select('.roo-combo-removable-btn', true).first();
11143         return close ? close : false;
11144     },
11145     
11146     removeBtnClick : function(e, h, el)
11147     {
11148         e.preventDefault();
11149         
11150         if(this.fireEvent("remove", this) !== false){
11151             this.reset();
11152             this.fireEvent("afterremove", this)
11153         }
11154     },
11155     
11156     createList : function()
11157     {
11158         this.list = Roo.get(document.body).createChild({
11159             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11160             cls: 'typeahead typeahead-long dropdown-menu',
11161             style: 'display:none'
11162         });
11163         
11164         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11165         
11166     },
11167
11168     // private
11169     initTrigger : function(){
11170        
11171     },
11172
11173     // private
11174     onDestroy : function(){
11175         if(this.trigger){
11176             this.trigger.removeAllListeners();
11177           //  this.trigger.remove();
11178         }
11179         //if(this.wrap){
11180         //    this.wrap.remove();
11181         //}
11182         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11183     },
11184
11185     // private
11186     onFocus : function(){
11187         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11188         /*
11189         if(!this.mimicing){
11190             this.wrap.addClass('x-trigger-wrap-focus');
11191             this.mimicing = true;
11192             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11193             if(this.monitorTab){
11194                 this.el.on("keydown", this.checkTab, this);
11195             }
11196         }
11197         */
11198     },
11199
11200     // private
11201     checkTab : function(e){
11202         if(e.getKey() == e.TAB){
11203             this.triggerBlur();
11204         }
11205     },
11206
11207     // private
11208     onBlur : function(){
11209         // do nothing
11210     },
11211
11212     // private
11213     mimicBlur : function(e, t){
11214         /*
11215         if(!this.wrap.contains(t) && this.validateBlur()){
11216             this.triggerBlur();
11217         }
11218         */
11219     },
11220
11221     // private
11222     triggerBlur : function(){
11223         this.mimicing = false;
11224         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11225         if(this.monitorTab){
11226             this.el.un("keydown", this.checkTab, this);
11227         }
11228         //this.wrap.removeClass('x-trigger-wrap-focus');
11229         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11230     },
11231
11232     // private
11233     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11234     validateBlur : function(e, t){
11235         return true;
11236     },
11237
11238     // private
11239     onDisable : function(){
11240         this.inputEl().dom.disabled = true;
11241         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11242         //if(this.wrap){
11243         //    this.wrap.addClass('x-item-disabled');
11244         //}
11245     },
11246
11247     // private
11248     onEnable : function(){
11249         this.inputEl().dom.disabled = false;
11250         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11251         //if(this.wrap){
11252         //    this.el.removeClass('x-item-disabled');
11253         //}
11254     },
11255
11256     // private
11257     onShow : function(){
11258         var ae = this.getActionEl();
11259         
11260         if(ae){
11261             ae.dom.style.display = '';
11262             ae.dom.style.visibility = 'visible';
11263         }
11264     },
11265
11266     // private
11267     
11268     onHide : function(){
11269         var ae = this.getActionEl();
11270         ae.dom.style.display = 'none';
11271     },
11272
11273     /**
11274      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11275      * by an implementing function.
11276      * @method
11277      * @param {EventObject} e
11278      */
11279     onTriggerClick : Roo.emptyFn
11280 });
11281  /*
11282  * Based on:
11283  * Ext JS Library 1.1.1
11284  * Copyright(c) 2006-2007, Ext JS, LLC.
11285  *
11286  * Originally Released Under LGPL - original licence link has changed is not relivant.
11287  *
11288  * Fork - LGPL
11289  * <script type="text/javascript">
11290  */
11291
11292
11293 /**
11294  * @class Roo.data.SortTypes
11295  * @singleton
11296  * Defines the default sorting (casting?) comparison functions used when sorting data.
11297  */
11298 Roo.data.SortTypes = {
11299     /**
11300      * Default sort that does nothing
11301      * @param {Mixed} s The value being converted
11302      * @return {Mixed} The comparison value
11303      */
11304     none : function(s){
11305         return s;
11306     },
11307     
11308     /**
11309      * The regular expression used to strip tags
11310      * @type {RegExp}
11311      * @property
11312      */
11313     stripTagsRE : /<\/?[^>]+>/gi,
11314     
11315     /**
11316      * Strips all HTML tags to sort on text only
11317      * @param {Mixed} s The value being converted
11318      * @return {String} The comparison value
11319      */
11320     asText : function(s){
11321         return String(s).replace(this.stripTagsRE, "");
11322     },
11323     
11324     /**
11325      * Strips all HTML tags to sort on text only - Case insensitive
11326      * @param {Mixed} s The value being converted
11327      * @return {String} The comparison value
11328      */
11329     asUCText : function(s){
11330         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11331     },
11332     
11333     /**
11334      * Case insensitive string
11335      * @param {Mixed} s The value being converted
11336      * @return {String} The comparison value
11337      */
11338     asUCString : function(s) {
11339         return String(s).toUpperCase();
11340     },
11341     
11342     /**
11343      * Date sorting
11344      * @param {Mixed} s The value being converted
11345      * @return {Number} The comparison value
11346      */
11347     asDate : function(s) {
11348         if(!s){
11349             return 0;
11350         }
11351         if(s instanceof Date){
11352             return s.getTime();
11353         }
11354         return Date.parse(String(s));
11355     },
11356     
11357     /**
11358      * Float sorting
11359      * @param {Mixed} s The value being converted
11360      * @return {Float} The comparison value
11361      */
11362     asFloat : function(s) {
11363         var val = parseFloat(String(s).replace(/,/g, ""));
11364         if(isNaN(val)) {
11365             val = 0;
11366         }
11367         return val;
11368     },
11369     
11370     /**
11371      * Integer sorting
11372      * @param {Mixed} s The value being converted
11373      * @return {Number} The comparison value
11374      */
11375     asInt : function(s) {
11376         var val = parseInt(String(s).replace(/,/g, ""));
11377         if(isNaN(val)) {
11378             val = 0;
11379         }
11380         return val;
11381     }
11382 };/*
11383  * Based on:
11384  * Ext JS Library 1.1.1
11385  * Copyright(c) 2006-2007, Ext JS, LLC.
11386  *
11387  * Originally Released Under LGPL - original licence link has changed is not relivant.
11388  *
11389  * Fork - LGPL
11390  * <script type="text/javascript">
11391  */
11392
11393 /**
11394 * @class Roo.data.Record
11395  * Instances of this class encapsulate both record <em>definition</em> information, and record
11396  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11397  * to access Records cached in an {@link Roo.data.Store} object.<br>
11398  * <p>
11399  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11400  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11401  * objects.<br>
11402  * <p>
11403  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11404  * @constructor
11405  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11406  * {@link #create}. The parameters are the same.
11407  * @param {Array} data An associative Array of data values keyed by the field name.
11408  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11409  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11410  * not specified an integer id is generated.
11411  */
11412 Roo.data.Record = function(data, id){
11413     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11414     this.data = data;
11415 };
11416
11417 /**
11418  * Generate a constructor for a specific record layout.
11419  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11420  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11421  * Each field definition object may contain the following properties: <ul>
11422  * <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,
11423  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11424  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11425  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11426  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11427  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11428  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11429  * this may be omitted.</p></li>
11430  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11431  * <ul><li>auto (Default, implies no conversion)</li>
11432  * <li>string</li>
11433  * <li>int</li>
11434  * <li>float</li>
11435  * <li>boolean</li>
11436  * <li>date</li></ul></p></li>
11437  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11438  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11439  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11440  * by the Reader into an object that will be stored in the Record. It is passed the
11441  * following parameters:<ul>
11442  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11443  * </ul></p></li>
11444  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11445  * </ul>
11446  * <br>usage:<br><pre><code>
11447 var TopicRecord = Roo.data.Record.create(
11448     {name: 'title', mapping: 'topic_title'},
11449     {name: 'author', mapping: 'username'},
11450     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11451     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11452     {name: 'lastPoster', mapping: 'user2'},
11453     {name: 'excerpt', mapping: 'post_text'}
11454 );
11455
11456 var myNewRecord = new TopicRecord({
11457     title: 'Do my job please',
11458     author: 'noobie',
11459     totalPosts: 1,
11460     lastPost: new Date(),
11461     lastPoster: 'Animal',
11462     excerpt: 'No way dude!'
11463 });
11464 myStore.add(myNewRecord);
11465 </code></pre>
11466  * @method create
11467  * @static
11468  */
11469 Roo.data.Record.create = function(o){
11470     var f = function(){
11471         f.superclass.constructor.apply(this, arguments);
11472     };
11473     Roo.extend(f, Roo.data.Record);
11474     var p = f.prototype;
11475     p.fields = new Roo.util.MixedCollection(false, function(field){
11476         return field.name;
11477     });
11478     for(var i = 0, len = o.length; i < len; i++){
11479         p.fields.add(new Roo.data.Field(o[i]));
11480     }
11481     f.getField = function(name){
11482         return p.fields.get(name);  
11483     };
11484     return f;
11485 };
11486
11487 Roo.data.Record.AUTO_ID = 1000;
11488 Roo.data.Record.EDIT = 'edit';
11489 Roo.data.Record.REJECT = 'reject';
11490 Roo.data.Record.COMMIT = 'commit';
11491
11492 Roo.data.Record.prototype = {
11493     /**
11494      * Readonly flag - true if this record has been modified.
11495      * @type Boolean
11496      */
11497     dirty : false,
11498     editing : false,
11499     error: null,
11500     modified: null,
11501
11502     // private
11503     join : function(store){
11504         this.store = store;
11505     },
11506
11507     /**
11508      * Set the named field to the specified value.
11509      * @param {String} name The name of the field to set.
11510      * @param {Object} value The value to set the field to.
11511      */
11512     set : function(name, value){
11513         if(this.data[name] == value){
11514             return;
11515         }
11516         this.dirty = true;
11517         if(!this.modified){
11518             this.modified = {};
11519         }
11520         if(typeof this.modified[name] == 'undefined'){
11521             this.modified[name] = this.data[name];
11522         }
11523         this.data[name] = value;
11524         if(!this.editing && this.store){
11525             this.store.afterEdit(this);
11526         }       
11527     },
11528
11529     /**
11530      * Get the value of the named field.
11531      * @param {String} name The name of the field to get the value of.
11532      * @return {Object} The value of the field.
11533      */
11534     get : function(name){
11535         return this.data[name]; 
11536     },
11537
11538     // private
11539     beginEdit : function(){
11540         this.editing = true;
11541         this.modified = {}; 
11542     },
11543
11544     // private
11545     cancelEdit : function(){
11546         this.editing = false;
11547         delete this.modified;
11548     },
11549
11550     // private
11551     endEdit : function(){
11552         this.editing = false;
11553         if(this.dirty && this.store){
11554             this.store.afterEdit(this);
11555         }
11556     },
11557
11558     /**
11559      * Usually called by the {@link Roo.data.Store} which owns the Record.
11560      * Rejects all changes made to the Record since either creation, or the last commit operation.
11561      * Modified fields are reverted to their original values.
11562      * <p>
11563      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11564      * of reject operations.
11565      */
11566     reject : function(){
11567         var m = this.modified;
11568         for(var n in m){
11569             if(typeof m[n] != "function"){
11570                 this.data[n] = m[n];
11571             }
11572         }
11573         this.dirty = false;
11574         delete this.modified;
11575         this.editing = false;
11576         if(this.store){
11577             this.store.afterReject(this);
11578         }
11579     },
11580
11581     /**
11582      * Usually called by the {@link Roo.data.Store} which owns the Record.
11583      * Commits all changes made to the Record since either creation, or the last commit operation.
11584      * <p>
11585      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11586      * of commit operations.
11587      */
11588     commit : function(){
11589         this.dirty = false;
11590         delete this.modified;
11591         this.editing = false;
11592         if(this.store){
11593             this.store.afterCommit(this);
11594         }
11595     },
11596
11597     // private
11598     hasError : function(){
11599         return this.error != null;
11600     },
11601
11602     // private
11603     clearError : function(){
11604         this.error = null;
11605     },
11606
11607     /**
11608      * Creates a copy of this record.
11609      * @param {String} id (optional) A new record id if you don't want to use this record's id
11610      * @return {Record}
11611      */
11612     copy : function(newId) {
11613         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11614     }
11615 };/*
11616  * Based on:
11617  * Ext JS Library 1.1.1
11618  * Copyright(c) 2006-2007, Ext JS, LLC.
11619  *
11620  * Originally Released Under LGPL - original licence link has changed is not relivant.
11621  *
11622  * Fork - LGPL
11623  * <script type="text/javascript">
11624  */
11625
11626
11627
11628 /**
11629  * @class Roo.data.Store
11630  * @extends Roo.util.Observable
11631  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11632  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11633  * <p>
11634  * 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
11635  * has no knowledge of the format of the data returned by the Proxy.<br>
11636  * <p>
11637  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11638  * instances from the data object. These records are cached and made available through accessor functions.
11639  * @constructor
11640  * Creates a new Store.
11641  * @param {Object} config A config object containing the objects needed for the Store to access data,
11642  * and read the data into Records.
11643  */
11644 Roo.data.Store = function(config){
11645     this.data = new Roo.util.MixedCollection(false);
11646     this.data.getKey = function(o){
11647         return o.id;
11648     };
11649     this.baseParams = {};
11650     // private
11651     this.paramNames = {
11652         "start" : "start",
11653         "limit" : "limit",
11654         "sort" : "sort",
11655         "dir" : "dir",
11656         "multisort" : "_multisort"
11657     };
11658
11659     if(config && config.data){
11660         this.inlineData = config.data;
11661         delete config.data;
11662     }
11663
11664     Roo.apply(this, config);
11665     
11666     if(this.reader){ // reader passed
11667         this.reader = Roo.factory(this.reader, Roo.data);
11668         this.reader.xmodule = this.xmodule || false;
11669         if(!this.recordType){
11670             this.recordType = this.reader.recordType;
11671         }
11672         if(this.reader.onMetaChange){
11673             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11674         }
11675     }
11676
11677     if(this.recordType){
11678         this.fields = this.recordType.prototype.fields;
11679     }
11680     this.modified = [];
11681
11682     this.addEvents({
11683         /**
11684          * @event datachanged
11685          * Fires when the data cache has changed, and a widget which is using this Store
11686          * as a Record cache should refresh its view.
11687          * @param {Store} this
11688          */
11689         datachanged : true,
11690         /**
11691          * @event metachange
11692          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11693          * @param {Store} this
11694          * @param {Object} meta The JSON metadata
11695          */
11696         metachange : true,
11697         /**
11698          * @event add
11699          * Fires when Records have been added to the Store
11700          * @param {Store} this
11701          * @param {Roo.data.Record[]} records The array of Records added
11702          * @param {Number} index The index at which the record(s) were added
11703          */
11704         add : true,
11705         /**
11706          * @event remove
11707          * Fires when a Record has been removed from the Store
11708          * @param {Store} this
11709          * @param {Roo.data.Record} record The Record that was removed
11710          * @param {Number} index The index at which the record was removed
11711          */
11712         remove : true,
11713         /**
11714          * @event update
11715          * Fires when a Record has been updated
11716          * @param {Store} this
11717          * @param {Roo.data.Record} record The Record that was updated
11718          * @param {String} operation The update operation being performed.  Value may be one of:
11719          * <pre><code>
11720  Roo.data.Record.EDIT
11721  Roo.data.Record.REJECT
11722  Roo.data.Record.COMMIT
11723          * </code></pre>
11724          */
11725         update : true,
11726         /**
11727          * @event clear
11728          * Fires when the data cache has been cleared.
11729          * @param {Store} this
11730          */
11731         clear : true,
11732         /**
11733          * @event beforeload
11734          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11735          * the load action will be canceled.
11736          * @param {Store} this
11737          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11738          */
11739         beforeload : true,
11740         /**
11741          * @event beforeloadadd
11742          * Fires after a new set of Records has been loaded.
11743          * @param {Store} this
11744          * @param {Roo.data.Record[]} records The Records that were loaded
11745          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11746          */
11747         beforeloadadd : true,
11748         /**
11749          * @event load
11750          * Fires after a new set of Records has been loaded, before they are added to the store.
11751          * @param {Store} this
11752          * @param {Roo.data.Record[]} records The Records that were loaded
11753          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11754          * @params {Object} return from reader
11755          */
11756         load : true,
11757         /**
11758          * @event loadexception
11759          * Fires if an exception occurs in the Proxy during loading.
11760          * Called with the signature of the Proxy's "loadexception" event.
11761          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11762          * 
11763          * @param {Proxy} 
11764          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11765          * @param {Object} load options 
11766          * @param {Object} jsonData from your request (normally this contains the Exception)
11767          */
11768         loadexception : true
11769     });
11770     
11771     if(this.proxy){
11772         this.proxy = Roo.factory(this.proxy, Roo.data);
11773         this.proxy.xmodule = this.xmodule || false;
11774         this.relayEvents(this.proxy,  ["loadexception"]);
11775     }
11776     this.sortToggle = {};
11777     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11778
11779     Roo.data.Store.superclass.constructor.call(this);
11780
11781     if(this.inlineData){
11782         this.loadData(this.inlineData);
11783         delete this.inlineData;
11784     }
11785 };
11786
11787 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11788      /**
11789     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11790     * without a remote query - used by combo/forms at present.
11791     */
11792     
11793     /**
11794     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11795     */
11796     /**
11797     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11798     */
11799     /**
11800     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11801     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11802     */
11803     /**
11804     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11805     * on any HTTP request
11806     */
11807     /**
11808     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11809     */
11810     /**
11811     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11812     */
11813     multiSort: false,
11814     /**
11815     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11816     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11817     */
11818     remoteSort : false,
11819
11820     /**
11821     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11822      * loaded or when a record is removed. (defaults to false).
11823     */
11824     pruneModifiedRecords : false,
11825
11826     // private
11827     lastOptions : null,
11828
11829     /**
11830      * Add Records to the Store and fires the add event.
11831      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11832      */
11833     add : function(records){
11834         records = [].concat(records);
11835         for(var i = 0, len = records.length; i < len; i++){
11836             records[i].join(this);
11837         }
11838         var index = this.data.length;
11839         this.data.addAll(records);
11840         this.fireEvent("add", this, records, index);
11841     },
11842
11843     /**
11844      * Remove a Record from the Store and fires the remove event.
11845      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11846      */
11847     remove : function(record){
11848         var index = this.data.indexOf(record);
11849         this.data.removeAt(index);
11850  
11851         if(this.pruneModifiedRecords){
11852             this.modified.remove(record);
11853         }
11854         this.fireEvent("remove", this, record, index);
11855     },
11856
11857     /**
11858      * Remove all Records from the Store and fires the clear event.
11859      */
11860     removeAll : function(){
11861         this.data.clear();
11862         if(this.pruneModifiedRecords){
11863             this.modified = [];
11864         }
11865         this.fireEvent("clear", this);
11866     },
11867
11868     /**
11869      * Inserts Records to the Store at the given index and fires the add event.
11870      * @param {Number} index The start index at which to insert the passed Records.
11871      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11872      */
11873     insert : function(index, records){
11874         records = [].concat(records);
11875         for(var i = 0, len = records.length; i < len; i++){
11876             this.data.insert(index, records[i]);
11877             records[i].join(this);
11878         }
11879         this.fireEvent("add", this, records, index);
11880     },
11881
11882     /**
11883      * Get the index within the cache of the passed Record.
11884      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11885      * @return {Number} The index of the passed Record. Returns -1 if not found.
11886      */
11887     indexOf : function(record){
11888         return this.data.indexOf(record);
11889     },
11890
11891     /**
11892      * Get the index within the cache of the Record with the passed id.
11893      * @param {String} id The id of the Record to find.
11894      * @return {Number} The index of the Record. Returns -1 if not found.
11895      */
11896     indexOfId : function(id){
11897         return this.data.indexOfKey(id);
11898     },
11899
11900     /**
11901      * Get the Record with the specified id.
11902      * @param {String} id The id of the Record to find.
11903      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11904      */
11905     getById : function(id){
11906         return this.data.key(id);
11907     },
11908
11909     /**
11910      * Get the Record at the specified index.
11911      * @param {Number} index The index of the Record to find.
11912      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11913      */
11914     getAt : function(index){
11915         return this.data.itemAt(index);
11916     },
11917
11918     /**
11919      * Returns a range of Records between specified indices.
11920      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11921      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11922      * @return {Roo.data.Record[]} An array of Records
11923      */
11924     getRange : function(start, end){
11925         return this.data.getRange(start, end);
11926     },
11927
11928     // private
11929     storeOptions : function(o){
11930         o = Roo.apply({}, o);
11931         delete o.callback;
11932         delete o.scope;
11933         this.lastOptions = o;
11934     },
11935
11936     /**
11937      * Loads the Record cache from the configured Proxy using the configured Reader.
11938      * <p>
11939      * If using remote paging, then the first load call must specify the <em>start</em>
11940      * and <em>limit</em> properties in the options.params property to establish the initial
11941      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11942      * <p>
11943      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11944      * and this call will return before the new data has been loaded. Perform any post-processing
11945      * in a callback function, or in a "load" event handler.</strong>
11946      * <p>
11947      * @param {Object} options An object containing properties which control loading options:<ul>
11948      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11949      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11950      * passed the following arguments:<ul>
11951      * <li>r : Roo.data.Record[]</li>
11952      * <li>options: Options object from the load call</li>
11953      * <li>success: Boolean success indicator</li></ul></li>
11954      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11955      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11956      * </ul>
11957      */
11958     load : function(options){
11959         options = options || {};
11960         if(this.fireEvent("beforeload", this, options) !== false){
11961             this.storeOptions(options);
11962             var p = Roo.apply(options.params || {}, this.baseParams);
11963             // if meta was not loaded from remote source.. try requesting it.
11964             if (!this.reader.metaFromRemote) {
11965                 p._requestMeta = 1;
11966             }
11967             if(this.sortInfo && this.remoteSort){
11968                 var pn = this.paramNames;
11969                 p[pn["sort"]] = this.sortInfo.field;
11970                 p[pn["dir"]] = this.sortInfo.direction;
11971             }
11972             if (this.multiSort) {
11973                 var pn = this.paramNames;
11974                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11975             }
11976             
11977             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11978         }
11979     },
11980
11981     /**
11982      * Reloads the Record cache from the configured Proxy using the configured Reader and
11983      * the options from the last load operation performed.
11984      * @param {Object} options (optional) An object containing properties which may override the options
11985      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11986      * the most recently used options are reused).
11987      */
11988     reload : function(options){
11989         this.load(Roo.applyIf(options||{}, this.lastOptions));
11990     },
11991
11992     // private
11993     // Called as a callback by the Reader during a load operation.
11994     loadRecords : function(o, options, success){
11995         if(!o || success === false){
11996             if(success !== false){
11997                 this.fireEvent("load", this, [], options, o);
11998             }
11999             if(options.callback){
12000                 options.callback.call(options.scope || this, [], options, false);
12001             }
12002             return;
12003         }
12004         // if data returned failure - throw an exception.
12005         if (o.success === false) {
12006             // show a message if no listener is registered.
12007             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12008                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12009             }
12010             // loadmask wil be hooked into this..
12011             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12012             return;
12013         }
12014         var r = o.records, t = o.totalRecords || r.length;
12015         
12016         this.fireEvent("beforeloadadd", this, r, options, o);
12017         
12018         if(!options || options.add !== true){
12019             if(this.pruneModifiedRecords){
12020                 this.modified = [];
12021             }
12022             for(var i = 0, len = r.length; i < len; i++){
12023                 r[i].join(this);
12024             }
12025             if(this.snapshot){
12026                 this.data = this.snapshot;
12027                 delete this.snapshot;
12028             }
12029             this.data.clear();
12030             this.data.addAll(r);
12031             this.totalLength = t;
12032             this.applySort();
12033             this.fireEvent("datachanged", this);
12034         }else{
12035             this.totalLength = Math.max(t, this.data.length+r.length);
12036             this.add(r);
12037         }
12038         
12039         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12040                 
12041             var e = new Roo.data.Record({});
12042
12043             e.set(this.parent.displayField, this.parent.emptyTitle);
12044             e.set(this.parent.valueField, '');
12045
12046             this.insert(0, e);
12047         }
12048             
12049         this.fireEvent("load", this, r, options, o);
12050         if(options.callback){
12051             options.callback.call(options.scope || this, r, options, true);
12052         }
12053     },
12054
12055
12056     /**
12057      * Loads data from a passed data block. A Reader which understands the format of the data
12058      * must have been configured in the constructor.
12059      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12060      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12061      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12062      */
12063     loadData : function(o, append){
12064         var r = this.reader.readRecords(o);
12065         this.loadRecords(r, {add: append}, true);
12066     },
12067     
12068      /**
12069      * using 'cn' the nested child reader read the child array into it's child stores.
12070      * @param {Object} rec The record with a 'children array
12071      */
12072     loadDataFromChildren : function(rec)
12073     {
12074         this.loadData(this.reader.toLoadData(rec));
12075     },
12076     
12077
12078     /**
12079      * Gets the number of cached records.
12080      * <p>
12081      * <em>If using paging, this may not be the total size of the dataset. If the data object
12082      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12083      * the data set size</em>
12084      */
12085     getCount : function(){
12086         return this.data.length || 0;
12087     },
12088
12089     /**
12090      * Gets the total number of records in the dataset as returned by the server.
12091      * <p>
12092      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12093      * the dataset size</em>
12094      */
12095     getTotalCount : function(){
12096         return this.totalLength || 0;
12097     },
12098
12099     /**
12100      * Returns the sort state of the Store as an object with two properties:
12101      * <pre><code>
12102  field {String} The name of the field by which the Records are sorted
12103  direction {String} The sort order, "ASC" or "DESC"
12104      * </code></pre>
12105      */
12106     getSortState : function(){
12107         return this.sortInfo;
12108     },
12109
12110     // private
12111     applySort : function(){
12112         if(this.sortInfo && !this.remoteSort){
12113             var s = this.sortInfo, f = s.field;
12114             var st = this.fields.get(f).sortType;
12115             var fn = function(r1, r2){
12116                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12117                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12118             };
12119             this.data.sort(s.direction, fn);
12120             if(this.snapshot && this.snapshot != this.data){
12121                 this.snapshot.sort(s.direction, fn);
12122             }
12123         }
12124     },
12125
12126     /**
12127      * Sets the default sort column and order to be used by the next load operation.
12128      * @param {String} fieldName The name of the field to sort by.
12129      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12130      */
12131     setDefaultSort : function(field, dir){
12132         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12133     },
12134
12135     /**
12136      * Sort the Records.
12137      * If remote sorting is used, the sort is performed on the server, and the cache is
12138      * reloaded. If local sorting is used, the cache is sorted internally.
12139      * @param {String} fieldName The name of the field to sort by.
12140      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12141      */
12142     sort : function(fieldName, dir){
12143         var f = this.fields.get(fieldName);
12144         if(!dir){
12145             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12146             
12147             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12148                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12149             }else{
12150                 dir = f.sortDir;
12151             }
12152         }
12153         this.sortToggle[f.name] = dir;
12154         this.sortInfo = {field: f.name, direction: dir};
12155         if(!this.remoteSort){
12156             this.applySort();
12157             this.fireEvent("datachanged", this);
12158         }else{
12159             this.load(this.lastOptions);
12160         }
12161     },
12162
12163     /**
12164      * Calls the specified function for each of the Records in the cache.
12165      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12166      * Returning <em>false</em> aborts and exits the iteration.
12167      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12168      */
12169     each : function(fn, scope){
12170         this.data.each(fn, scope);
12171     },
12172
12173     /**
12174      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12175      * (e.g., during paging).
12176      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12177      */
12178     getModifiedRecords : function(){
12179         return this.modified;
12180     },
12181
12182     // private
12183     createFilterFn : function(property, value, anyMatch){
12184         if(!value.exec){ // not a regex
12185             value = String(value);
12186             if(value.length == 0){
12187                 return false;
12188             }
12189             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12190         }
12191         return function(r){
12192             return value.test(r.data[property]);
12193         };
12194     },
12195
12196     /**
12197      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12198      * @param {String} property A field on your records
12199      * @param {Number} start The record index to start at (defaults to 0)
12200      * @param {Number} end The last record index to include (defaults to length - 1)
12201      * @return {Number} The sum
12202      */
12203     sum : function(property, start, end){
12204         var rs = this.data.items, v = 0;
12205         start = start || 0;
12206         end = (end || end === 0) ? end : rs.length-1;
12207
12208         for(var i = start; i <= end; i++){
12209             v += (rs[i].data[property] || 0);
12210         }
12211         return v;
12212     },
12213
12214     /**
12215      * Filter the records by a specified property.
12216      * @param {String} field A field on your records
12217      * @param {String/RegExp} value Either a string that the field
12218      * should start with or a RegExp to test against the field
12219      * @param {Boolean} anyMatch True to match any part not just the beginning
12220      */
12221     filter : function(property, value, anyMatch){
12222         var fn = this.createFilterFn(property, value, anyMatch);
12223         return fn ? this.filterBy(fn) : this.clearFilter();
12224     },
12225
12226     /**
12227      * Filter by a function. The specified function will be called with each
12228      * record in this data source. If the function returns true the record is included,
12229      * otherwise it is filtered.
12230      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12231      * @param {Object} scope (optional) The scope of the function (defaults to this)
12232      */
12233     filterBy : function(fn, scope){
12234         this.snapshot = this.snapshot || this.data;
12235         this.data = this.queryBy(fn, scope||this);
12236         this.fireEvent("datachanged", this);
12237     },
12238
12239     /**
12240      * Query the records by a specified property.
12241      * @param {String} field A field on your records
12242      * @param {String/RegExp} value Either a string that the field
12243      * should start with or a RegExp to test against the field
12244      * @param {Boolean} anyMatch True to match any part not just the beginning
12245      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12246      */
12247     query : function(property, value, anyMatch){
12248         var fn = this.createFilterFn(property, value, anyMatch);
12249         return fn ? this.queryBy(fn) : this.data.clone();
12250     },
12251
12252     /**
12253      * Query by a function. The specified function will be called with each
12254      * record in this data source. If the function returns true the record is included
12255      * in the results.
12256      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12257      * @param {Object} scope (optional) The scope of the function (defaults to this)
12258       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12259      **/
12260     queryBy : function(fn, scope){
12261         var data = this.snapshot || this.data;
12262         return data.filterBy(fn, scope||this);
12263     },
12264
12265     /**
12266      * Collects unique values for a particular dataIndex from this store.
12267      * @param {String} dataIndex The property to collect
12268      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12269      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12270      * @return {Array} An array of the unique values
12271      **/
12272     collect : function(dataIndex, allowNull, bypassFilter){
12273         var d = (bypassFilter === true && this.snapshot) ?
12274                 this.snapshot.items : this.data.items;
12275         var v, sv, r = [], l = {};
12276         for(var i = 0, len = d.length; i < len; i++){
12277             v = d[i].data[dataIndex];
12278             sv = String(v);
12279             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12280                 l[sv] = true;
12281                 r[r.length] = v;
12282             }
12283         }
12284         return r;
12285     },
12286
12287     /**
12288      * Revert to a view of the Record cache with no filtering applied.
12289      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12290      */
12291     clearFilter : function(suppressEvent){
12292         if(this.snapshot && this.snapshot != this.data){
12293             this.data = this.snapshot;
12294             delete this.snapshot;
12295             if(suppressEvent !== true){
12296                 this.fireEvent("datachanged", this);
12297             }
12298         }
12299     },
12300
12301     // private
12302     afterEdit : function(record){
12303         if(this.modified.indexOf(record) == -1){
12304             this.modified.push(record);
12305         }
12306         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12307     },
12308     
12309     // private
12310     afterReject : function(record){
12311         this.modified.remove(record);
12312         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12313     },
12314
12315     // private
12316     afterCommit : function(record){
12317         this.modified.remove(record);
12318         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12319     },
12320
12321     /**
12322      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12323      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12324      */
12325     commitChanges : function(){
12326         var m = this.modified.slice(0);
12327         this.modified = [];
12328         for(var i = 0, len = m.length; i < len; i++){
12329             m[i].commit();
12330         }
12331     },
12332
12333     /**
12334      * Cancel outstanding changes on all changed records.
12335      */
12336     rejectChanges : function(){
12337         var m = this.modified.slice(0);
12338         this.modified = [];
12339         for(var i = 0, len = m.length; i < len; i++){
12340             m[i].reject();
12341         }
12342     },
12343
12344     onMetaChange : function(meta, rtype, o){
12345         this.recordType = rtype;
12346         this.fields = rtype.prototype.fields;
12347         delete this.snapshot;
12348         this.sortInfo = meta.sortInfo || this.sortInfo;
12349         this.modified = [];
12350         this.fireEvent('metachange', this, this.reader.meta);
12351     },
12352     
12353     moveIndex : function(data, type)
12354     {
12355         var index = this.indexOf(data);
12356         
12357         var newIndex = index + type;
12358         
12359         this.remove(data);
12360         
12361         this.insert(newIndex, data);
12362         
12363     }
12364 });/*
12365  * Based on:
12366  * Ext JS Library 1.1.1
12367  * Copyright(c) 2006-2007, Ext JS, LLC.
12368  *
12369  * Originally Released Under LGPL - original licence link has changed is not relivant.
12370  *
12371  * Fork - LGPL
12372  * <script type="text/javascript">
12373  */
12374
12375 /**
12376  * @class Roo.data.SimpleStore
12377  * @extends Roo.data.Store
12378  * Small helper class to make creating Stores from Array data easier.
12379  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12380  * @cfg {Array} fields An array of field definition objects, or field name strings.
12381  * @cfg {Object} an existing reader (eg. copied from another store)
12382  * @cfg {Array} data The multi-dimensional array of data
12383  * @constructor
12384  * @param {Object} config
12385  */
12386 Roo.data.SimpleStore = function(config)
12387 {
12388     Roo.data.SimpleStore.superclass.constructor.call(this, {
12389         isLocal : true,
12390         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12391                 id: config.id
12392             },
12393             Roo.data.Record.create(config.fields)
12394         ),
12395         proxy : new Roo.data.MemoryProxy(config.data)
12396     });
12397     this.load();
12398 };
12399 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12400  * Based on:
12401  * Ext JS Library 1.1.1
12402  * Copyright(c) 2006-2007, Ext JS, LLC.
12403  *
12404  * Originally Released Under LGPL - original licence link has changed is not relivant.
12405  *
12406  * Fork - LGPL
12407  * <script type="text/javascript">
12408  */
12409
12410 /**
12411 /**
12412  * @extends Roo.data.Store
12413  * @class Roo.data.JsonStore
12414  * Small helper class to make creating Stores for JSON data easier. <br/>
12415 <pre><code>
12416 var store = new Roo.data.JsonStore({
12417     url: 'get-images.php',
12418     root: 'images',
12419     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12420 });
12421 </code></pre>
12422  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12423  * JsonReader and HttpProxy (unless inline data is provided).</b>
12424  * @cfg {Array} fields An array of field definition objects, or field name strings.
12425  * @constructor
12426  * @param {Object} config
12427  */
12428 Roo.data.JsonStore = function(c){
12429     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12430         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12431         reader: new Roo.data.JsonReader(c, c.fields)
12432     }));
12433 };
12434 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12435  * Based on:
12436  * Ext JS Library 1.1.1
12437  * Copyright(c) 2006-2007, Ext JS, LLC.
12438  *
12439  * Originally Released Under LGPL - original licence link has changed is not relivant.
12440  *
12441  * Fork - LGPL
12442  * <script type="text/javascript">
12443  */
12444
12445  
12446 Roo.data.Field = function(config){
12447     if(typeof config == "string"){
12448         config = {name: config};
12449     }
12450     Roo.apply(this, config);
12451     
12452     if(!this.type){
12453         this.type = "auto";
12454     }
12455     
12456     var st = Roo.data.SortTypes;
12457     // named sortTypes are supported, here we look them up
12458     if(typeof this.sortType == "string"){
12459         this.sortType = st[this.sortType];
12460     }
12461     
12462     // set default sortType for strings and dates
12463     if(!this.sortType){
12464         switch(this.type){
12465             case "string":
12466                 this.sortType = st.asUCString;
12467                 break;
12468             case "date":
12469                 this.sortType = st.asDate;
12470                 break;
12471             default:
12472                 this.sortType = st.none;
12473         }
12474     }
12475
12476     // define once
12477     var stripRe = /[\$,%]/g;
12478
12479     // prebuilt conversion function for this field, instead of
12480     // switching every time we're reading a value
12481     if(!this.convert){
12482         var cv, dateFormat = this.dateFormat;
12483         switch(this.type){
12484             case "":
12485             case "auto":
12486             case undefined:
12487                 cv = function(v){ return v; };
12488                 break;
12489             case "string":
12490                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12491                 break;
12492             case "int":
12493                 cv = function(v){
12494                     return v !== undefined && v !== null && v !== '' ?
12495                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12496                     };
12497                 break;
12498             case "float":
12499                 cv = function(v){
12500                     return v !== undefined && v !== null && v !== '' ?
12501                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12502                     };
12503                 break;
12504             case "bool":
12505             case "boolean":
12506                 cv = function(v){ return v === true || v === "true" || v == 1; };
12507                 break;
12508             case "date":
12509                 cv = function(v){
12510                     if(!v){
12511                         return '';
12512                     }
12513                     if(v instanceof Date){
12514                         return v;
12515                     }
12516                     if(dateFormat){
12517                         if(dateFormat == "timestamp"){
12518                             return new Date(v*1000);
12519                         }
12520                         return Date.parseDate(v, dateFormat);
12521                     }
12522                     var parsed = Date.parse(v);
12523                     return parsed ? new Date(parsed) : null;
12524                 };
12525              break;
12526             
12527         }
12528         this.convert = cv;
12529     }
12530 };
12531
12532 Roo.data.Field.prototype = {
12533     dateFormat: null,
12534     defaultValue: "",
12535     mapping: null,
12536     sortType : null,
12537     sortDir : "ASC"
12538 };/*
12539  * Based on:
12540  * Ext JS Library 1.1.1
12541  * Copyright(c) 2006-2007, Ext JS, LLC.
12542  *
12543  * Originally Released Under LGPL - original licence link has changed is not relivant.
12544  *
12545  * Fork - LGPL
12546  * <script type="text/javascript">
12547  */
12548  
12549 // Base class for reading structured data from a data source.  This class is intended to be
12550 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12551
12552 /**
12553  * @class Roo.data.DataReader
12554  * Base class for reading structured data from a data source.  This class is intended to be
12555  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12556  */
12557
12558 Roo.data.DataReader = function(meta, recordType){
12559     
12560     this.meta = meta;
12561     
12562     this.recordType = recordType instanceof Array ? 
12563         Roo.data.Record.create(recordType) : recordType;
12564 };
12565
12566 Roo.data.DataReader.prototype = {
12567     
12568     
12569     readerType : 'Data',
12570      /**
12571      * Create an empty record
12572      * @param {Object} data (optional) - overlay some values
12573      * @return {Roo.data.Record} record created.
12574      */
12575     newRow :  function(d) {
12576         var da =  {};
12577         this.recordType.prototype.fields.each(function(c) {
12578             switch( c.type) {
12579                 case 'int' : da[c.name] = 0; break;
12580                 case 'date' : da[c.name] = new Date(); break;
12581                 case 'float' : da[c.name] = 0.0; break;
12582                 case 'boolean' : da[c.name] = false; break;
12583                 default : da[c.name] = ""; break;
12584             }
12585             
12586         });
12587         return new this.recordType(Roo.apply(da, d));
12588     }
12589     
12590     
12591 };/*
12592  * Based on:
12593  * Ext JS Library 1.1.1
12594  * Copyright(c) 2006-2007, Ext JS, LLC.
12595  *
12596  * Originally Released Under LGPL - original licence link has changed is not relivant.
12597  *
12598  * Fork - LGPL
12599  * <script type="text/javascript">
12600  */
12601
12602 /**
12603  * @class Roo.data.DataProxy
12604  * @extends Roo.data.Observable
12605  * This class is an abstract base class for implementations which provide retrieval of
12606  * unformatted data objects.<br>
12607  * <p>
12608  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12609  * (of the appropriate type which knows how to parse the data object) to provide a block of
12610  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12611  * <p>
12612  * Custom implementations must implement the load method as described in
12613  * {@link Roo.data.HttpProxy#load}.
12614  */
12615 Roo.data.DataProxy = function(){
12616     this.addEvents({
12617         /**
12618          * @event beforeload
12619          * Fires before a network request is made to retrieve a data object.
12620          * @param {Object} This DataProxy object.
12621          * @param {Object} params The params parameter to the load function.
12622          */
12623         beforeload : true,
12624         /**
12625          * @event load
12626          * Fires before the load method's callback is called.
12627          * @param {Object} This DataProxy object.
12628          * @param {Object} o The data object.
12629          * @param {Object} arg The callback argument object passed to the load function.
12630          */
12631         load : true,
12632         /**
12633          * @event loadexception
12634          * Fires if an Exception occurs during data retrieval.
12635          * @param {Object} This DataProxy object.
12636          * @param {Object} o The data object.
12637          * @param {Object} arg The callback argument object passed to the load function.
12638          * @param {Object} e The Exception.
12639          */
12640         loadexception : true
12641     });
12642     Roo.data.DataProxy.superclass.constructor.call(this);
12643 };
12644
12645 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12646
12647     /**
12648      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12649      */
12650 /*
12651  * Based on:
12652  * Ext JS Library 1.1.1
12653  * Copyright(c) 2006-2007, Ext JS, LLC.
12654  *
12655  * Originally Released Under LGPL - original licence link has changed is not relivant.
12656  *
12657  * Fork - LGPL
12658  * <script type="text/javascript">
12659  */
12660 /**
12661  * @class Roo.data.MemoryProxy
12662  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12663  * to the Reader when its load method is called.
12664  * @constructor
12665  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12666  */
12667 Roo.data.MemoryProxy = function(data){
12668     if (data.data) {
12669         data = data.data;
12670     }
12671     Roo.data.MemoryProxy.superclass.constructor.call(this);
12672     this.data = data;
12673 };
12674
12675 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12676     
12677     /**
12678      * Load data from the requested source (in this case an in-memory
12679      * data object passed to the constructor), read the data object into
12680      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12681      * process that block using the passed callback.
12682      * @param {Object} params This parameter is not used by the MemoryProxy class.
12683      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12684      * object into a block of Roo.data.Records.
12685      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12686      * The function must be passed <ul>
12687      * <li>The Record block object</li>
12688      * <li>The "arg" argument from the load function</li>
12689      * <li>A boolean success indicator</li>
12690      * </ul>
12691      * @param {Object} scope The scope in which to call the callback
12692      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12693      */
12694     load : function(params, reader, callback, scope, arg){
12695         params = params || {};
12696         var result;
12697         try {
12698             result = reader.readRecords(params.data ? params.data :this.data);
12699         }catch(e){
12700             this.fireEvent("loadexception", this, arg, null, e);
12701             callback.call(scope, null, arg, false);
12702             return;
12703         }
12704         callback.call(scope, result, arg, true);
12705     },
12706     
12707     // private
12708     update : function(params, records){
12709         
12710     }
12711 });/*
12712  * Based on:
12713  * Ext JS Library 1.1.1
12714  * Copyright(c) 2006-2007, Ext JS, LLC.
12715  *
12716  * Originally Released Under LGPL - original licence link has changed is not relivant.
12717  *
12718  * Fork - LGPL
12719  * <script type="text/javascript">
12720  */
12721 /**
12722  * @class Roo.data.HttpProxy
12723  * @extends Roo.data.DataProxy
12724  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12725  * configured to reference a certain URL.<br><br>
12726  * <p>
12727  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12728  * from which the running page was served.<br><br>
12729  * <p>
12730  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12731  * <p>
12732  * Be aware that to enable the browser to parse an XML document, the server must set
12733  * the Content-Type header in the HTTP response to "text/xml".
12734  * @constructor
12735  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12736  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12737  * will be used to make the request.
12738  */
12739 Roo.data.HttpProxy = function(conn){
12740     Roo.data.HttpProxy.superclass.constructor.call(this);
12741     // is conn a conn config or a real conn?
12742     this.conn = conn;
12743     this.useAjax = !conn || !conn.events;
12744   
12745 };
12746
12747 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12748     // thse are take from connection...
12749     
12750     /**
12751      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12752      */
12753     /**
12754      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12755      * extra parameters to each request made by this object. (defaults to undefined)
12756      */
12757     /**
12758      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12759      *  to each request made by this object. (defaults to undefined)
12760      */
12761     /**
12762      * @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)
12763      */
12764     /**
12765      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12766      */
12767      /**
12768      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12769      * @type Boolean
12770      */
12771   
12772
12773     /**
12774      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12775      * @type Boolean
12776      */
12777     /**
12778      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12779      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12780      * a finer-grained basis than the DataProxy events.
12781      */
12782     getConnection : function(){
12783         return this.useAjax ? Roo.Ajax : this.conn;
12784     },
12785
12786     /**
12787      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12788      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12789      * process that block using the passed callback.
12790      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12791      * for the request to the remote server.
12792      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12793      * object into a block of Roo.data.Records.
12794      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12795      * The function must be passed <ul>
12796      * <li>The Record block object</li>
12797      * <li>The "arg" argument from the load function</li>
12798      * <li>A boolean success indicator</li>
12799      * </ul>
12800      * @param {Object} scope The scope in which to call the callback
12801      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12802      */
12803     load : function(params, reader, callback, scope, arg){
12804         if(this.fireEvent("beforeload", this, params) !== false){
12805             var  o = {
12806                 params : params || {},
12807                 request: {
12808                     callback : callback,
12809                     scope : scope,
12810                     arg : arg
12811                 },
12812                 reader: reader,
12813                 callback : this.loadResponse,
12814                 scope: this
12815             };
12816             if(this.useAjax){
12817                 Roo.applyIf(o, this.conn);
12818                 if(this.activeRequest){
12819                     Roo.Ajax.abort(this.activeRequest);
12820                 }
12821                 this.activeRequest = Roo.Ajax.request(o);
12822             }else{
12823                 this.conn.request(o);
12824             }
12825         }else{
12826             callback.call(scope||this, null, arg, false);
12827         }
12828     },
12829
12830     // private
12831     loadResponse : function(o, success, response){
12832         delete this.activeRequest;
12833         if(!success){
12834             this.fireEvent("loadexception", this, o, response);
12835             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12836             return;
12837         }
12838         var result;
12839         try {
12840             result = o.reader.read(response);
12841         }catch(e){
12842             this.fireEvent("loadexception", this, o, response, e);
12843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12844             return;
12845         }
12846         
12847         this.fireEvent("load", this, o, o.request.arg);
12848         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12849     },
12850
12851     // private
12852     update : function(dataSet){
12853
12854     },
12855
12856     // private
12857     updateResponse : function(dataSet){
12858
12859     }
12860 });/*
12861  * Based on:
12862  * Ext JS Library 1.1.1
12863  * Copyright(c) 2006-2007, Ext JS, LLC.
12864  *
12865  * Originally Released Under LGPL - original licence link has changed is not relivant.
12866  *
12867  * Fork - LGPL
12868  * <script type="text/javascript">
12869  */
12870
12871 /**
12872  * @class Roo.data.ScriptTagProxy
12873  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12874  * other than the originating domain of the running page.<br><br>
12875  * <p>
12876  * <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
12877  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12878  * <p>
12879  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12880  * source code that is used as the source inside a &lt;script> tag.<br><br>
12881  * <p>
12882  * In order for the browser to process the returned data, the server must wrap the data object
12883  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12884  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12885  * depending on whether the callback name was passed:
12886  * <p>
12887  * <pre><code>
12888 boolean scriptTag = false;
12889 String cb = request.getParameter("callback");
12890 if (cb != null) {
12891     scriptTag = true;
12892     response.setContentType("text/javascript");
12893 } else {
12894     response.setContentType("application/x-json");
12895 }
12896 Writer out = response.getWriter();
12897 if (scriptTag) {
12898     out.write(cb + "(");
12899 }
12900 out.print(dataBlock.toJsonString());
12901 if (scriptTag) {
12902     out.write(");");
12903 }
12904 </pre></code>
12905  *
12906  * @constructor
12907  * @param {Object} config A configuration object.
12908  */
12909 Roo.data.ScriptTagProxy = function(config){
12910     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12911     Roo.apply(this, config);
12912     this.head = document.getElementsByTagName("head")[0];
12913 };
12914
12915 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12916
12917 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12918     /**
12919      * @cfg {String} url The URL from which to request the data object.
12920      */
12921     /**
12922      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12923      */
12924     timeout : 30000,
12925     /**
12926      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12927      * the server the name of the callback function set up by the load call to process the returned data object.
12928      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12929      * javascript output which calls this named function passing the data object as its only parameter.
12930      */
12931     callbackParam : "callback",
12932     /**
12933      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12934      * name to the request.
12935      */
12936     nocache : true,
12937
12938     /**
12939      * Load data from the configured URL, read the data object into
12940      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12941      * process that block using the passed callback.
12942      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12943      * for the request to the remote server.
12944      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12945      * object into a block of Roo.data.Records.
12946      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12947      * The function must be passed <ul>
12948      * <li>The Record block object</li>
12949      * <li>The "arg" argument from the load function</li>
12950      * <li>A boolean success indicator</li>
12951      * </ul>
12952      * @param {Object} scope The scope in which to call the callback
12953      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12954      */
12955     load : function(params, reader, callback, scope, arg){
12956         if(this.fireEvent("beforeload", this, params) !== false){
12957
12958             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12959
12960             var url = this.url;
12961             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12962             if(this.nocache){
12963                 url += "&_dc=" + (new Date().getTime());
12964             }
12965             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12966             var trans = {
12967                 id : transId,
12968                 cb : "stcCallback"+transId,
12969                 scriptId : "stcScript"+transId,
12970                 params : params,
12971                 arg : arg,
12972                 url : url,
12973                 callback : callback,
12974                 scope : scope,
12975                 reader : reader
12976             };
12977             var conn = this;
12978
12979             window[trans.cb] = function(o){
12980                 conn.handleResponse(o, trans);
12981             };
12982
12983             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12984
12985             if(this.autoAbort !== false){
12986                 this.abort();
12987             }
12988
12989             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12990
12991             var script = document.createElement("script");
12992             script.setAttribute("src", url);
12993             script.setAttribute("type", "text/javascript");
12994             script.setAttribute("id", trans.scriptId);
12995             this.head.appendChild(script);
12996
12997             this.trans = trans;
12998         }else{
12999             callback.call(scope||this, null, arg, false);
13000         }
13001     },
13002
13003     // private
13004     isLoading : function(){
13005         return this.trans ? true : false;
13006     },
13007
13008     /**
13009      * Abort the current server request.
13010      */
13011     abort : function(){
13012         if(this.isLoading()){
13013             this.destroyTrans(this.trans);
13014         }
13015     },
13016
13017     // private
13018     destroyTrans : function(trans, isLoaded){
13019         this.head.removeChild(document.getElementById(trans.scriptId));
13020         clearTimeout(trans.timeoutId);
13021         if(isLoaded){
13022             window[trans.cb] = undefined;
13023             try{
13024                 delete window[trans.cb];
13025             }catch(e){}
13026         }else{
13027             // if hasn't been loaded, wait for load to remove it to prevent script error
13028             window[trans.cb] = function(){
13029                 window[trans.cb] = undefined;
13030                 try{
13031                     delete window[trans.cb];
13032                 }catch(e){}
13033             };
13034         }
13035     },
13036
13037     // private
13038     handleResponse : function(o, trans){
13039         this.trans = false;
13040         this.destroyTrans(trans, true);
13041         var result;
13042         try {
13043             result = trans.reader.readRecords(o);
13044         }catch(e){
13045             this.fireEvent("loadexception", this, o, trans.arg, e);
13046             trans.callback.call(trans.scope||window, null, trans.arg, false);
13047             return;
13048         }
13049         this.fireEvent("load", this, o, trans.arg);
13050         trans.callback.call(trans.scope||window, result, trans.arg, true);
13051     },
13052
13053     // private
13054     handleFailure : function(trans){
13055         this.trans = false;
13056         this.destroyTrans(trans, false);
13057         this.fireEvent("loadexception", this, null, trans.arg);
13058         trans.callback.call(trans.scope||window, null, trans.arg, false);
13059     }
13060 });/*
13061  * Based on:
13062  * Ext JS Library 1.1.1
13063  * Copyright(c) 2006-2007, Ext JS, LLC.
13064  *
13065  * Originally Released Under LGPL - original licence link has changed is not relivant.
13066  *
13067  * Fork - LGPL
13068  * <script type="text/javascript">
13069  */
13070
13071 /**
13072  * @class Roo.data.JsonReader
13073  * @extends Roo.data.DataReader
13074  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13075  * based on mappings in a provided Roo.data.Record constructor.
13076  * 
13077  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13078  * in the reply previously. 
13079  * 
13080  * <p>
13081  * Example code:
13082  * <pre><code>
13083 var RecordDef = Roo.data.Record.create([
13084     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13085     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13086 ]);
13087 var myReader = new Roo.data.JsonReader({
13088     totalProperty: "results",    // The property which contains the total dataset size (optional)
13089     root: "rows",                // The property which contains an Array of row objects
13090     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13091 }, RecordDef);
13092 </code></pre>
13093  * <p>
13094  * This would consume a JSON file like this:
13095  * <pre><code>
13096 { 'results': 2, 'rows': [
13097     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13098     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13099 }
13100 </code></pre>
13101  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13102  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13103  * paged from the remote server.
13104  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13105  * @cfg {String} root name of the property which contains the Array of row objects.
13106  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13107  * @cfg {Array} fields Array of field definition objects
13108  * @constructor
13109  * Create a new JsonReader
13110  * @param {Object} meta Metadata configuration options
13111  * @param {Object} recordType Either an Array of field definition objects,
13112  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13113  */
13114 Roo.data.JsonReader = function(meta, recordType){
13115     
13116     meta = meta || {};
13117     // set some defaults:
13118     Roo.applyIf(meta, {
13119         totalProperty: 'total',
13120         successProperty : 'success',
13121         root : 'data',
13122         id : 'id'
13123     });
13124     
13125     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13126 };
13127 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13128     
13129     readerType : 'Json',
13130     
13131     /**
13132      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13133      * Used by Store query builder to append _requestMeta to params.
13134      * 
13135      */
13136     metaFromRemote : false,
13137     /**
13138      * This method is only used by a DataProxy which has retrieved data from a remote server.
13139      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13140      * @return {Object} data A data block which is used by an Roo.data.Store object as
13141      * a cache of Roo.data.Records.
13142      */
13143     read : function(response){
13144         var json = response.responseText;
13145        
13146         var o = /* eval:var:o */ eval("("+json+")");
13147         if(!o) {
13148             throw {message: "JsonReader.read: Json object not found"};
13149         }
13150         
13151         if(o.metaData){
13152             
13153             delete this.ef;
13154             this.metaFromRemote = true;
13155             this.meta = o.metaData;
13156             this.recordType = Roo.data.Record.create(o.metaData.fields);
13157             this.onMetaChange(this.meta, this.recordType, o);
13158         }
13159         return this.readRecords(o);
13160     },
13161
13162     // private function a store will implement
13163     onMetaChange : function(meta, recordType, o){
13164
13165     },
13166
13167     /**
13168          * @ignore
13169          */
13170     simpleAccess: function(obj, subsc) {
13171         return obj[subsc];
13172     },
13173
13174         /**
13175          * @ignore
13176          */
13177     getJsonAccessor: function(){
13178         var re = /[\[\.]/;
13179         return function(expr) {
13180             try {
13181                 return(re.test(expr))
13182                     ? new Function("obj", "return obj." + expr)
13183                     : function(obj){
13184                         return obj[expr];
13185                     };
13186             } catch(e){}
13187             return Roo.emptyFn;
13188         };
13189     }(),
13190
13191     /**
13192      * Create a data block containing Roo.data.Records from an XML document.
13193      * @param {Object} o An object which contains an Array of row objects in the property specified
13194      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13195      * which contains the total size of the dataset.
13196      * @return {Object} data A data block which is used by an Roo.data.Store object as
13197      * a cache of Roo.data.Records.
13198      */
13199     readRecords : function(o){
13200         /**
13201          * After any data loads, the raw JSON data is available for further custom processing.
13202          * @type Object
13203          */
13204         this.o = o;
13205         var s = this.meta, Record = this.recordType,
13206             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13207
13208 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13209         if (!this.ef) {
13210             if(s.totalProperty) {
13211                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13212                 }
13213                 if(s.successProperty) {
13214                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13215                 }
13216                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13217                 if (s.id) {
13218                         var g = this.getJsonAccessor(s.id);
13219                         this.getId = function(rec) {
13220                                 var r = g(rec);  
13221                                 return (r === undefined || r === "") ? null : r;
13222                         };
13223                 } else {
13224                         this.getId = function(){return null;};
13225                 }
13226             this.ef = [];
13227             for(var jj = 0; jj < fl; jj++){
13228                 f = fi[jj];
13229                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13230                 this.ef[jj] = this.getJsonAccessor(map);
13231             }
13232         }
13233
13234         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13235         if(s.totalProperty){
13236             var vt = parseInt(this.getTotal(o), 10);
13237             if(!isNaN(vt)){
13238                 totalRecords = vt;
13239             }
13240         }
13241         if(s.successProperty){
13242             var vs = this.getSuccess(o);
13243             if(vs === false || vs === 'false'){
13244                 success = false;
13245             }
13246         }
13247         var records = [];
13248         for(var i = 0; i < c; i++){
13249                 var n = root[i];
13250             var values = {};
13251             var id = this.getId(n);
13252             for(var j = 0; j < fl; j++){
13253                 f = fi[j];
13254             var v = this.ef[j](n);
13255             if (!f.convert) {
13256                 Roo.log('missing convert for ' + f.name);
13257                 Roo.log(f);
13258                 continue;
13259             }
13260             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13261             }
13262             var record = new Record(values, id);
13263             record.json = n;
13264             records[i] = record;
13265         }
13266         return {
13267             raw : o,
13268             success : success,
13269             records : records,
13270             totalRecords : totalRecords
13271         };
13272     },
13273     // used when loading children.. @see loadDataFromChildren
13274     toLoadData: function(rec)
13275     {
13276         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13277         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13278         return { data : data, total : data.length };
13279         
13280     }
13281 });/*
13282  * Based on:
13283  * Ext JS Library 1.1.1
13284  * Copyright(c) 2006-2007, Ext JS, LLC.
13285  *
13286  * Originally Released Under LGPL - original licence link has changed is not relivant.
13287  *
13288  * Fork - LGPL
13289  * <script type="text/javascript">
13290  */
13291
13292 /**
13293  * @class Roo.data.ArrayReader
13294  * @extends Roo.data.DataReader
13295  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13296  * Each element of that Array represents a row of data fields. The
13297  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13298  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13299  * <p>
13300  * Example code:.
13301  * <pre><code>
13302 var RecordDef = Roo.data.Record.create([
13303     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13304     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13305 ]);
13306 var myReader = new Roo.data.ArrayReader({
13307     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13308 }, RecordDef);
13309 </code></pre>
13310  * <p>
13311  * This would consume an Array like this:
13312  * <pre><code>
13313 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13314   </code></pre>
13315  
13316  * @constructor
13317  * Create a new JsonReader
13318  * @param {Object} meta Metadata configuration options.
13319  * @param {Object|Array} recordType Either an Array of field definition objects
13320  * 
13321  * @cfg {Array} fields Array of field definition objects
13322  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13323  * as specified to {@link Roo.data.Record#create},
13324  * or an {@link Roo.data.Record} object
13325  *
13326  * 
13327  * created using {@link Roo.data.Record#create}.
13328  */
13329 Roo.data.ArrayReader = function(meta, recordType)
13330 {    
13331     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13332 };
13333
13334 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13335     
13336       /**
13337      * Create a data block containing Roo.data.Records from an XML document.
13338      * @param {Object} o An Array of row objects which represents the dataset.
13339      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13340      * a cache of Roo.data.Records.
13341      */
13342     readRecords : function(o)
13343     {
13344         var sid = this.meta ? this.meta.id : null;
13345         var recordType = this.recordType, fields = recordType.prototype.fields;
13346         var records = [];
13347         var root = o;
13348         for(var i = 0; i < root.length; i++){
13349                 var n = root[i];
13350             var values = {};
13351             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13352             for(var j = 0, jlen = fields.length; j < jlen; j++){
13353                 var f = fields.items[j];
13354                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13355                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13356                 v = f.convert(v);
13357                 values[f.name] = v;
13358             }
13359             var record = new recordType(values, id);
13360             record.json = n;
13361             records[records.length] = record;
13362         }
13363         return {
13364             records : records,
13365             totalRecords : records.length
13366         };
13367     },
13368     // used when loading children.. @see loadDataFromChildren
13369     toLoadData: function(rec)
13370     {
13371         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13372         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13373         
13374     }
13375     
13376     
13377 });/*
13378  * - LGPL
13379  * * 
13380  */
13381
13382 /**
13383  * @class Roo.bootstrap.ComboBox
13384  * @extends Roo.bootstrap.TriggerField
13385  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13386  * @cfg {Boolean} append (true|false) default false
13387  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13388  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13389  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13390  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13391  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13392  * @cfg {Boolean} animate default true
13393  * @cfg {Boolean} emptyResultText only for touch device
13394  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13395  * @cfg {String} emptyTitle default ''
13396  * @constructor
13397  * Create a new ComboBox.
13398  * @param {Object} config Configuration options
13399  */
13400 Roo.bootstrap.ComboBox = function(config){
13401     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13402     this.addEvents({
13403         /**
13404          * @event expand
13405          * Fires when the dropdown list is expanded
13406         * @param {Roo.bootstrap.ComboBox} combo This combo box
13407         */
13408         'expand' : true,
13409         /**
13410          * @event collapse
13411          * Fires when the dropdown list is collapsed
13412         * @param {Roo.bootstrap.ComboBox} combo This combo box
13413         */
13414         'collapse' : true,
13415         /**
13416          * @event beforeselect
13417          * Fires before a list item is selected. Return false to cancel the selection.
13418         * @param {Roo.bootstrap.ComboBox} combo This combo box
13419         * @param {Roo.data.Record} record The data record returned from the underlying store
13420         * @param {Number} index The index of the selected item in the dropdown list
13421         */
13422         'beforeselect' : true,
13423         /**
13424          * @event select
13425          * Fires when a list item is selected
13426         * @param {Roo.bootstrap.ComboBox} combo This combo box
13427         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13428         * @param {Number} index The index of the selected item in the dropdown list
13429         */
13430         'select' : true,
13431         /**
13432          * @event beforequery
13433          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13434          * The event object passed has these properties:
13435         * @param {Roo.bootstrap.ComboBox} combo This combo box
13436         * @param {String} query The query
13437         * @param {Boolean} forceAll true to force "all" query
13438         * @param {Boolean} cancel true to cancel the query
13439         * @param {Object} e The query event object
13440         */
13441         'beforequery': true,
13442          /**
13443          * @event add
13444          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13445         * @param {Roo.bootstrap.ComboBox} combo This combo box
13446         */
13447         'add' : true,
13448         /**
13449          * @event edit
13450          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13451         * @param {Roo.bootstrap.ComboBox} combo This combo box
13452         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13453         */
13454         'edit' : true,
13455         /**
13456          * @event remove
13457          * Fires when the remove value from the combobox array
13458         * @param {Roo.bootstrap.ComboBox} combo This combo box
13459         */
13460         'remove' : true,
13461         /**
13462          * @event afterremove
13463          * Fires when the remove value from the combobox array
13464         * @param {Roo.bootstrap.ComboBox} combo This combo box
13465         */
13466         'afterremove' : true,
13467         /**
13468          * @event specialfilter
13469          * Fires when specialfilter
13470             * @param {Roo.bootstrap.ComboBox} combo This combo box
13471             */
13472         'specialfilter' : true,
13473         /**
13474          * @event tick
13475          * Fires when tick the element
13476             * @param {Roo.bootstrap.ComboBox} combo This combo box
13477             */
13478         'tick' : true,
13479         /**
13480          * @event touchviewdisplay
13481          * Fires when touch view require special display (default is using displayField)
13482             * @param {Roo.bootstrap.ComboBox} combo This combo box
13483             * @param {Object} cfg set html .
13484             */
13485         'touchviewdisplay' : true
13486         
13487     });
13488     
13489     this.item = [];
13490     this.tickItems = [];
13491     
13492     this.selectedIndex = -1;
13493     if(this.mode == 'local'){
13494         if(config.queryDelay === undefined){
13495             this.queryDelay = 10;
13496         }
13497         if(config.minChars === undefined){
13498             this.minChars = 0;
13499         }
13500     }
13501 };
13502
13503 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13504      
13505     /**
13506      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13507      * rendering into an Roo.Editor, defaults to false)
13508      */
13509     /**
13510      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13511      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13512      */
13513     /**
13514      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13515      */
13516     /**
13517      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13518      * the dropdown list (defaults to undefined, with no header element)
13519      */
13520
13521      /**
13522      * @cfg {String/Roo.Template} tpl The template to use to render the output
13523      */
13524      
13525      /**
13526      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13527      */
13528     listWidth: undefined,
13529     /**
13530      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13531      * mode = 'remote' or 'text' if mode = 'local')
13532      */
13533     displayField: undefined,
13534     
13535     /**
13536      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13537      * mode = 'remote' or 'value' if mode = 'local'). 
13538      * Note: use of a valueField requires the user make a selection
13539      * in order for a value to be mapped.
13540      */
13541     valueField: undefined,
13542     /**
13543      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13544      */
13545     modalTitle : '',
13546     
13547     /**
13548      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13549      * field's data value (defaults to the underlying DOM element's name)
13550      */
13551     hiddenName: undefined,
13552     /**
13553      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13554      */
13555     listClass: '',
13556     /**
13557      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13558      */
13559     selectedClass: 'active',
13560     
13561     /**
13562      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13563      */
13564     shadow:'sides',
13565     /**
13566      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13567      * anchor positions (defaults to 'tl-bl')
13568      */
13569     listAlign: 'tl-bl?',
13570     /**
13571      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13572      */
13573     maxHeight: 300,
13574     /**
13575      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13576      * query specified by the allQuery config option (defaults to 'query')
13577      */
13578     triggerAction: 'query',
13579     /**
13580      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13581      * (defaults to 4, does not apply if editable = false)
13582      */
13583     minChars : 4,
13584     /**
13585      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13586      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13587      */
13588     typeAhead: false,
13589     /**
13590      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13591      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13592      */
13593     queryDelay: 500,
13594     /**
13595      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13596      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13597      */
13598     pageSize: 0,
13599     /**
13600      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13601      * when editable = true (defaults to false)
13602      */
13603     selectOnFocus:false,
13604     /**
13605      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13606      */
13607     queryParam: 'query',
13608     /**
13609      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13610      * when mode = 'remote' (defaults to 'Loading...')
13611      */
13612     loadingText: 'Loading...',
13613     /**
13614      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13615      */
13616     resizable: false,
13617     /**
13618      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13619      */
13620     handleHeight : 8,
13621     /**
13622      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13623      * traditional select (defaults to true)
13624      */
13625     editable: true,
13626     /**
13627      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13628      */
13629     allQuery: '',
13630     /**
13631      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13632      */
13633     mode: 'remote',
13634     /**
13635      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13636      * listWidth has a higher value)
13637      */
13638     minListWidth : 70,
13639     /**
13640      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13641      * allow the user to set arbitrary text into the field (defaults to false)
13642      */
13643     forceSelection:false,
13644     /**
13645      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13646      * if typeAhead = true (defaults to 250)
13647      */
13648     typeAheadDelay : 250,
13649     /**
13650      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13651      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13652      */
13653     valueNotFoundText : undefined,
13654     /**
13655      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13656      */
13657     blockFocus : false,
13658     
13659     /**
13660      * @cfg {Boolean} disableClear Disable showing of clear button.
13661      */
13662     disableClear : false,
13663     /**
13664      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13665      */
13666     alwaysQuery : false,
13667     
13668     /**
13669      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13670      */
13671     multiple : false,
13672     
13673     /**
13674      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13675      */
13676     invalidClass : "has-warning",
13677     
13678     /**
13679      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13680      */
13681     validClass : "has-success",
13682     
13683     /**
13684      * @cfg {Boolean} specialFilter (true|false) special filter default false
13685      */
13686     specialFilter : false,
13687     
13688     /**
13689      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13690      */
13691     mobileTouchView : true,
13692     
13693     /**
13694      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13695      */
13696     useNativeIOS : false,
13697     
13698     /**
13699      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13700      */
13701     mobile_restrict_height : false,
13702     
13703     ios_options : false,
13704     
13705     //private
13706     addicon : false,
13707     editicon: false,
13708     
13709     page: 0,
13710     hasQuery: false,
13711     append: false,
13712     loadNext: false,
13713     autoFocus : true,
13714     tickable : false,
13715     btnPosition : 'right',
13716     triggerList : true,
13717     showToggleBtn : true,
13718     animate : true,
13719     emptyResultText: 'Empty',
13720     triggerText : 'Select',
13721     emptyTitle : '',
13722     
13723     // element that contains real text value.. (when hidden is used..)
13724     
13725     getAutoCreate : function()
13726     {   
13727         var cfg = false;
13728         //render
13729         /*
13730          * Render classic select for iso
13731          */
13732         
13733         if(Roo.isIOS && this.useNativeIOS){
13734             cfg = this.getAutoCreateNativeIOS();
13735             return cfg;
13736         }
13737         
13738         /*
13739          * Touch Devices
13740          */
13741         
13742         if(Roo.isTouch && this.mobileTouchView){
13743             cfg = this.getAutoCreateTouchView();
13744             return cfg;;
13745         }
13746         
13747         /*
13748          *  Normal ComboBox
13749          */
13750         if(!this.tickable){
13751             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13752             return cfg;
13753         }
13754         
13755         /*
13756          *  ComboBox with tickable selections
13757          */
13758              
13759         var align = this.labelAlign || this.parentLabelAlign();
13760         
13761         cfg = {
13762             cls : 'form-group roo-combobox-tickable' //input-group
13763         };
13764         
13765         var btn_text_select = '';
13766         var btn_text_done = '';
13767         var btn_text_cancel = '';
13768         
13769         if (this.btn_text_show) {
13770             btn_text_select = 'Select';
13771             btn_text_done = 'Done';
13772             btn_text_cancel = 'Cancel'; 
13773         }
13774         
13775         var buttons = {
13776             tag : 'div',
13777             cls : 'tickable-buttons',
13778             cn : [
13779                 {
13780                     tag : 'button',
13781                     type : 'button',
13782                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13783                     //html : this.triggerText
13784                     html: btn_text_select
13785                 },
13786                 {
13787                     tag : 'button',
13788                     type : 'button',
13789                     name : 'ok',
13790                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13791                     //html : 'Done'
13792                     html: btn_text_done
13793                 },
13794                 {
13795                     tag : 'button',
13796                     type : 'button',
13797                     name : 'cancel',
13798                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13799                     //html : 'Cancel'
13800                     html: btn_text_cancel
13801                 }
13802             ]
13803         };
13804         
13805         if(this.editable){
13806             buttons.cn.unshift({
13807                 tag: 'input',
13808                 cls: 'roo-select2-search-field-input'
13809             });
13810         }
13811         
13812         var _this = this;
13813         
13814         Roo.each(buttons.cn, function(c){
13815             if (_this.size) {
13816                 c.cls += ' btn-' + _this.size;
13817             }
13818
13819             if (_this.disabled) {
13820                 c.disabled = true;
13821             }
13822         });
13823         
13824         var box = {
13825             tag: 'div',
13826             style : 'display: contents',
13827             cn: [
13828                 {
13829                     tag: 'input',
13830                     type : 'hidden',
13831                     cls: 'form-hidden-field'
13832                 },
13833                 {
13834                     tag: 'ul',
13835                     cls: 'roo-select2-choices',
13836                     cn:[
13837                         {
13838                             tag: 'li',
13839                             cls: 'roo-select2-search-field',
13840                             cn: [
13841                                 buttons
13842                             ]
13843                         }
13844                     ]
13845                 }
13846             ]
13847         };
13848         
13849         var combobox = {
13850             cls: 'roo-select2-container input-group roo-select2-container-multi',
13851             cn: [
13852                 
13853                 box
13854 //                {
13855 //                    tag: 'ul',
13856 //                    cls: 'typeahead typeahead-long dropdown-menu',
13857 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13858 //                }
13859             ]
13860         };
13861         
13862         if(this.hasFeedback && !this.allowBlank){
13863             
13864             var feedback = {
13865                 tag: 'span',
13866                 cls: 'glyphicon form-control-feedback'
13867             };
13868
13869             combobox.cn.push(feedback);
13870         }
13871         
13872         var indicator = {
13873             tag : 'i',
13874             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13875             tooltip : 'This field is required'
13876         };
13877         if (Roo.bootstrap.version == 4) {
13878             indicator = {
13879                 tag : 'i',
13880                 style : 'display:none'
13881             };
13882         }
13883         if (align ==='left' && this.fieldLabel.length) {
13884             
13885             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13886             
13887             cfg.cn = [
13888                 indicator,
13889                 {
13890                     tag: 'label',
13891                     'for' :  id,
13892                     cls : 'control-label col-form-label',
13893                     html : this.fieldLabel
13894
13895                 },
13896                 {
13897                     cls : "", 
13898                     cn: [
13899                         combobox
13900                     ]
13901                 }
13902
13903             ];
13904             
13905             var labelCfg = cfg.cn[1];
13906             var contentCfg = cfg.cn[2];
13907             
13908
13909             if(this.indicatorpos == 'right'){
13910                 
13911                 cfg.cn = [
13912                     {
13913                         tag: 'label',
13914                         'for' :  id,
13915                         cls : 'control-label col-form-label',
13916                         cn : [
13917                             {
13918                                 tag : 'span',
13919                                 html : this.fieldLabel
13920                             },
13921                             indicator
13922                         ]
13923                     },
13924                     {
13925                         cls : "",
13926                         cn: [
13927                             combobox
13928                         ]
13929                     }
13930
13931                 ];
13932                 
13933                 
13934                 
13935                 labelCfg = cfg.cn[0];
13936                 contentCfg = cfg.cn[1];
13937             
13938             }
13939             
13940             if(this.labelWidth > 12){
13941                 labelCfg.style = "width: " + this.labelWidth + 'px';
13942             }
13943             
13944             if(this.labelWidth < 13 && this.labelmd == 0){
13945                 this.labelmd = this.labelWidth;
13946             }
13947             
13948             if(this.labellg > 0){
13949                 labelCfg.cls += ' col-lg-' + this.labellg;
13950                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13951             }
13952             
13953             if(this.labelmd > 0){
13954                 labelCfg.cls += ' col-md-' + this.labelmd;
13955                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13956             }
13957             
13958             if(this.labelsm > 0){
13959                 labelCfg.cls += ' col-sm-' + this.labelsm;
13960                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13961             }
13962             
13963             if(this.labelxs > 0){
13964                 labelCfg.cls += ' col-xs-' + this.labelxs;
13965                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13966             }
13967                 
13968                 
13969         } else if ( this.fieldLabel.length) {
13970 //                Roo.log(" label");
13971                  cfg.cn = [
13972                    indicator,
13973                     {
13974                         tag: 'label',
13975                         //cls : 'input-group-addon',
13976                         html : this.fieldLabel
13977                     },
13978                     combobox
13979                 ];
13980                 
13981                 if(this.indicatorpos == 'right'){
13982                     cfg.cn = [
13983                         {
13984                             tag: 'label',
13985                             //cls : 'input-group-addon',
13986                             html : this.fieldLabel
13987                         },
13988                         indicator,
13989                         combobox
13990                     ];
13991                     
13992                 }
13993
13994         } else {
13995             
13996 //                Roo.log(" no label && no align");
13997                 cfg = combobox
13998                      
13999                 
14000         }
14001          
14002         var settings=this;
14003         ['xs','sm','md','lg'].map(function(size){
14004             if (settings[size]) {
14005                 cfg.cls += ' col-' + size + '-' + settings[size];
14006             }
14007         });
14008         
14009         return cfg;
14010         
14011     },
14012     
14013     _initEventsCalled : false,
14014     
14015     // private
14016     initEvents: function()
14017     {   
14018         if (this._initEventsCalled) { // as we call render... prevent looping...
14019             return;
14020         }
14021         this._initEventsCalled = true;
14022         
14023         if (!this.store) {
14024             throw "can not find store for combo";
14025         }
14026         
14027         this.indicator = this.indicatorEl();
14028         
14029         this.store = Roo.factory(this.store, Roo.data);
14030         this.store.parent = this;
14031         
14032         // if we are building from html. then this element is so complex, that we can not really
14033         // use the rendered HTML.
14034         // so we have to trash and replace the previous code.
14035         if (Roo.XComponent.build_from_html) {
14036             // remove this element....
14037             var e = this.el.dom, k=0;
14038             while (e ) { e = e.previousSibling;  ++k;}
14039
14040             this.el.remove();
14041             
14042             this.el=false;
14043             this.rendered = false;
14044             
14045             this.render(this.parent().getChildContainer(true), k);
14046         }
14047         
14048         if(Roo.isIOS && this.useNativeIOS){
14049             this.initIOSView();
14050             return;
14051         }
14052         
14053         /*
14054          * Touch Devices
14055          */
14056         
14057         if(Roo.isTouch && this.mobileTouchView){
14058             this.initTouchView();
14059             return;
14060         }
14061         
14062         if(this.tickable){
14063             this.initTickableEvents();
14064             return;
14065         }
14066         
14067         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14068         
14069         if(this.hiddenName){
14070             
14071             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14072             
14073             this.hiddenField.dom.value =
14074                 this.hiddenValue !== undefined ? this.hiddenValue :
14075                 this.value !== undefined ? this.value : '';
14076
14077             // prevent input submission
14078             this.el.dom.removeAttribute('name');
14079             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14080              
14081              
14082         }
14083         //if(Roo.isGecko){
14084         //    this.el.dom.setAttribute('autocomplete', 'off');
14085         //}
14086         
14087         var cls = 'x-combo-list';
14088         
14089         //this.list = new Roo.Layer({
14090         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14091         //});
14092         
14093         var _this = this;
14094         
14095         (function(){
14096             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14097             _this.list.setWidth(lw);
14098         }).defer(100);
14099         
14100         this.list.on('mouseover', this.onViewOver, this);
14101         this.list.on('mousemove', this.onViewMove, this);
14102         this.list.on('scroll', this.onViewScroll, this);
14103         
14104         /*
14105         this.list.swallowEvent('mousewheel');
14106         this.assetHeight = 0;
14107
14108         if(this.title){
14109             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14110             this.assetHeight += this.header.getHeight();
14111         }
14112
14113         this.innerList = this.list.createChild({cls:cls+'-inner'});
14114         this.innerList.on('mouseover', this.onViewOver, this);
14115         this.innerList.on('mousemove', this.onViewMove, this);
14116         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14117         
14118         if(this.allowBlank && !this.pageSize && !this.disableClear){
14119             this.footer = this.list.createChild({cls:cls+'-ft'});
14120             this.pageTb = new Roo.Toolbar(this.footer);
14121            
14122         }
14123         if(this.pageSize){
14124             this.footer = this.list.createChild({cls:cls+'-ft'});
14125             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14126                     {pageSize: this.pageSize});
14127             
14128         }
14129         
14130         if (this.pageTb && this.allowBlank && !this.disableClear) {
14131             var _this = this;
14132             this.pageTb.add(new Roo.Toolbar.Fill(), {
14133                 cls: 'x-btn-icon x-btn-clear',
14134                 text: '&#160;',
14135                 handler: function()
14136                 {
14137                     _this.collapse();
14138                     _this.clearValue();
14139                     _this.onSelect(false, -1);
14140                 }
14141             });
14142         }
14143         if (this.footer) {
14144             this.assetHeight += this.footer.getHeight();
14145         }
14146         */
14147             
14148         if(!this.tpl){
14149             this.tpl = Roo.bootstrap.version == 4 ?
14150                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14151                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14152         }
14153
14154         this.view = new Roo.View(this.list, this.tpl, {
14155             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14156         });
14157         //this.view.wrapEl.setDisplayed(false);
14158         this.view.on('click', this.onViewClick, this);
14159         
14160         
14161         this.store.on('beforeload', this.onBeforeLoad, this);
14162         this.store.on('load', this.onLoad, this);
14163         this.store.on('loadexception', this.onLoadException, this);
14164         /*
14165         if(this.resizable){
14166             this.resizer = new Roo.Resizable(this.list,  {
14167                pinned:true, handles:'se'
14168             });
14169             this.resizer.on('resize', function(r, w, h){
14170                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14171                 this.listWidth = w;
14172                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14173                 this.restrictHeight();
14174             }, this);
14175             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14176         }
14177         */
14178         if(!this.editable){
14179             this.editable = true;
14180             this.setEditable(false);
14181         }
14182         
14183         /*
14184         
14185         if (typeof(this.events.add.listeners) != 'undefined') {
14186             
14187             this.addicon = this.wrap.createChild(
14188                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14189        
14190             this.addicon.on('click', function(e) {
14191                 this.fireEvent('add', this);
14192             }, this);
14193         }
14194         if (typeof(this.events.edit.listeners) != 'undefined') {
14195             
14196             this.editicon = this.wrap.createChild(
14197                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14198             if (this.addicon) {
14199                 this.editicon.setStyle('margin-left', '40px');
14200             }
14201             this.editicon.on('click', function(e) {
14202                 
14203                 // we fire even  if inothing is selected..
14204                 this.fireEvent('edit', this, this.lastData );
14205                 
14206             }, this);
14207         }
14208         */
14209         
14210         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14211             "up" : function(e){
14212                 this.inKeyMode = true;
14213                 this.selectPrev();
14214             },
14215
14216             "down" : function(e){
14217                 if(!this.isExpanded()){
14218                     this.onTriggerClick();
14219                 }else{
14220                     this.inKeyMode = true;
14221                     this.selectNext();
14222                 }
14223             },
14224
14225             "enter" : function(e){
14226 //                this.onViewClick();
14227                 //return true;
14228                 this.collapse();
14229                 
14230                 if(this.fireEvent("specialkey", this, e)){
14231                     this.onViewClick(false);
14232                 }
14233                 
14234                 return true;
14235             },
14236
14237             "esc" : function(e){
14238                 this.collapse();
14239             },
14240
14241             "tab" : function(e){
14242                 this.collapse();
14243                 
14244                 if(this.fireEvent("specialkey", this, e)){
14245                     this.onViewClick(false);
14246                 }
14247                 
14248                 return true;
14249             },
14250
14251             scope : this,
14252
14253             doRelay : function(foo, bar, hname){
14254                 if(hname == 'down' || this.scope.isExpanded()){
14255                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14256                 }
14257                 return true;
14258             },
14259
14260             forceKeyDown: true
14261         });
14262         
14263         
14264         this.queryDelay = Math.max(this.queryDelay || 10,
14265                 this.mode == 'local' ? 10 : 250);
14266         
14267         
14268         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14269         
14270         if(this.typeAhead){
14271             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14272         }
14273         if(this.editable !== false){
14274             this.inputEl().on("keyup", this.onKeyUp, this);
14275         }
14276         if(this.forceSelection){
14277             this.inputEl().on('blur', this.doForce, this);
14278         }
14279         
14280         if(this.multiple){
14281             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14282             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14283         }
14284     },
14285     
14286     initTickableEvents: function()
14287     {   
14288         this.createList();
14289         
14290         if(this.hiddenName){
14291             
14292             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14293             
14294             this.hiddenField.dom.value =
14295                 this.hiddenValue !== undefined ? this.hiddenValue :
14296                 this.value !== undefined ? this.value : '';
14297
14298             // prevent input submission
14299             this.el.dom.removeAttribute('name');
14300             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14301              
14302              
14303         }
14304         
14305 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14306         
14307         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14308         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14309         if(this.triggerList){
14310             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14311         }
14312          
14313         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14314         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14315         
14316         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14317         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14318         
14319         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14320         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14321         
14322         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14323         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14324         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14325         
14326         this.okBtn.hide();
14327         this.cancelBtn.hide();
14328         
14329         var _this = this;
14330         
14331         (function(){
14332             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14333             _this.list.setWidth(lw);
14334         }).defer(100);
14335         
14336         this.list.on('mouseover', this.onViewOver, this);
14337         this.list.on('mousemove', this.onViewMove, this);
14338         
14339         this.list.on('scroll', this.onViewScroll, this);
14340         
14341         if(!this.tpl){
14342             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14343                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14344         }
14345
14346         this.view = new Roo.View(this.list, this.tpl, {
14347             singleSelect:true,
14348             tickable:true,
14349             parent:this,
14350             store: this.store,
14351             selectedClass: this.selectedClass
14352         });
14353         
14354         //this.view.wrapEl.setDisplayed(false);
14355         this.view.on('click', this.onViewClick, this);
14356         
14357         
14358         
14359         this.store.on('beforeload', this.onBeforeLoad, this);
14360         this.store.on('load', this.onLoad, this);
14361         this.store.on('loadexception', this.onLoadException, this);
14362         
14363         if(this.editable){
14364             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14365                 "up" : function(e){
14366                     this.inKeyMode = true;
14367                     this.selectPrev();
14368                 },
14369
14370                 "down" : function(e){
14371                     this.inKeyMode = true;
14372                     this.selectNext();
14373                 },
14374
14375                 "enter" : function(e){
14376                     if(this.fireEvent("specialkey", this, e)){
14377                         this.onViewClick(false);
14378                     }
14379                     
14380                     return true;
14381                 },
14382
14383                 "esc" : function(e){
14384                     this.onTickableFooterButtonClick(e, false, false);
14385                 },
14386
14387                 "tab" : function(e){
14388                     this.fireEvent("specialkey", this, e);
14389                     
14390                     this.onTickableFooterButtonClick(e, false, false);
14391                     
14392                     return true;
14393                 },
14394
14395                 scope : this,
14396
14397                 doRelay : function(e, fn, key){
14398                     if(this.scope.isExpanded()){
14399                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14400                     }
14401                     return true;
14402                 },
14403
14404                 forceKeyDown: true
14405             });
14406         }
14407         
14408         this.queryDelay = Math.max(this.queryDelay || 10,
14409                 this.mode == 'local' ? 10 : 250);
14410         
14411         
14412         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14413         
14414         if(this.typeAhead){
14415             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14416         }
14417         
14418         if(this.editable !== false){
14419             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14420         }
14421         
14422         this.indicator = this.indicatorEl();
14423         
14424         if(this.indicator){
14425             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14426             this.indicator.hide();
14427         }
14428         
14429     },
14430
14431     onDestroy : function(){
14432         if(this.view){
14433             this.view.setStore(null);
14434             this.view.el.removeAllListeners();
14435             this.view.el.remove();
14436             this.view.purgeListeners();
14437         }
14438         if(this.list){
14439             this.list.dom.innerHTML  = '';
14440         }
14441         
14442         if(this.store){
14443             this.store.un('beforeload', this.onBeforeLoad, this);
14444             this.store.un('load', this.onLoad, this);
14445             this.store.un('loadexception', this.onLoadException, this);
14446         }
14447         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14448     },
14449
14450     // private
14451     fireKey : function(e){
14452         if(e.isNavKeyPress() && !this.list.isVisible()){
14453             this.fireEvent("specialkey", this, e);
14454         }
14455     },
14456
14457     // private
14458     onResize: function(w, h){
14459 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14460 //        
14461 //        if(typeof w != 'number'){
14462 //            // we do not handle it!?!?
14463 //            return;
14464 //        }
14465 //        var tw = this.trigger.getWidth();
14466 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14467 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14468 //        var x = w - tw;
14469 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14470 //            
14471 //        //this.trigger.setStyle('left', x+'px');
14472 //        
14473 //        if(this.list && this.listWidth === undefined){
14474 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14475 //            this.list.setWidth(lw);
14476 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14477 //        }
14478         
14479     
14480         
14481     },
14482
14483     /**
14484      * Allow or prevent the user from directly editing the field text.  If false is passed,
14485      * the user will only be able to select from the items defined in the dropdown list.  This method
14486      * is the runtime equivalent of setting the 'editable' config option at config time.
14487      * @param {Boolean} value True to allow the user to directly edit the field text
14488      */
14489     setEditable : function(value){
14490         if(value == this.editable){
14491             return;
14492         }
14493         this.editable = value;
14494         if(!value){
14495             this.inputEl().dom.setAttribute('readOnly', true);
14496             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14497             this.inputEl().addClass('x-combo-noedit');
14498         }else{
14499             this.inputEl().dom.setAttribute('readOnly', false);
14500             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14501             this.inputEl().removeClass('x-combo-noedit');
14502         }
14503     },
14504
14505     // private
14506     
14507     onBeforeLoad : function(combo,opts){
14508         if(!this.hasFocus){
14509             return;
14510         }
14511          if (!opts.add) {
14512             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14513          }
14514         this.restrictHeight();
14515         this.selectedIndex = -1;
14516     },
14517
14518     // private
14519     onLoad : function(){
14520         
14521         this.hasQuery = false;
14522         
14523         if(!this.hasFocus){
14524             return;
14525         }
14526         
14527         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14528             this.loading.hide();
14529         }
14530         
14531         if(this.store.getCount() > 0){
14532             
14533             this.expand();
14534             this.restrictHeight();
14535             if(this.lastQuery == this.allQuery){
14536                 if(this.editable && !this.tickable){
14537                     this.inputEl().dom.select();
14538                 }
14539                 
14540                 if(
14541                     !this.selectByValue(this.value, true) &&
14542                     this.autoFocus && 
14543                     (
14544                         !this.store.lastOptions ||
14545                         typeof(this.store.lastOptions.add) == 'undefined' || 
14546                         this.store.lastOptions.add != true
14547                     )
14548                 ){
14549                     this.select(0, true);
14550                 }
14551             }else{
14552                 if(this.autoFocus){
14553                     this.selectNext();
14554                 }
14555                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14556                     this.taTask.delay(this.typeAheadDelay);
14557                 }
14558             }
14559         }else{
14560             this.onEmptyResults();
14561         }
14562         
14563         //this.el.focus();
14564     },
14565     // private
14566     onLoadException : function()
14567     {
14568         this.hasQuery = false;
14569         
14570         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14571             this.loading.hide();
14572         }
14573         
14574         if(this.tickable && this.editable){
14575             return;
14576         }
14577         
14578         this.collapse();
14579         // only causes errors at present
14580         //Roo.log(this.store.reader.jsonData);
14581         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14582             // fixme
14583             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14584         //}
14585         
14586         
14587     },
14588     // private
14589     onTypeAhead : function(){
14590         if(this.store.getCount() > 0){
14591             var r = this.store.getAt(0);
14592             var newValue = r.data[this.displayField];
14593             var len = newValue.length;
14594             var selStart = this.getRawValue().length;
14595             
14596             if(selStart != len){
14597                 this.setRawValue(newValue);
14598                 this.selectText(selStart, newValue.length);
14599             }
14600         }
14601     },
14602
14603     // private
14604     onSelect : function(record, index){
14605         
14606         if(this.fireEvent('beforeselect', this, record, index) !== false){
14607         
14608             this.setFromData(index > -1 ? record.data : false);
14609             
14610             this.collapse();
14611             this.fireEvent('select', this, record, index);
14612         }
14613     },
14614
14615     /**
14616      * Returns the currently selected field value or empty string if no value is set.
14617      * @return {String} value The selected value
14618      */
14619     getValue : function()
14620     {
14621         if(Roo.isIOS && this.useNativeIOS){
14622             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14623         }
14624         
14625         if(this.multiple){
14626             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14627         }
14628         
14629         if(this.valueField){
14630             return typeof this.value != 'undefined' ? this.value : '';
14631         }else{
14632             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14633         }
14634     },
14635     
14636     getRawValue : function()
14637     {
14638         if(Roo.isIOS && this.useNativeIOS){
14639             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14640         }
14641         
14642         var v = this.inputEl().getValue();
14643         
14644         return v;
14645     },
14646
14647     /**
14648      * Clears any text/value currently set in the field
14649      */
14650     clearValue : function(){
14651         
14652         if(this.hiddenField){
14653             this.hiddenField.dom.value = '';
14654         }
14655         this.value = '';
14656         this.setRawValue('');
14657         this.lastSelectionText = '';
14658         this.lastData = false;
14659         
14660         var close = this.closeTriggerEl();
14661         
14662         if(close){
14663             close.hide();
14664         }
14665         
14666         this.validate();
14667         
14668     },
14669
14670     /**
14671      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14672      * will be displayed in the field.  If the value does not match the data value of an existing item,
14673      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14674      * Otherwise the field will be blank (although the value will still be set).
14675      * @param {String} value The value to match
14676      */
14677     setValue : function(v)
14678     {
14679         if(Roo.isIOS && this.useNativeIOS){
14680             this.setIOSValue(v);
14681             return;
14682         }
14683         
14684         if(this.multiple){
14685             this.syncValue();
14686             return;
14687         }
14688         
14689         var text = v;
14690         if(this.valueField){
14691             var r = this.findRecord(this.valueField, v);
14692             if(r){
14693                 text = r.data[this.displayField];
14694             }else if(this.valueNotFoundText !== undefined){
14695                 text = this.valueNotFoundText;
14696             }
14697         }
14698         this.lastSelectionText = text;
14699         if(this.hiddenField){
14700             this.hiddenField.dom.value = v;
14701         }
14702         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14703         this.value = v;
14704         
14705         var close = this.closeTriggerEl();
14706         
14707         if(close){
14708             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14709         }
14710         
14711         this.validate();
14712     },
14713     /**
14714      * @property {Object} the last set data for the element
14715      */
14716     
14717     lastData : false,
14718     /**
14719      * Sets the value of the field based on a object which is related to the record format for the store.
14720      * @param {Object} value the value to set as. or false on reset?
14721      */
14722     setFromData : function(o){
14723         
14724         if(this.multiple){
14725             this.addItem(o);
14726             return;
14727         }
14728             
14729         var dv = ''; // display value
14730         var vv = ''; // value value..
14731         this.lastData = o;
14732         if (this.displayField) {
14733             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14734         } else {
14735             // this is an error condition!!!
14736             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14737         }
14738         
14739         if(this.valueField){
14740             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14741         }
14742         
14743         var close = this.closeTriggerEl();
14744         
14745         if(close){
14746             if(dv.length || vv * 1 > 0){
14747                 close.show() ;
14748                 this.blockFocus=true;
14749             } else {
14750                 close.hide();
14751             }             
14752         }
14753         
14754         if(this.hiddenField){
14755             this.hiddenField.dom.value = vv;
14756             
14757             this.lastSelectionText = dv;
14758             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14759             this.value = vv;
14760             return;
14761         }
14762         // no hidden field.. - we store the value in 'value', but still display
14763         // display field!!!!
14764         this.lastSelectionText = dv;
14765         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14766         this.value = vv;
14767         
14768         
14769         
14770     },
14771     // private
14772     reset : function(){
14773         // overridden so that last data is reset..
14774         
14775         if(this.multiple){
14776             this.clearItem();
14777             return;
14778         }
14779         
14780         this.setValue(this.originalValue);
14781         //this.clearInvalid();
14782         this.lastData = false;
14783         if (this.view) {
14784             this.view.clearSelections();
14785         }
14786         
14787         this.validate();
14788     },
14789     // private
14790     findRecord : function(prop, value){
14791         var record;
14792         if(this.store.getCount() > 0){
14793             this.store.each(function(r){
14794                 if(r.data[prop] == value){
14795                     record = r;
14796                     return false;
14797                 }
14798                 return true;
14799             });
14800         }
14801         return record;
14802     },
14803     
14804     getName: function()
14805     {
14806         // returns hidden if it's set..
14807         if (!this.rendered) {return ''};
14808         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14809         
14810     },
14811     // private
14812     onViewMove : function(e, t){
14813         this.inKeyMode = false;
14814     },
14815
14816     // private
14817     onViewOver : function(e, t){
14818         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14819             return;
14820         }
14821         var item = this.view.findItemFromChild(t);
14822         
14823         if(item){
14824             var index = this.view.indexOf(item);
14825             this.select(index, false);
14826         }
14827     },
14828
14829     // private
14830     onViewClick : function(view, doFocus, el, e)
14831     {
14832         var index = this.view.getSelectedIndexes()[0];
14833         
14834         var r = this.store.getAt(index);
14835         
14836         if(this.tickable){
14837             
14838             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14839                 return;
14840             }
14841             
14842             var rm = false;
14843             var _this = this;
14844             
14845             Roo.each(this.tickItems, function(v,k){
14846                 
14847                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14848                     Roo.log(v);
14849                     _this.tickItems.splice(k, 1);
14850                     
14851                     if(typeof(e) == 'undefined' && view == false){
14852                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14853                     }
14854                     
14855                     rm = true;
14856                     return;
14857                 }
14858             });
14859             
14860             if(rm){
14861                 return;
14862             }
14863             
14864             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14865                 this.tickItems.push(r.data);
14866             }
14867             
14868             if(typeof(e) == 'undefined' && view == false){
14869                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14870             }
14871                     
14872             return;
14873         }
14874         
14875         if(r){
14876             this.onSelect(r, index);
14877         }
14878         if(doFocus !== false && !this.blockFocus){
14879             this.inputEl().focus();
14880         }
14881     },
14882
14883     // private
14884     restrictHeight : function(){
14885         //this.innerList.dom.style.height = '';
14886         //var inner = this.innerList.dom;
14887         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14888         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14889         //this.list.beginUpdate();
14890         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14891         this.list.alignTo(this.inputEl(), this.listAlign);
14892         this.list.alignTo(this.inputEl(), this.listAlign);
14893         //this.list.endUpdate();
14894     },
14895
14896     // private
14897     onEmptyResults : function(){
14898         
14899         if(this.tickable && this.editable){
14900             this.hasFocus = false;
14901             this.restrictHeight();
14902             return;
14903         }
14904         
14905         this.collapse();
14906     },
14907
14908     /**
14909      * Returns true if the dropdown list is expanded, else false.
14910      */
14911     isExpanded : function(){
14912         return this.list.isVisible();
14913     },
14914
14915     /**
14916      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14917      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14918      * @param {String} value The data value of the item to select
14919      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14920      * selected item if it is not currently in view (defaults to true)
14921      * @return {Boolean} True if the value matched an item in the list, else false
14922      */
14923     selectByValue : function(v, scrollIntoView){
14924         if(v !== undefined && v !== null){
14925             var r = this.findRecord(this.valueField || this.displayField, v);
14926             if(r){
14927                 this.select(this.store.indexOf(r), scrollIntoView);
14928                 return true;
14929             }
14930         }
14931         return false;
14932     },
14933
14934     /**
14935      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14936      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14937      * @param {Number} index The zero-based index of the list item to select
14938      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14939      * selected item if it is not currently in view (defaults to true)
14940      */
14941     select : function(index, scrollIntoView){
14942         this.selectedIndex = index;
14943         this.view.select(index);
14944         if(scrollIntoView !== false){
14945             var el = this.view.getNode(index);
14946             /*
14947              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14948              */
14949             if(el){
14950                 this.list.scrollChildIntoView(el, false);
14951             }
14952         }
14953     },
14954
14955     // private
14956     selectNext : function(){
14957         var ct = this.store.getCount();
14958         if(ct > 0){
14959             if(this.selectedIndex == -1){
14960                 this.select(0);
14961             }else if(this.selectedIndex < ct-1){
14962                 this.select(this.selectedIndex+1);
14963             }
14964         }
14965     },
14966
14967     // private
14968     selectPrev : function(){
14969         var ct = this.store.getCount();
14970         if(ct > 0){
14971             if(this.selectedIndex == -1){
14972                 this.select(0);
14973             }else if(this.selectedIndex != 0){
14974                 this.select(this.selectedIndex-1);
14975             }
14976         }
14977     },
14978
14979     // private
14980     onKeyUp : function(e){
14981         if(this.editable !== false && !e.isSpecialKey()){
14982             this.lastKey = e.getKey();
14983             this.dqTask.delay(this.queryDelay);
14984         }
14985     },
14986
14987     // private
14988     validateBlur : function(){
14989         return !this.list || !this.list.isVisible();   
14990     },
14991
14992     // private
14993     initQuery : function(){
14994         
14995         var v = this.getRawValue();
14996         
14997         if(this.tickable && this.editable){
14998             v = this.tickableInputEl().getValue();
14999         }
15000         
15001         this.doQuery(v);
15002     },
15003
15004     // private
15005     doForce : function(){
15006         if(this.inputEl().dom.value.length > 0){
15007             this.inputEl().dom.value =
15008                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15009              
15010         }
15011     },
15012
15013     /**
15014      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15015      * query allowing the query action to be canceled if needed.
15016      * @param {String} query The SQL query to execute
15017      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15018      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15019      * saved in the current store (defaults to false)
15020      */
15021     doQuery : function(q, forceAll){
15022         
15023         if(q === undefined || q === null){
15024             q = '';
15025         }
15026         var qe = {
15027             query: q,
15028             forceAll: forceAll,
15029             combo: this,
15030             cancel:false
15031         };
15032         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15033             return false;
15034         }
15035         q = qe.query;
15036         
15037         forceAll = qe.forceAll;
15038         if(forceAll === true || (q.length >= this.minChars)){
15039             
15040             this.hasQuery = true;
15041             
15042             if(this.lastQuery != q || this.alwaysQuery){
15043                 this.lastQuery = q;
15044                 if(this.mode == 'local'){
15045                     this.selectedIndex = -1;
15046                     if(forceAll){
15047                         this.store.clearFilter();
15048                     }else{
15049                         
15050                         if(this.specialFilter){
15051                             this.fireEvent('specialfilter', this);
15052                             this.onLoad();
15053                             return;
15054                         }
15055                         
15056                         this.store.filter(this.displayField, q);
15057                     }
15058                     
15059                     this.store.fireEvent("datachanged", this.store);
15060                     
15061                     this.onLoad();
15062                     
15063                     
15064                 }else{
15065                     
15066                     this.store.baseParams[this.queryParam] = q;
15067                     
15068                     var options = {params : this.getParams(q)};
15069                     
15070                     if(this.loadNext){
15071                         options.add = true;
15072                         options.params.start = this.page * this.pageSize;
15073                     }
15074                     
15075                     this.store.load(options);
15076                     
15077                     /*
15078                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15079                      *  we should expand the list on onLoad
15080                      *  so command out it
15081                      */
15082 //                    this.expand();
15083                 }
15084             }else{
15085                 this.selectedIndex = -1;
15086                 this.onLoad();   
15087             }
15088         }
15089         
15090         this.loadNext = false;
15091     },
15092     
15093     // private
15094     getParams : function(q){
15095         var p = {};
15096         //p[this.queryParam] = q;
15097         
15098         if(this.pageSize){
15099             p.start = 0;
15100             p.limit = this.pageSize;
15101         }
15102         return p;
15103     },
15104
15105     /**
15106      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15107      */
15108     collapse : function(){
15109         if(!this.isExpanded()){
15110             return;
15111         }
15112         
15113         this.list.hide();
15114         
15115         this.hasFocus = false;
15116         
15117         if(this.tickable){
15118             this.okBtn.hide();
15119             this.cancelBtn.hide();
15120             this.trigger.show();
15121             
15122             if(this.editable){
15123                 this.tickableInputEl().dom.value = '';
15124                 this.tickableInputEl().blur();
15125             }
15126             
15127         }
15128         
15129         Roo.get(document).un('mousedown', this.collapseIf, this);
15130         Roo.get(document).un('mousewheel', this.collapseIf, this);
15131         if (!this.editable) {
15132             Roo.get(document).un('keydown', this.listKeyPress, this);
15133         }
15134         this.fireEvent('collapse', this);
15135         
15136         this.validate();
15137     },
15138
15139     // private
15140     collapseIf : function(e){
15141         var in_combo  = e.within(this.el);
15142         var in_list =  e.within(this.list);
15143         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15144         
15145         if (in_combo || in_list || is_list) {
15146             //e.stopPropagation();
15147             return;
15148         }
15149         
15150         if(this.tickable){
15151             this.onTickableFooterButtonClick(e, false, false);
15152         }
15153
15154         this.collapse();
15155         
15156     },
15157
15158     /**
15159      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15160      */
15161     expand : function(){
15162        
15163         if(this.isExpanded() || !this.hasFocus){
15164             return;
15165         }
15166         
15167         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15168         this.list.setWidth(lw);
15169         
15170         Roo.log('expand');
15171         
15172         this.list.show();
15173         
15174         this.restrictHeight();
15175         
15176         if(this.tickable){
15177             
15178             this.tickItems = Roo.apply([], this.item);
15179             
15180             this.okBtn.show();
15181             this.cancelBtn.show();
15182             this.trigger.hide();
15183             
15184             if(this.editable){
15185                 this.tickableInputEl().focus();
15186             }
15187             
15188         }
15189         
15190         Roo.get(document).on('mousedown', this.collapseIf, this);
15191         Roo.get(document).on('mousewheel', this.collapseIf, this);
15192         if (!this.editable) {
15193             Roo.get(document).on('keydown', this.listKeyPress, this);
15194         }
15195         
15196         this.fireEvent('expand', this);
15197     },
15198
15199     // private
15200     // Implements the default empty TriggerField.onTriggerClick function
15201     onTriggerClick : function(e)
15202     {
15203         Roo.log('trigger click');
15204         
15205         if(this.disabled || !this.triggerList){
15206             return;
15207         }
15208         
15209         this.page = 0;
15210         this.loadNext = false;
15211         
15212         if(this.isExpanded()){
15213             this.collapse();
15214             if (!this.blockFocus) {
15215                 this.inputEl().focus();
15216             }
15217             
15218         }else {
15219             this.hasFocus = true;
15220             if(this.triggerAction == 'all') {
15221                 this.doQuery(this.allQuery, true);
15222             } else {
15223                 this.doQuery(this.getRawValue());
15224             }
15225             if (!this.blockFocus) {
15226                 this.inputEl().focus();
15227             }
15228         }
15229     },
15230     
15231     onTickableTriggerClick : function(e)
15232     {
15233         if(this.disabled){
15234             return;
15235         }
15236         
15237         this.page = 0;
15238         this.loadNext = false;
15239         this.hasFocus = true;
15240         
15241         if(this.triggerAction == 'all') {
15242             this.doQuery(this.allQuery, true);
15243         } else {
15244             this.doQuery(this.getRawValue());
15245         }
15246     },
15247     
15248     onSearchFieldClick : function(e)
15249     {
15250         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15251             this.onTickableFooterButtonClick(e, false, false);
15252             return;
15253         }
15254         
15255         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15256             return;
15257         }
15258         
15259         this.page = 0;
15260         this.loadNext = false;
15261         this.hasFocus = true;
15262         
15263         if(this.triggerAction == 'all') {
15264             this.doQuery(this.allQuery, true);
15265         } else {
15266             this.doQuery(this.getRawValue());
15267         }
15268     },
15269     
15270     listKeyPress : function(e)
15271     {
15272         //Roo.log('listkeypress');
15273         // scroll to first matching element based on key pres..
15274         if (e.isSpecialKey()) {
15275             return false;
15276         }
15277         var k = String.fromCharCode(e.getKey()).toUpperCase();
15278         //Roo.log(k);
15279         var match  = false;
15280         var csel = this.view.getSelectedNodes();
15281         var cselitem = false;
15282         if (csel.length) {
15283             var ix = this.view.indexOf(csel[0]);
15284             cselitem  = this.store.getAt(ix);
15285             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15286                 cselitem = false;
15287             }
15288             
15289         }
15290         
15291         this.store.each(function(v) { 
15292             if (cselitem) {
15293                 // start at existing selection.
15294                 if (cselitem.id == v.id) {
15295                     cselitem = false;
15296                 }
15297                 return true;
15298             }
15299                 
15300             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15301                 match = this.store.indexOf(v);
15302                 return false;
15303             }
15304             return true;
15305         }, this);
15306         
15307         if (match === false) {
15308             return true; // no more action?
15309         }
15310         // scroll to?
15311         this.view.select(match);
15312         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15313         sn.scrollIntoView(sn.dom.parentNode, false);
15314     },
15315     
15316     onViewScroll : function(e, t){
15317         
15318         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){
15319             return;
15320         }
15321         
15322         this.hasQuery = true;
15323         
15324         this.loading = this.list.select('.loading', true).first();
15325         
15326         if(this.loading === null){
15327             this.list.createChild({
15328                 tag: 'div',
15329                 cls: 'loading roo-select2-more-results roo-select2-active',
15330                 html: 'Loading more results...'
15331             });
15332             
15333             this.loading = this.list.select('.loading', true).first();
15334             
15335             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15336             
15337             this.loading.hide();
15338         }
15339         
15340         this.loading.show();
15341         
15342         var _combo = this;
15343         
15344         this.page++;
15345         this.loadNext = true;
15346         
15347         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15348         
15349         return;
15350     },
15351     
15352     addItem : function(o)
15353     {   
15354         var dv = ''; // display value
15355         
15356         if (this.displayField) {
15357             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15358         } else {
15359             // this is an error condition!!!
15360             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15361         }
15362         
15363         if(!dv.length){
15364             return;
15365         }
15366         
15367         var choice = this.choices.createChild({
15368             tag: 'li',
15369             cls: 'roo-select2-search-choice',
15370             cn: [
15371                 {
15372                     tag: 'div',
15373                     html: dv
15374                 },
15375                 {
15376                     tag: 'a',
15377                     href: '#',
15378                     cls: 'roo-select2-search-choice-close fa fa-times',
15379                     tabindex: '-1'
15380                 }
15381             ]
15382             
15383         }, this.searchField);
15384         
15385         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15386         
15387         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15388         
15389         this.item.push(o);
15390         
15391         this.lastData = o;
15392         
15393         this.syncValue();
15394         
15395         this.inputEl().dom.value = '';
15396         
15397         this.validate();
15398     },
15399     
15400     onRemoveItem : function(e, _self, o)
15401     {
15402         e.preventDefault();
15403         
15404         this.lastItem = Roo.apply([], this.item);
15405         
15406         var index = this.item.indexOf(o.data) * 1;
15407         
15408         if( index < 0){
15409             Roo.log('not this item?!');
15410             return;
15411         }
15412         
15413         this.item.splice(index, 1);
15414         o.item.remove();
15415         
15416         this.syncValue();
15417         
15418         this.fireEvent('remove', this, e);
15419         
15420         this.validate();
15421         
15422     },
15423     
15424     syncValue : function()
15425     {
15426         if(!this.item.length){
15427             this.clearValue();
15428             return;
15429         }
15430             
15431         var value = [];
15432         var _this = this;
15433         Roo.each(this.item, function(i){
15434             if(_this.valueField){
15435                 value.push(i[_this.valueField]);
15436                 return;
15437             }
15438
15439             value.push(i);
15440         });
15441
15442         this.value = value.join(',');
15443
15444         if(this.hiddenField){
15445             this.hiddenField.dom.value = this.value;
15446         }
15447         
15448         this.store.fireEvent("datachanged", this.store);
15449         
15450         this.validate();
15451     },
15452     
15453     clearItem : function()
15454     {
15455         if(!this.multiple){
15456             return;
15457         }
15458         
15459         this.item = [];
15460         
15461         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15462            c.remove();
15463         });
15464         
15465         this.syncValue();
15466         
15467         this.validate();
15468         
15469         if(this.tickable && !Roo.isTouch){
15470             this.view.refresh();
15471         }
15472     },
15473     
15474     inputEl: function ()
15475     {
15476         if(Roo.isIOS && this.useNativeIOS){
15477             return this.el.select('select.roo-ios-select', true).first();
15478         }
15479         
15480         if(Roo.isTouch && this.mobileTouchView){
15481             return this.el.select('input.form-control',true).first();
15482         }
15483         
15484         if(this.tickable){
15485             return this.searchField;
15486         }
15487         
15488         return this.el.select('input.form-control',true).first();
15489     },
15490     
15491     onTickableFooterButtonClick : function(e, btn, el)
15492     {
15493         e.preventDefault();
15494         
15495         this.lastItem = Roo.apply([], this.item);
15496         
15497         if(btn && btn.name == 'cancel'){
15498             this.tickItems = Roo.apply([], this.item);
15499             this.collapse();
15500             return;
15501         }
15502         
15503         this.clearItem();
15504         
15505         var _this = this;
15506         
15507         Roo.each(this.tickItems, function(o){
15508             _this.addItem(o);
15509         });
15510         
15511         this.collapse();
15512         
15513     },
15514     
15515     validate : function()
15516     {
15517         if(this.getVisibilityEl().hasClass('hidden')){
15518             return true;
15519         }
15520         
15521         var v = this.getRawValue();
15522         
15523         if(this.multiple){
15524             v = this.getValue();
15525         }
15526         
15527         if(this.disabled || this.allowBlank || v.length){
15528             this.markValid();
15529             return true;
15530         }
15531         
15532         this.markInvalid();
15533         return false;
15534     },
15535     
15536     tickableInputEl : function()
15537     {
15538         if(!this.tickable || !this.editable){
15539             return this.inputEl();
15540         }
15541         
15542         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15543     },
15544     
15545     
15546     getAutoCreateTouchView : function()
15547     {
15548         var id = Roo.id();
15549         
15550         var cfg = {
15551             cls: 'form-group' //input-group
15552         };
15553         
15554         var input =  {
15555             tag: 'input',
15556             id : id,
15557             type : this.inputType,
15558             cls : 'form-control x-combo-noedit',
15559             autocomplete: 'new-password',
15560             placeholder : this.placeholder || '',
15561             readonly : true
15562         };
15563         
15564         if (this.name) {
15565             input.name = this.name;
15566         }
15567         
15568         if (this.size) {
15569             input.cls += ' input-' + this.size;
15570         }
15571         
15572         if (this.disabled) {
15573             input.disabled = true;
15574         }
15575         
15576         var inputblock = {
15577             cls : '',
15578             cn : [
15579                 input
15580             ]
15581         };
15582         
15583         if(this.before){
15584             inputblock.cls += ' input-group';
15585             
15586             inputblock.cn.unshift({
15587                 tag :'span',
15588                 cls : 'input-group-addon input-group-prepend input-group-text',
15589                 html : this.before
15590             });
15591         }
15592         
15593         if(this.removable && !this.multiple){
15594             inputblock.cls += ' roo-removable';
15595             
15596             inputblock.cn.push({
15597                 tag: 'button',
15598                 html : 'x',
15599                 cls : 'roo-combo-removable-btn close'
15600             });
15601         }
15602
15603         if(this.hasFeedback && !this.allowBlank){
15604             
15605             inputblock.cls += ' has-feedback';
15606             
15607             inputblock.cn.push({
15608                 tag: 'span',
15609                 cls: 'glyphicon form-control-feedback'
15610             });
15611             
15612         }
15613         
15614         if (this.after) {
15615             
15616             inputblock.cls += (this.before) ? '' : ' input-group';
15617             
15618             inputblock.cn.push({
15619                 tag :'span',
15620                 cls : 'input-group-addon input-group-append input-group-text',
15621                 html : this.after
15622             });
15623         }
15624
15625         
15626         var ibwrap = inputblock;
15627         
15628         if(this.multiple){
15629             ibwrap = {
15630                 tag: 'ul',
15631                 cls: 'roo-select2-choices',
15632                 cn:[
15633                     {
15634                         tag: 'li',
15635                         cls: 'roo-select2-search-field',
15636                         cn: [
15637
15638                             inputblock
15639                         ]
15640                     }
15641                 ]
15642             };
15643         
15644             
15645         }
15646         
15647         var combobox = {
15648             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15649             cn: [
15650                 {
15651                     tag: 'input',
15652                     type : 'hidden',
15653                     cls: 'form-hidden-field'
15654                 },
15655                 ibwrap
15656             ]
15657         };
15658         
15659         if(!this.multiple && this.showToggleBtn){
15660             
15661             var caret = {
15662                 cls: 'caret'
15663             };
15664             
15665             if (this.caret != false) {
15666                 caret = {
15667                      tag: 'i',
15668                      cls: 'fa fa-' + this.caret
15669                 };
15670                 
15671             }
15672             
15673             combobox.cn.push({
15674                 tag :'span',
15675                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15676                 cn : [
15677                     Roo.bootstrap.version == 3 ? caret : '',
15678                     {
15679                         tag: 'span',
15680                         cls: 'combobox-clear',
15681                         cn  : [
15682                             {
15683                                 tag : 'i',
15684                                 cls: 'icon-remove'
15685                             }
15686                         ]
15687                     }
15688                 ]
15689
15690             })
15691         }
15692         
15693         if(this.multiple){
15694             combobox.cls += ' roo-select2-container-multi';
15695         }
15696         
15697         var align = this.labelAlign || this.parentLabelAlign();
15698         
15699         if (align ==='left' && this.fieldLabel.length) {
15700
15701             cfg.cn = [
15702                 {
15703                    tag : 'i',
15704                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15705                    tooltip : 'This field is required'
15706                 },
15707                 {
15708                     tag: 'label',
15709                     cls : 'control-label col-form-label',
15710                     html : this.fieldLabel
15711
15712                 },
15713                 {
15714                     cls : '', 
15715                     cn: [
15716                         combobox
15717                     ]
15718                 }
15719             ];
15720             
15721             var labelCfg = cfg.cn[1];
15722             var contentCfg = cfg.cn[2];
15723             
15724
15725             if(this.indicatorpos == 'right'){
15726                 cfg.cn = [
15727                     {
15728                         tag: 'label',
15729                         'for' :  id,
15730                         cls : 'control-label col-form-label',
15731                         cn : [
15732                             {
15733                                 tag : 'span',
15734                                 html : this.fieldLabel
15735                             },
15736                             {
15737                                 tag : 'i',
15738                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15739                                 tooltip : 'This field is required'
15740                             }
15741                         ]
15742                     },
15743                     {
15744                         cls : "",
15745                         cn: [
15746                             combobox
15747                         ]
15748                     }
15749
15750                 ];
15751                 
15752                 labelCfg = cfg.cn[0];
15753                 contentCfg = cfg.cn[1];
15754             }
15755             
15756            
15757             
15758             if(this.labelWidth > 12){
15759                 labelCfg.style = "width: " + this.labelWidth + 'px';
15760             }
15761             
15762             if(this.labelWidth < 13 && this.labelmd == 0){
15763                 this.labelmd = this.labelWidth;
15764             }
15765             
15766             if(this.labellg > 0){
15767                 labelCfg.cls += ' col-lg-' + this.labellg;
15768                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15769             }
15770             
15771             if(this.labelmd > 0){
15772                 labelCfg.cls += ' col-md-' + this.labelmd;
15773                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15774             }
15775             
15776             if(this.labelsm > 0){
15777                 labelCfg.cls += ' col-sm-' + this.labelsm;
15778                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15779             }
15780             
15781             if(this.labelxs > 0){
15782                 labelCfg.cls += ' col-xs-' + this.labelxs;
15783                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15784             }
15785                 
15786                 
15787         } else if ( this.fieldLabel.length) {
15788             cfg.cn = [
15789                 {
15790                    tag : 'i',
15791                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15792                    tooltip : 'This field is required'
15793                 },
15794                 {
15795                     tag: 'label',
15796                     cls : 'control-label',
15797                     html : this.fieldLabel
15798
15799                 },
15800                 {
15801                     cls : '', 
15802                     cn: [
15803                         combobox
15804                     ]
15805                 }
15806             ];
15807             
15808             if(this.indicatorpos == 'right'){
15809                 cfg.cn = [
15810                     {
15811                         tag: 'label',
15812                         cls : 'control-label',
15813                         html : this.fieldLabel,
15814                         cn : [
15815                             {
15816                                tag : 'i',
15817                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15818                                tooltip : 'This field is required'
15819                             }
15820                         ]
15821                     },
15822                     {
15823                         cls : '', 
15824                         cn: [
15825                             combobox
15826                         ]
15827                     }
15828                 ];
15829             }
15830         } else {
15831             cfg.cn = combobox;    
15832         }
15833         
15834         
15835         var settings = this;
15836         
15837         ['xs','sm','md','lg'].map(function(size){
15838             if (settings[size]) {
15839                 cfg.cls += ' col-' + size + '-' + settings[size];
15840             }
15841         });
15842         
15843         return cfg;
15844     },
15845     
15846     initTouchView : function()
15847     {
15848         this.renderTouchView();
15849         
15850         this.touchViewEl.on('scroll', function(){
15851             this.el.dom.scrollTop = 0;
15852         }, this);
15853         
15854         this.originalValue = this.getValue();
15855         
15856         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15857         
15858         this.inputEl().on("click", this.showTouchView, this);
15859         if (this.triggerEl) {
15860             this.triggerEl.on("click", this.showTouchView, this);
15861         }
15862         
15863         
15864         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15865         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15866         
15867         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15868         
15869         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15870         this.store.on('load', this.onTouchViewLoad, this);
15871         this.store.on('loadexception', this.onTouchViewLoadException, this);
15872         
15873         if(this.hiddenName){
15874             
15875             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15876             
15877             this.hiddenField.dom.value =
15878                 this.hiddenValue !== undefined ? this.hiddenValue :
15879                 this.value !== undefined ? this.value : '';
15880         
15881             this.el.dom.removeAttribute('name');
15882             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15883         }
15884         
15885         if(this.multiple){
15886             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15887             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15888         }
15889         
15890         if(this.removable && !this.multiple){
15891             var close = this.closeTriggerEl();
15892             if(close){
15893                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15894                 close.on('click', this.removeBtnClick, this, close);
15895             }
15896         }
15897         /*
15898          * fix the bug in Safari iOS8
15899          */
15900         this.inputEl().on("focus", function(e){
15901             document.activeElement.blur();
15902         }, this);
15903         
15904         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15905         
15906         return;
15907         
15908         
15909     },
15910     
15911     renderTouchView : function()
15912     {
15913         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15914         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15915         
15916         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15917         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15918         
15919         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15920         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15921         this.touchViewBodyEl.setStyle('overflow', 'auto');
15922         
15923         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15924         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15925         
15926         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15927         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15928         
15929     },
15930     
15931     showTouchView : function()
15932     {
15933         if(this.disabled){
15934             return;
15935         }
15936         
15937         this.touchViewHeaderEl.hide();
15938
15939         if(this.modalTitle.length){
15940             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15941             this.touchViewHeaderEl.show();
15942         }
15943
15944         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15945         this.touchViewEl.show();
15946
15947         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15948         
15949         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15950         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15951
15952         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15953
15954         if(this.modalTitle.length){
15955             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15956         }
15957         
15958         this.touchViewBodyEl.setHeight(bodyHeight);
15959
15960         if(this.animate){
15961             var _this = this;
15962             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15963         }else{
15964             this.touchViewEl.addClass('in');
15965         }
15966         
15967         if(this._touchViewMask){
15968             Roo.get(document.body).addClass("x-body-masked");
15969             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15970             this._touchViewMask.setStyle('z-index', 10000);
15971             this._touchViewMask.addClass('show');
15972         }
15973         
15974         this.doTouchViewQuery();
15975         
15976     },
15977     
15978     hideTouchView : function()
15979     {
15980         this.touchViewEl.removeClass('in');
15981
15982         if(this.animate){
15983             var _this = this;
15984             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15985         }else{
15986             this.touchViewEl.setStyle('display', 'none');
15987         }
15988         
15989         if(this._touchViewMask){
15990             this._touchViewMask.removeClass('show');
15991             Roo.get(document.body).removeClass("x-body-masked");
15992         }
15993     },
15994     
15995     setTouchViewValue : function()
15996     {
15997         if(this.multiple){
15998             this.clearItem();
15999         
16000             var _this = this;
16001
16002             Roo.each(this.tickItems, function(o){
16003                 this.addItem(o);
16004             }, this);
16005         }
16006         
16007         this.hideTouchView();
16008     },
16009     
16010     doTouchViewQuery : function()
16011     {
16012         var qe = {
16013             query: '',
16014             forceAll: true,
16015             combo: this,
16016             cancel:false
16017         };
16018         
16019         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16020             return false;
16021         }
16022         
16023         if(!this.alwaysQuery || this.mode == 'local'){
16024             this.onTouchViewLoad();
16025             return;
16026         }
16027         
16028         this.store.load();
16029     },
16030     
16031     onTouchViewBeforeLoad : function(combo,opts)
16032     {
16033         return;
16034     },
16035
16036     // private
16037     onTouchViewLoad : function()
16038     {
16039         if(this.store.getCount() < 1){
16040             this.onTouchViewEmptyResults();
16041             return;
16042         }
16043         
16044         this.clearTouchView();
16045         
16046         var rawValue = this.getRawValue();
16047         
16048         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16049         
16050         this.tickItems = [];
16051         
16052         this.store.data.each(function(d, rowIndex){
16053             var row = this.touchViewListGroup.createChild(template);
16054             
16055             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16056                 row.addClass(d.data.cls);
16057             }
16058             
16059             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16060                 var cfg = {
16061                     data : d.data,
16062                     html : d.data[this.displayField]
16063                 };
16064                 
16065                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16066                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16067                 }
16068             }
16069             row.removeClass('selected');
16070             if(!this.multiple && this.valueField &&
16071                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16072             {
16073                 // radio buttons..
16074                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16075                 row.addClass('selected');
16076             }
16077             
16078             if(this.multiple && this.valueField &&
16079                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16080             {
16081                 
16082                 // checkboxes...
16083                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16084                 this.tickItems.push(d.data);
16085             }
16086             
16087             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16088             
16089         }, this);
16090         
16091         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16092         
16093         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16094
16095         if(this.modalTitle.length){
16096             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16097         }
16098
16099         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16100         
16101         if(this.mobile_restrict_height && listHeight < bodyHeight){
16102             this.touchViewBodyEl.setHeight(listHeight);
16103         }
16104         
16105         var _this = this;
16106         
16107         if(firstChecked && listHeight > bodyHeight){
16108             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16109         }
16110         
16111     },
16112     
16113     onTouchViewLoadException : function()
16114     {
16115         this.hideTouchView();
16116     },
16117     
16118     onTouchViewEmptyResults : function()
16119     {
16120         this.clearTouchView();
16121         
16122         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16123         
16124         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16125         
16126     },
16127     
16128     clearTouchView : function()
16129     {
16130         this.touchViewListGroup.dom.innerHTML = '';
16131     },
16132     
16133     onTouchViewClick : function(e, el, o)
16134     {
16135         e.preventDefault();
16136         
16137         var row = o.row;
16138         var rowIndex = o.rowIndex;
16139         
16140         var r = this.store.getAt(rowIndex);
16141         
16142         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16143             
16144             if(!this.multiple){
16145                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16146                     c.dom.removeAttribute('checked');
16147                 }, this);
16148
16149                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16150
16151                 this.setFromData(r.data);
16152
16153                 var close = this.closeTriggerEl();
16154
16155                 if(close){
16156                     close.show();
16157                 }
16158
16159                 this.hideTouchView();
16160
16161                 this.fireEvent('select', this, r, rowIndex);
16162
16163                 return;
16164             }
16165
16166             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16167                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16168                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16169                 return;
16170             }
16171
16172             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16173             this.addItem(r.data);
16174             this.tickItems.push(r.data);
16175         }
16176     },
16177     
16178     getAutoCreateNativeIOS : function()
16179     {
16180         var cfg = {
16181             cls: 'form-group' //input-group,
16182         };
16183         
16184         var combobox =  {
16185             tag: 'select',
16186             cls : 'roo-ios-select'
16187         };
16188         
16189         if (this.name) {
16190             combobox.name = this.name;
16191         }
16192         
16193         if (this.disabled) {
16194             combobox.disabled = true;
16195         }
16196         
16197         var settings = this;
16198         
16199         ['xs','sm','md','lg'].map(function(size){
16200             if (settings[size]) {
16201                 cfg.cls += ' col-' + size + '-' + settings[size];
16202             }
16203         });
16204         
16205         cfg.cn = combobox;
16206         
16207         return cfg;
16208         
16209     },
16210     
16211     initIOSView : function()
16212     {
16213         this.store.on('load', this.onIOSViewLoad, this);
16214         
16215         return;
16216     },
16217     
16218     onIOSViewLoad : function()
16219     {
16220         if(this.store.getCount() < 1){
16221             return;
16222         }
16223         
16224         this.clearIOSView();
16225         
16226         if(this.allowBlank) {
16227             
16228             var default_text = '-- SELECT --';
16229             
16230             if(this.placeholder.length){
16231                 default_text = this.placeholder;
16232             }
16233             
16234             if(this.emptyTitle.length){
16235                 default_text += ' - ' + this.emptyTitle + ' -';
16236             }
16237             
16238             var opt = this.inputEl().createChild({
16239                 tag: 'option',
16240                 value : 0,
16241                 html : default_text
16242             });
16243             
16244             var o = {};
16245             o[this.valueField] = 0;
16246             o[this.displayField] = default_text;
16247             
16248             this.ios_options.push({
16249                 data : o,
16250                 el : opt
16251             });
16252             
16253         }
16254         
16255         this.store.data.each(function(d, rowIndex){
16256             
16257             var html = '';
16258             
16259             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16260                 html = d.data[this.displayField];
16261             }
16262             
16263             var value = '';
16264             
16265             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16266                 value = d.data[this.valueField];
16267             }
16268             
16269             var option = {
16270                 tag: 'option',
16271                 value : value,
16272                 html : html
16273             };
16274             
16275             if(this.value == d.data[this.valueField]){
16276                 option['selected'] = true;
16277             }
16278             
16279             var opt = this.inputEl().createChild(option);
16280             
16281             this.ios_options.push({
16282                 data : d.data,
16283                 el : opt
16284             });
16285             
16286         }, this);
16287         
16288         this.inputEl().on('change', function(){
16289            this.fireEvent('select', this);
16290         }, this);
16291         
16292     },
16293     
16294     clearIOSView: function()
16295     {
16296         this.inputEl().dom.innerHTML = '';
16297         
16298         this.ios_options = [];
16299     },
16300     
16301     setIOSValue: function(v)
16302     {
16303         this.value = v;
16304         
16305         if(!this.ios_options){
16306             return;
16307         }
16308         
16309         Roo.each(this.ios_options, function(opts){
16310            
16311            opts.el.dom.removeAttribute('selected');
16312            
16313            if(opts.data[this.valueField] != v){
16314                return;
16315            }
16316            
16317            opts.el.dom.setAttribute('selected', true);
16318            
16319         }, this);
16320     }
16321
16322     /** 
16323     * @cfg {Boolean} grow 
16324     * @hide 
16325     */
16326     /** 
16327     * @cfg {Number} growMin 
16328     * @hide 
16329     */
16330     /** 
16331     * @cfg {Number} growMax 
16332     * @hide 
16333     */
16334     /**
16335      * @hide
16336      * @method autoSize
16337      */
16338 });
16339
16340 Roo.apply(Roo.bootstrap.ComboBox,  {
16341     
16342     header : {
16343         tag: 'div',
16344         cls: 'modal-header',
16345         cn: [
16346             {
16347                 tag: 'h4',
16348                 cls: 'modal-title'
16349             }
16350         ]
16351     },
16352     
16353     body : {
16354         tag: 'div',
16355         cls: 'modal-body',
16356         cn: [
16357             {
16358                 tag: 'ul',
16359                 cls: 'list-group'
16360             }
16361         ]
16362     },
16363     
16364     listItemRadio : {
16365         tag: 'li',
16366         cls: 'list-group-item',
16367         cn: [
16368             {
16369                 tag: 'span',
16370                 cls: 'roo-combobox-list-group-item-value'
16371             },
16372             {
16373                 tag: 'div',
16374                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16375                 cn: [
16376                     {
16377                         tag: 'input',
16378                         type: 'radio'
16379                     },
16380                     {
16381                         tag: 'label'
16382                     }
16383                 ]
16384             }
16385         ]
16386     },
16387     
16388     listItemCheckbox : {
16389         tag: 'li',
16390         cls: 'list-group-item',
16391         cn: [
16392             {
16393                 tag: 'span',
16394                 cls: 'roo-combobox-list-group-item-value'
16395             },
16396             {
16397                 tag: 'div',
16398                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16399                 cn: [
16400                     {
16401                         tag: 'input',
16402                         type: 'checkbox'
16403                     },
16404                     {
16405                         tag: 'label'
16406                     }
16407                 ]
16408             }
16409         ]
16410     },
16411     
16412     emptyResult : {
16413         tag: 'div',
16414         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16415     },
16416     
16417     footer : {
16418         tag: 'div',
16419         cls: 'modal-footer',
16420         cn: [
16421             {
16422                 tag: 'div',
16423                 cls: 'row',
16424                 cn: [
16425                     {
16426                         tag: 'div',
16427                         cls: 'col-xs-6 text-left',
16428                         cn: {
16429                             tag: 'button',
16430                             cls: 'btn btn-danger roo-touch-view-cancel',
16431                             html: 'Cancel'
16432                         }
16433                     },
16434                     {
16435                         tag: 'div',
16436                         cls: 'col-xs-6 text-right',
16437                         cn: {
16438                             tag: 'button',
16439                             cls: 'btn btn-success roo-touch-view-ok',
16440                             html: 'OK'
16441                         }
16442                     }
16443                 ]
16444             }
16445         ]
16446         
16447     }
16448 });
16449
16450 Roo.apply(Roo.bootstrap.ComboBox,  {
16451     
16452     touchViewTemplate : {
16453         tag: 'div',
16454         cls: 'modal fade roo-combobox-touch-view',
16455         cn: [
16456             {
16457                 tag: 'div',
16458                 cls: 'modal-dialog',
16459                 style : 'position:fixed', // we have to fix position....
16460                 cn: [
16461                     {
16462                         tag: 'div',
16463                         cls: 'modal-content',
16464                         cn: [
16465                             Roo.bootstrap.ComboBox.header,
16466                             Roo.bootstrap.ComboBox.body,
16467                             Roo.bootstrap.ComboBox.footer
16468                         ]
16469                     }
16470                 ]
16471             }
16472         ]
16473     }
16474 });/*
16475  * Based on:
16476  * Ext JS Library 1.1.1
16477  * Copyright(c) 2006-2007, Ext JS, LLC.
16478  *
16479  * Originally Released Under LGPL - original licence link has changed is not relivant.
16480  *
16481  * Fork - LGPL
16482  * <script type="text/javascript">
16483  */
16484
16485 /**
16486  * @class Roo.View
16487  * @extends Roo.util.Observable
16488  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16489  * This class also supports single and multi selection modes. <br>
16490  * Create a data model bound view:
16491  <pre><code>
16492  var store = new Roo.data.Store(...);
16493
16494  var view = new Roo.View({
16495     el : "my-element",
16496     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16497  
16498     singleSelect: true,
16499     selectedClass: "ydataview-selected",
16500     store: store
16501  });
16502
16503  // listen for node click?
16504  view.on("click", function(vw, index, node, e){
16505  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16506  });
16507
16508  // load XML data
16509  dataModel.load("foobar.xml");
16510  </code></pre>
16511  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16512  * <br><br>
16513  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16514  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16515  * 
16516  * Note: old style constructor is still suported (container, template, config)
16517  * 
16518  * @constructor
16519  * Create a new View
16520  * @param {Object} config The config object
16521  * 
16522  */
16523 Roo.View = function(config, depreciated_tpl, depreciated_config){
16524     
16525     this.parent = false;
16526     
16527     if (typeof(depreciated_tpl) == 'undefined') {
16528         // new way.. - universal constructor.
16529         Roo.apply(this, config);
16530         this.el  = Roo.get(this.el);
16531     } else {
16532         // old format..
16533         this.el  = Roo.get(config);
16534         this.tpl = depreciated_tpl;
16535         Roo.apply(this, depreciated_config);
16536     }
16537     this.wrapEl  = this.el.wrap().wrap();
16538     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16539     
16540     
16541     if(typeof(this.tpl) == "string"){
16542         this.tpl = new Roo.Template(this.tpl);
16543     } else {
16544         // support xtype ctors..
16545         this.tpl = new Roo.factory(this.tpl, Roo);
16546     }
16547     
16548     
16549     this.tpl.compile();
16550     
16551     /** @private */
16552     this.addEvents({
16553         /**
16554          * @event beforeclick
16555          * Fires before a click is processed. Returns false to cancel the default action.
16556          * @param {Roo.View} this
16557          * @param {Number} index The index of the target node
16558          * @param {HTMLElement} node The target node
16559          * @param {Roo.EventObject} e The raw event object
16560          */
16561             "beforeclick" : true,
16562         /**
16563          * @event click
16564          * Fires when a template node is clicked.
16565          * @param {Roo.View} this
16566          * @param {Number} index The index of the target node
16567          * @param {HTMLElement} node The target node
16568          * @param {Roo.EventObject} e The raw event object
16569          */
16570             "click" : true,
16571         /**
16572          * @event dblclick
16573          * Fires when a template node is double clicked.
16574          * @param {Roo.View} this
16575          * @param {Number} index The index of the target node
16576          * @param {HTMLElement} node The target node
16577          * @param {Roo.EventObject} e The raw event object
16578          */
16579             "dblclick" : true,
16580         /**
16581          * @event contextmenu
16582          * Fires when a template node is right clicked.
16583          * @param {Roo.View} this
16584          * @param {Number} index The index of the target node
16585          * @param {HTMLElement} node The target node
16586          * @param {Roo.EventObject} e The raw event object
16587          */
16588             "contextmenu" : true,
16589         /**
16590          * @event selectionchange
16591          * Fires when the selected nodes change.
16592          * @param {Roo.View} this
16593          * @param {Array} selections Array of the selected nodes
16594          */
16595             "selectionchange" : true,
16596     
16597         /**
16598          * @event beforeselect
16599          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16600          * @param {Roo.View} this
16601          * @param {HTMLElement} node The node to be selected
16602          * @param {Array} selections Array of currently selected nodes
16603          */
16604             "beforeselect" : true,
16605         /**
16606          * @event preparedata
16607          * Fires on every row to render, to allow you to change the data.
16608          * @param {Roo.View} this
16609          * @param {Object} data to be rendered (change this)
16610          */
16611           "preparedata" : true
16612           
16613           
16614         });
16615
16616
16617
16618     this.el.on({
16619         "click": this.onClick,
16620         "dblclick": this.onDblClick,
16621         "contextmenu": this.onContextMenu,
16622         scope:this
16623     });
16624
16625     this.selections = [];
16626     this.nodes = [];
16627     this.cmp = new Roo.CompositeElementLite([]);
16628     if(this.store){
16629         this.store = Roo.factory(this.store, Roo.data);
16630         this.setStore(this.store, true);
16631     }
16632     
16633     if ( this.footer && this.footer.xtype) {
16634            
16635          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16636         
16637         this.footer.dataSource = this.store;
16638         this.footer.container = fctr;
16639         this.footer = Roo.factory(this.footer, Roo);
16640         fctr.insertFirst(this.el);
16641         
16642         // this is a bit insane - as the paging toolbar seems to detach the el..
16643 //        dom.parentNode.parentNode.parentNode
16644          // they get detached?
16645     }
16646     
16647     
16648     Roo.View.superclass.constructor.call(this);
16649     
16650     
16651 };
16652
16653 Roo.extend(Roo.View, Roo.util.Observable, {
16654     
16655      /**
16656      * @cfg {Roo.data.Store} store Data store to load data from.
16657      */
16658     store : false,
16659     
16660     /**
16661      * @cfg {String|Roo.Element} el The container element.
16662      */
16663     el : '',
16664     
16665     /**
16666      * @cfg {String|Roo.Template} tpl The template used by this View 
16667      */
16668     tpl : false,
16669     /**
16670      * @cfg {String} dataName the named area of the template to use as the data area
16671      *                          Works with domtemplates roo-name="name"
16672      */
16673     dataName: false,
16674     /**
16675      * @cfg {String} selectedClass The css class to add to selected nodes
16676      */
16677     selectedClass : "x-view-selected",
16678      /**
16679      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16680      */
16681     emptyText : "",
16682     
16683     /**
16684      * @cfg {String} text to display on mask (default Loading)
16685      */
16686     mask : false,
16687     /**
16688      * @cfg {Boolean} multiSelect Allow multiple selection
16689      */
16690     multiSelect : false,
16691     /**
16692      * @cfg {Boolean} singleSelect Allow single selection
16693      */
16694     singleSelect:  false,
16695     
16696     /**
16697      * @cfg {Boolean} toggleSelect - selecting 
16698      */
16699     toggleSelect : false,
16700     
16701     /**
16702      * @cfg {Boolean} tickable - selecting 
16703      */
16704     tickable : false,
16705     
16706     /**
16707      * Returns the element this view is bound to.
16708      * @return {Roo.Element}
16709      */
16710     getEl : function(){
16711         return this.wrapEl;
16712     },
16713     
16714     
16715
16716     /**
16717      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16718      */
16719     refresh : function(){
16720         //Roo.log('refresh');
16721         var t = this.tpl;
16722         
16723         // if we are using something like 'domtemplate', then
16724         // the what gets used is:
16725         // t.applySubtemplate(NAME, data, wrapping data..)
16726         // the outer template then get' applied with
16727         //     the store 'extra data'
16728         // and the body get's added to the
16729         //      roo-name="data" node?
16730         //      <span class='roo-tpl-{name}'></span> ?????
16731         
16732         
16733         
16734         this.clearSelections();
16735         this.el.update("");
16736         var html = [];
16737         var records = this.store.getRange();
16738         if(records.length < 1) {
16739             
16740             // is this valid??  = should it render a template??
16741             
16742             this.el.update(this.emptyText);
16743             return;
16744         }
16745         var el = this.el;
16746         if (this.dataName) {
16747             this.el.update(t.apply(this.store.meta)); //????
16748             el = this.el.child('.roo-tpl-' + this.dataName);
16749         }
16750         
16751         for(var i = 0, len = records.length; i < len; i++){
16752             var data = this.prepareData(records[i].data, i, records[i]);
16753             this.fireEvent("preparedata", this, data, i, records[i]);
16754             
16755             var d = Roo.apply({}, data);
16756             
16757             if(this.tickable){
16758                 Roo.apply(d, {'roo-id' : Roo.id()});
16759                 
16760                 var _this = this;
16761             
16762                 Roo.each(this.parent.item, function(item){
16763                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16764                         return;
16765                     }
16766                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16767                 });
16768             }
16769             
16770             html[html.length] = Roo.util.Format.trim(
16771                 this.dataName ?
16772                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16773                     t.apply(d)
16774             );
16775         }
16776         
16777         
16778         
16779         el.update(html.join(""));
16780         this.nodes = el.dom.childNodes;
16781         this.updateIndexes(0);
16782     },
16783     
16784
16785     /**
16786      * Function to override to reformat the data that is sent to
16787      * the template for each node.
16788      * DEPRICATED - use the preparedata event handler.
16789      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16790      * a JSON object for an UpdateManager bound view).
16791      */
16792     prepareData : function(data, index, record)
16793     {
16794         this.fireEvent("preparedata", this, data, index, record);
16795         return data;
16796     },
16797
16798     onUpdate : function(ds, record){
16799         // Roo.log('on update');   
16800         this.clearSelections();
16801         var index = this.store.indexOf(record);
16802         var n = this.nodes[index];
16803         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16804         n.parentNode.removeChild(n);
16805         this.updateIndexes(index, index);
16806     },
16807
16808     
16809     
16810 // --------- FIXME     
16811     onAdd : function(ds, records, index)
16812     {
16813         //Roo.log(['on Add', ds, records, index] );        
16814         this.clearSelections();
16815         if(this.nodes.length == 0){
16816             this.refresh();
16817             return;
16818         }
16819         var n = this.nodes[index];
16820         for(var i = 0, len = records.length; i < len; i++){
16821             var d = this.prepareData(records[i].data, i, records[i]);
16822             if(n){
16823                 this.tpl.insertBefore(n, d);
16824             }else{
16825                 
16826                 this.tpl.append(this.el, d);
16827             }
16828         }
16829         this.updateIndexes(index);
16830     },
16831
16832     onRemove : function(ds, record, index){
16833        // Roo.log('onRemove');
16834         this.clearSelections();
16835         var el = this.dataName  ?
16836             this.el.child('.roo-tpl-' + this.dataName) :
16837             this.el; 
16838         
16839         el.dom.removeChild(this.nodes[index]);
16840         this.updateIndexes(index);
16841     },
16842
16843     /**
16844      * Refresh an individual node.
16845      * @param {Number} index
16846      */
16847     refreshNode : function(index){
16848         this.onUpdate(this.store, this.store.getAt(index));
16849     },
16850
16851     updateIndexes : function(startIndex, endIndex){
16852         var ns = this.nodes;
16853         startIndex = startIndex || 0;
16854         endIndex = endIndex || ns.length - 1;
16855         for(var i = startIndex; i <= endIndex; i++){
16856             ns[i].nodeIndex = i;
16857         }
16858     },
16859
16860     /**
16861      * Changes the data store this view uses and refresh the view.
16862      * @param {Store} store
16863      */
16864     setStore : function(store, initial){
16865         if(!initial && this.store){
16866             this.store.un("datachanged", this.refresh);
16867             this.store.un("add", this.onAdd);
16868             this.store.un("remove", this.onRemove);
16869             this.store.un("update", this.onUpdate);
16870             this.store.un("clear", this.refresh);
16871             this.store.un("beforeload", this.onBeforeLoad);
16872             this.store.un("load", this.onLoad);
16873             this.store.un("loadexception", this.onLoad);
16874         }
16875         if(store){
16876           
16877             store.on("datachanged", this.refresh, this);
16878             store.on("add", this.onAdd, this);
16879             store.on("remove", this.onRemove, this);
16880             store.on("update", this.onUpdate, this);
16881             store.on("clear", this.refresh, this);
16882             store.on("beforeload", this.onBeforeLoad, this);
16883             store.on("load", this.onLoad, this);
16884             store.on("loadexception", this.onLoad, this);
16885         }
16886         
16887         if(store){
16888             this.refresh();
16889         }
16890     },
16891     /**
16892      * onbeforeLoad - masks the loading area.
16893      *
16894      */
16895     onBeforeLoad : function(store,opts)
16896     {
16897          //Roo.log('onBeforeLoad');   
16898         if (!opts.add) {
16899             this.el.update("");
16900         }
16901         this.el.mask(this.mask ? this.mask : "Loading" ); 
16902     },
16903     onLoad : function ()
16904     {
16905         this.el.unmask();
16906     },
16907     
16908
16909     /**
16910      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16911      * @param {HTMLElement} node
16912      * @return {HTMLElement} The template node
16913      */
16914     findItemFromChild : function(node){
16915         var el = this.dataName  ?
16916             this.el.child('.roo-tpl-' + this.dataName,true) :
16917             this.el.dom; 
16918         
16919         if(!node || node.parentNode == el){
16920                     return node;
16921             }
16922             var p = node.parentNode;
16923             while(p && p != el){
16924             if(p.parentNode == el){
16925                 return p;
16926             }
16927             p = p.parentNode;
16928         }
16929             return null;
16930     },
16931
16932     /** @ignore */
16933     onClick : function(e){
16934         var item = this.findItemFromChild(e.getTarget());
16935         if(item){
16936             var index = this.indexOf(item);
16937             if(this.onItemClick(item, index, e) !== false){
16938                 this.fireEvent("click", this, index, item, e);
16939             }
16940         }else{
16941             this.clearSelections();
16942         }
16943     },
16944
16945     /** @ignore */
16946     onContextMenu : function(e){
16947         var item = this.findItemFromChild(e.getTarget());
16948         if(item){
16949             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16950         }
16951     },
16952
16953     /** @ignore */
16954     onDblClick : function(e){
16955         var item = this.findItemFromChild(e.getTarget());
16956         if(item){
16957             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16958         }
16959     },
16960
16961     onItemClick : function(item, index, e)
16962     {
16963         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16964             return false;
16965         }
16966         if (this.toggleSelect) {
16967             var m = this.isSelected(item) ? 'unselect' : 'select';
16968             //Roo.log(m);
16969             var _t = this;
16970             _t[m](item, true, false);
16971             return true;
16972         }
16973         if(this.multiSelect || this.singleSelect){
16974             if(this.multiSelect && e.shiftKey && this.lastSelection){
16975                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16976             }else{
16977                 this.select(item, this.multiSelect && e.ctrlKey);
16978                 this.lastSelection = item;
16979             }
16980             
16981             if(!this.tickable){
16982                 e.preventDefault();
16983             }
16984             
16985         }
16986         return true;
16987     },
16988
16989     /**
16990      * Get the number of selected nodes.
16991      * @return {Number}
16992      */
16993     getSelectionCount : function(){
16994         return this.selections.length;
16995     },
16996
16997     /**
16998      * Get the currently selected nodes.
16999      * @return {Array} An array of HTMLElements
17000      */
17001     getSelectedNodes : function(){
17002         return this.selections;
17003     },
17004
17005     /**
17006      * Get the indexes of the selected nodes.
17007      * @return {Array}
17008      */
17009     getSelectedIndexes : function(){
17010         var indexes = [], s = this.selections;
17011         for(var i = 0, len = s.length; i < len; i++){
17012             indexes.push(s[i].nodeIndex);
17013         }
17014         return indexes;
17015     },
17016
17017     /**
17018      * Clear all selections
17019      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17020      */
17021     clearSelections : function(suppressEvent){
17022         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17023             this.cmp.elements = this.selections;
17024             this.cmp.removeClass(this.selectedClass);
17025             this.selections = [];
17026             if(!suppressEvent){
17027                 this.fireEvent("selectionchange", this, this.selections);
17028             }
17029         }
17030     },
17031
17032     /**
17033      * Returns true if the passed node is selected
17034      * @param {HTMLElement/Number} node The node or node index
17035      * @return {Boolean}
17036      */
17037     isSelected : function(node){
17038         var s = this.selections;
17039         if(s.length < 1){
17040             return false;
17041         }
17042         node = this.getNode(node);
17043         return s.indexOf(node) !== -1;
17044     },
17045
17046     /**
17047      * Selects nodes.
17048      * @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
17049      * @param {Boolean} keepExisting (optional) true to keep existing selections
17050      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17051      */
17052     select : function(nodeInfo, keepExisting, suppressEvent){
17053         if(nodeInfo instanceof Array){
17054             if(!keepExisting){
17055                 this.clearSelections(true);
17056             }
17057             for(var i = 0, len = nodeInfo.length; i < len; i++){
17058                 this.select(nodeInfo[i], true, true);
17059             }
17060             return;
17061         } 
17062         var node = this.getNode(nodeInfo);
17063         if(!node || this.isSelected(node)){
17064             return; // already selected.
17065         }
17066         if(!keepExisting){
17067             this.clearSelections(true);
17068         }
17069         
17070         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17071             Roo.fly(node).addClass(this.selectedClass);
17072             this.selections.push(node);
17073             if(!suppressEvent){
17074                 this.fireEvent("selectionchange", this, this.selections);
17075             }
17076         }
17077         
17078         
17079     },
17080       /**
17081      * Unselects nodes.
17082      * @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
17083      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17084      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17085      */
17086     unselect : function(nodeInfo, keepExisting, suppressEvent)
17087     {
17088         if(nodeInfo instanceof Array){
17089             Roo.each(this.selections, function(s) {
17090                 this.unselect(s, nodeInfo);
17091             }, this);
17092             return;
17093         }
17094         var node = this.getNode(nodeInfo);
17095         if(!node || !this.isSelected(node)){
17096             //Roo.log("not selected");
17097             return; // not selected.
17098         }
17099         // fireevent???
17100         var ns = [];
17101         Roo.each(this.selections, function(s) {
17102             if (s == node ) {
17103                 Roo.fly(node).removeClass(this.selectedClass);
17104
17105                 return;
17106             }
17107             ns.push(s);
17108         },this);
17109         
17110         this.selections= ns;
17111         this.fireEvent("selectionchange", this, this.selections);
17112     },
17113
17114     /**
17115      * Gets a template node.
17116      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17117      * @return {HTMLElement} The node or null if it wasn't found
17118      */
17119     getNode : function(nodeInfo){
17120         if(typeof nodeInfo == "string"){
17121             return document.getElementById(nodeInfo);
17122         }else if(typeof nodeInfo == "number"){
17123             return this.nodes[nodeInfo];
17124         }
17125         return nodeInfo;
17126     },
17127
17128     /**
17129      * Gets a range template nodes.
17130      * @param {Number} startIndex
17131      * @param {Number} endIndex
17132      * @return {Array} An array of nodes
17133      */
17134     getNodes : function(start, end){
17135         var ns = this.nodes;
17136         start = start || 0;
17137         end = typeof end == "undefined" ? ns.length - 1 : end;
17138         var nodes = [];
17139         if(start <= end){
17140             for(var i = start; i <= end; i++){
17141                 nodes.push(ns[i]);
17142             }
17143         } else{
17144             for(var i = start; i >= end; i--){
17145                 nodes.push(ns[i]);
17146             }
17147         }
17148         return nodes;
17149     },
17150
17151     /**
17152      * Finds the index of the passed node
17153      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17154      * @return {Number} The index of the node or -1
17155      */
17156     indexOf : function(node){
17157         node = this.getNode(node);
17158         if(typeof node.nodeIndex == "number"){
17159             return node.nodeIndex;
17160         }
17161         var ns = this.nodes;
17162         for(var i = 0, len = ns.length; i < len; i++){
17163             if(ns[i] == node){
17164                 return i;
17165             }
17166         }
17167         return -1;
17168     }
17169 });
17170 /*
17171  * - LGPL
17172  *
17173  * based on jquery fullcalendar
17174  * 
17175  */
17176
17177 Roo.bootstrap = Roo.bootstrap || {};
17178 /**
17179  * @class Roo.bootstrap.Calendar
17180  * @extends Roo.bootstrap.Component
17181  * Bootstrap Calendar class
17182  * @cfg {Boolean} loadMask (true|false) default false
17183  * @cfg {Object} header generate the user specific header of the calendar, default false
17184
17185  * @constructor
17186  * Create a new Container
17187  * @param {Object} config The config object
17188  */
17189
17190
17191
17192 Roo.bootstrap.Calendar = function(config){
17193     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17194      this.addEvents({
17195         /**
17196              * @event select
17197              * Fires when a date is selected
17198              * @param {DatePicker} this
17199              * @param {Date} date The selected date
17200              */
17201         'select': true,
17202         /**
17203              * @event monthchange
17204              * Fires when the displayed month changes 
17205              * @param {DatePicker} this
17206              * @param {Date} date The selected month
17207              */
17208         'monthchange': true,
17209         /**
17210              * @event evententer
17211              * Fires when mouse over an event
17212              * @param {Calendar} this
17213              * @param {event} Event
17214              */
17215         'evententer': true,
17216         /**
17217              * @event eventleave
17218              * Fires when the mouse leaves an
17219              * @param {Calendar} this
17220              * @param {event}
17221              */
17222         'eventleave': true,
17223         /**
17224              * @event eventclick
17225              * Fires when the mouse click an
17226              * @param {Calendar} this
17227              * @param {event}
17228              */
17229         'eventclick': true
17230         
17231     });
17232
17233 };
17234
17235 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17236     
17237      /**
17238      * @cfg {Number} startDay
17239      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17240      */
17241     startDay : 0,
17242     
17243     loadMask : false,
17244     
17245     header : false,
17246       
17247     getAutoCreate : function(){
17248         
17249         
17250         var fc_button = function(name, corner, style, content ) {
17251             return Roo.apply({},{
17252                 tag : 'span',
17253                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17254                          (corner.length ?
17255                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17256                             ''
17257                         ),
17258                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17259                 unselectable: 'on'
17260             });
17261         };
17262         
17263         var header = {};
17264         
17265         if(!this.header){
17266             header = {
17267                 tag : 'table',
17268                 cls : 'fc-header',
17269                 style : 'width:100%',
17270                 cn : [
17271                     {
17272                         tag: 'tr',
17273                         cn : [
17274                             {
17275                                 tag : 'td',
17276                                 cls : 'fc-header-left',
17277                                 cn : [
17278                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17279                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17280                                     { tag: 'span', cls: 'fc-header-space' },
17281                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17282
17283
17284                                 ]
17285                             },
17286
17287                             {
17288                                 tag : 'td',
17289                                 cls : 'fc-header-center',
17290                                 cn : [
17291                                     {
17292                                         tag: 'span',
17293                                         cls: 'fc-header-title',
17294                                         cn : {
17295                                             tag: 'H2',
17296                                             html : 'month / year'
17297                                         }
17298                                     }
17299
17300                                 ]
17301                             },
17302                             {
17303                                 tag : 'td',
17304                                 cls : 'fc-header-right',
17305                                 cn : [
17306                               /*      fc_button('month', 'left', '', 'month' ),
17307                                     fc_button('week', '', '', 'week' ),
17308                                     fc_button('day', 'right', '', 'day' )
17309                                 */    
17310
17311                                 ]
17312                             }
17313
17314                         ]
17315                     }
17316                 ]
17317             };
17318         }
17319         
17320         header = this.header;
17321         
17322        
17323         var cal_heads = function() {
17324             var ret = [];
17325             // fixme - handle this.
17326             
17327             for (var i =0; i < Date.dayNames.length; i++) {
17328                 var d = Date.dayNames[i];
17329                 ret.push({
17330                     tag: 'th',
17331                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17332                     html : d.substring(0,3)
17333                 });
17334                 
17335             }
17336             ret[0].cls += ' fc-first';
17337             ret[6].cls += ' fc-last';
17338             return ret;
17339         };
17340         var cal_cell = function(n) {
17341             return  {
17342                 tag: 'td',
17343                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17344                 cn : [
17345                     {
17346                         cn : [
17347                             {
17348                                 cls: 'fc-day-number',
17349                                 html: 'D'
17350                             },
17351                             {
17352                                 cls: 'fc-day-content',
17353                              
17354                                 cn : [
17355                                      {
17356                                         style: 'position: relative;' // height: 17px;
17357                                     }
17358                                 ]
17359                             }
17360                             
17361                             
17362                         ]
17363                     }
17364                 ]
17365                 
17366             }
17367         };
17368         var cal_rows = function() {
17369             
17370             var ret = [];
17371             for (var r = 0; r < 6; r++) {
17372                 var row= {
17373                     tag : 'tr',
17374                     cls : 'fc-week',
17375                     cn : []
17376                 };
17377                 
17378                 for (var i =0; i < Date.dayNames.length; i++) {
17379                     var d = Date.dayNames[i];
17380                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17381
17382                 }
17383                 row.cn[0].cls+=' fc-first';
17384                 row.cn[0].cn[0].style = 'min-height:90px';
17385                 row.cn[6].cls+=' fc-last';
17386                 ret.push(row);
17387                 
17388             }
17389             ret[0].cls += ' fc-first';
17390             ret[4].cls += ' fc-prev-last';
17391             ret[5].cls += ' fc-last';
17392             return ret;
17393             
17394         };
17395         
17396         var cal_table = {
17397             tag: 'table',
17398             cls: 'fc-border-separate',
17399             style : 'width:100%',
17400             cellspacing  : 0,
17401             cn : [
17402                 { 
17403                     tag: 'thead',
17404                     cn : [
17405                         { 
17406                             tag: 'tr',
17407                             cls : 'fc-first fc-last',
17408                             cn : cal_heads()
17409                         }
17410                     ]
17411                 },
17412                 { 
17413                     tag: 'tbody',
17414                     cn : cal_rows()
17415                 }
17416                   
17417             ]
17418         };
17419          
17420          var cfg = {
17421             cls : 'fc fc-ltr',
17422             cn : [
17423                 header,
17424                 {
17425                     cls : 'fc-content',
17426                     style : "position: relative;",
17427                     cn : [
17428                         {
17429                             cls : 'fc-view fc-view-month fc-grid',
17430                             style : 'position: relative',
17431                             unselectable : 'on',
17432                             cn : [
17433                                 {
17434                                     cls : 'fc-event-container',
17435                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17436                                 },
17437                                 cal_table
17438                             ]
17439                         }
17440                     ]
17441     
17442                 }
17443            ] 
17444             
17445         };
17446         
17447          
17448         
17449         return cfg;
17450     },
17451     
17452     
17453     initEvents : function()
17454     {
17455         if(!this.store){
17456             throw "can not find store for calendar";
17457         }
17458         
17459         var mark = {
17460             tag: "div",
17461             cls:"x-dlg-mask",
17462             style: "text-align:center",
17463             cn: [
17464                 {
17465                     tag: "div",
17466                     style: "background-color:white;width:50%;margin:250 auto",
17467                     cn: [
17468                         {
17469                             tag: "img",
17470                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17471                         },
17472                         {
17473                             tag: "span",
17474                             html: "Loading"
17475                         }
17476                         
17477                     ]
17478                 }
17479             ]
17480         };
17481         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17482         
17483         var size = this.el.select('.fc-content', true).first().getSize();
17484         this.maskEl.setSize(size.width, size.height);
17485         this.maskEl.enableDisplayMode("block");
17486         if(!this.loadMask){
17487             this.maskEl.hide();
17488         }
17489         
17490         this.store = Roo.factory(this.store, Roo.data);
17491         this.store.on('load', this.onLoad, this);
17492         this.store.on('beforeload', this.onBeforeLoad, this);
17493         
17494         this.resize();
17495         
17496         this.cells = this.el.select('.fc-day',true);
17497         //Roo.log(this.cells);
17498         this.textNodes = this.el.query('.fc-day-number');
17499         this.cells.addClassOnOver('fc-state-hover');
17500         
17501         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17502         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17503         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17504         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17505         
17506         this.on('monthchange', this.onMonthChange, this);
17507         
17508         this.update(new Date().clearTime());
17509     },
17510     
17511     resize : function() {
17512         var sz  = this.el.getSize();
17513         
17514         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17515         this.el.select('.fc-day-content div',true).setHeight(34);
17516     },
17517     
17518     
17519     // private
17520     showPrevMonth : function(e){
17521         this.update(this.activeDate.add("mo", -1));
17522     },
17523     showToday : function(e){
17524         this.update(new Date().clearTime());
17525     },
17526     // private
17527     showNextMonth : function(e){
17528         this.update(this.activeDate.add("mo", 1));
17529     },
17530
17531     // private
17532     showPrevYear : function(){
17533         this.update(this.activeDate.add("y", -1));
17534     },
17535
17536     // private
17537     showNextYear : function(){
17538         this.update(this.activeDate.add("y", 1));
17539     },
17540
17541     
17542    // private
17543     update : function(date)
17544     {
17545         var vd = this.activeDate;
17546         this.activeDate = date;
17547 //        if(vd && this.el){
17548 //            var t = date.getTime();
17549 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17550 //                Roo.log('using add remove');
17551 //                
17552 //                this.fireEvent('monthchange', this, date);
17553 //                
17554 //                this.cells.removeClass("fc-state-highlight");
17555 //                this.cells.each(function(c){
17556 //                   if(c.dateValue == t){
17557 //                       c.addClass("fc-state-highlight");
17558 //                       setTimeout(function(){
17559 //                            try{c.dom.firstChild.focus();}catch(e){}
17560 //                       }, 50);
17561 //                       return false;
17562 //                   }
17563 //                   return true;
17564 //                });
17565 //                return;
17566 //            }
17567 //        }
17568         
17569         var days = date.getDaysInMonth();
17570         
17571         var firstOfMonth = date.getFirstDateOfMonth();
17572         var startingPos = firstOfMonth.getDay()-this.startDay;
17573         
17574         if(startingPos < this.startDay){
17575             startingPos += 7;
17576         }
17577         
17578         var pm = date.add(Date.MONTH, -1);
17579         var prevStart = pm.getDaysInMonth()-startingPos;
17580 //        
17581         this.cells = this.el.select('.fc-day',true);
17582         this.textNodes = this.el.query('.fc-day-number');
17583         this.cells.addClassOnOver('fc-state-hover');
17584         
17585         var cells = this.cells.elements;
17586         var textEls = this.textNodes;
17587         
17588         Roo.each(cells, function(cell){
17589             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17590         });
17591         
17592         days += startingPos;
17593
17594         // convert everything to numbers so it's fast
17595         var day = 86400000;
17596         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17597         //Roo.log(d);
17598         //Roo.log(pm);
17599         //Roo.log(prevStart);
17600         
17601         var today = new Date().clearTime().getTime();
17602         var sel = date.clearTime().getTime();
17603         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17604         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17605         var ddMatch = this.disabledDatesRE;
17606         var ddText = this.disabledDatesText;
17607         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17608         var ddaysText = this.disabledDaysText;
17609         var format = this.format;
17610         
17611         var setCellClass = function(cal, cell){
17612             cell.row = 0;
17613             cell.events = [];
17614             cell.more = [];
17615             //Roo.log('set Cell Class');
17616             cell.title = "";
17617             var t = d.getTime();
17618             
17619             //Roo.log(d);
17620             
17621             cell.dateValue = t;
17622             if(t == today){
17623                 cell.className += " fc-today";
17624                 cell.className += " fc-state-highlight";
17625                 cell.title = cal.todayText;
17626             }
17627             if(t == sel){
17628                 // disable highlight in other month..
17629                 //cell.className += " fc-state-highlight";
17630                 
17631             }
17632             // disabling
17633             if(t < min) {
17634                 cell.className = " fc-state-disabled";
17635                 cell.title = cal.minText;
17636                 return;
17637             }
17638             if(t > max) {
17639                 cell.className = " fc-state-disabled";
17640                 cell.title = cal.maxText;
17641                 return;
17642             }
17643             if(ddays){
17644                 if(ddays.indexOf(d.getDay()) != -1){
17645                     cell.title = ddaysText;
17646                     cell.className = " fc-state-disabled";
17647                 }
17648             }
17649             if(ddMatch && format){
17650                 var fvalue = d.dateFormat(format);
17651                 if(ddMatch.test(fvalue)){
17652                     cell.title = ddText.replace("%0", fvalue);
17653                     cell.className = " fc-state-disabled";
17654                 }
17655             }
17656             
17657             if (!cell.initialClassName) {
17658                 cell.initialClassName = cell.dom.className;
17659             }
17660             
17661             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17662         };
17663
17664         var i = 0;
17665         
17666         for(; i < startingPos; i++) {
17667             textEls[i].innerHTML = (++prevStart);
17668             d.setDate(d.getDate()+1);
17669             
17670             cells[i].className = "fc-past fc-other-month";
17671             setCellClass(this, cells[i]);
17672         }
17673         
17674         var intDay = 0;
17675         
17676         for(; i < days; i++){
17677             intDay = i - startingPos + 1;
17678             textEls[i].innerHTML = (intDay);
17679             d.setDate(d.getDate()+1);
17680             
17681             cells[i].className = ''; // "x-date-active";
17682             setCellClass(this, cells[i]);
17683         }
17684         var extraDays = 0;
17685         
17686         for(; i < 42; i++) {
17687             textEls[i].innerHTML = (++extraDays);
17688             d.setDate(d.getDate()+1);
17689             
17690             cells[i].className = "fc-future fc-other-month";
17691             setCellClass(this, cells[i]);
17692         }
17693         
17694         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17695         
17696         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17697         
17698         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17699         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17700         
17701         if(totalRows != 6){
17702             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17703             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17704         }
17705         
17706         this.fireEvent('monthchange', this, date);
17707         
17708         
17709         /*
17710         if(!this.internalRender){
17711             var main = this.el.dom.firstChild;
17712             var w = main.offsetWidth;
17713             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17714             Roo.fly(main).setWidth(w);
17715             this.internalRender = true;
17716             // opera does not respect the auto grow header center column
17717             // then, after it gets a width opera refuses to recalculate
17718             // without a second pass
17719             if(Roo.isOpera && !this.secondPass){
17720                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17721                 this.secondPass = true;
17722                 this.update.defer(10, this, [date]);
17723             }
17724         }
17725         */
17726         
17727     },
17728     
17729     findCell : function(dt) {
17730         dt = dt.clearTime().getTime();
17731         var ret = false;
17732         this.cells.each(function(c){
17733             //Roo.log("check " +c.dateValue + '?=' + dt);
17734             if(c.dateValue == dt){
17735                 ret = c;
17736                 return false;
17737             }
17738             return true;
17739         });
17740         
17741         return ret;
17742     },
17743     
17744     findCells : function(ev) {
17745         var s = ev.start.clone().clearTime().getTime();
17746        // Roo.log(s);
17747         var e= ev.end.clone().clearTime().getTime();
17748        // Roo.log(e);
17749         var ret = [];
17750         this.cells.each(function(c){
17751              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17752             
17753             if(c.dateValue > e){
17754                 return ;
17755             }
17756             if(c.dateValue < s){
17757                 return ;
17758             }
17759             ret.push(c);
17760         });
17761         
17762         return ret;    
17763     },
17764     
17765 //    findBestRow: function(cells)
17766 //    {
17767 //        var ret = 0;
17768 //        
17769 //        for (var i =0 ; i < cells.length;i++) {
17770 //            ret  = Math.max(cells[i].rows || 0,ret);
17771 //        }
17772 //        return ret;
17773 //        
17774 //    },
17775     
17776     
17777     addItem : function(ev)
17778     {
17779         // look for vertical location slot in
17780         var cells = this.findCells(ev);
17781         
17782 //        ev.row = this.findBestRow(cells);
17783         
17784         // work out the location.
17785         
17786         var crow = false;
17787         var rows = [];
17788         for(var i =0; i < cells.length; i++) {
17789             
17790             cells[i].row = cells[0].row;
17791             
17792             if(i == 0){
17793                 cells[i].row = cells[i].row + 1;
17794             }
17795             
17796             if (!crow) {
17797                 crow = {
17798                     start : cells[i],
17799                     end :  cells[i]
17800                 };
17801                 continue;
17802             }
17803             if (crow.start.getY() == cells[i].getY()) {
17804                 // on same row.
17805                 crow.end = cells[i];
17806                 continue;
17807             }
17808             // different row.
17809             rows.push(crow);
17810             crow = {
17811                 start: cells[i],
17812                 end : cells[i]
17813             };
17814             
17815         }
17816         
17817         rows.push(crow);
17818         ev.els = [];
17819         ev.rows = rows;
17820         ev.cells = cells;
17821         
17822         cells[0].events.push(ev);
17823         
17824         this.calevents.push(ev);
17825     },
17826     
17827     clearEvents: function() {
17828         
17829         if(!this.calevents){
17830             return;
17831         }
17832         
17833         Roo.each(this.cells.elements, function(c){
17834             c.row = 0;
17835             c.events = [];
17836             c.more = [];
17837         });
17838         
17839         Roo.each(this.calevents, function(e) {
17840             Roo.each(e.els, function(el) {
17841                 el.un('mouseenter' ,this.onEventEnter, this);
17842                 el.un('mouseleave' ,this.onEventLeave, this);
17843                 el.remove();
17844             },this);
17845         },this);
17846         
17847         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17848             e.remove();
17849         });
17850         
17851     },
17852     
17853     renderEvents: function()
17854     {   
17855         var _this = this;
17856         
17857         this.cells.each(function(c) {
17858             
17859             if(c.row < 5){
17860                 return;
17861             }
17862             
17863             var ev = c.events;
17864             
17865             var r = 4;
17866             if(c.row != c.events.length){
17867                 r = 4 - (4 - (c.row - c.events.length));
17868             }
17869             
17870             c.events = ev.slice(0, r);
17871             c.more = ev.slice(r);
17872             
17873             if(c.more.length && c.more.length == 1){
17874                 c.events.push(c.more.pop());
17875             }
17876             
17877             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17878             
17879         });
17880             
17881         this.cells.each(function(c) {
17882             
17883             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17884             
17885             
17886             for (var e = 0; e < c.events.length; e++){
17887                 var ev = c.events[e];
17888                 var rows = ev.rows;
17889                 
17890                 for(var i = 0; i < rows.length; i++) {
17891                 
17892                     // how many rows should it span..
17893
17894                     var  cfg = {
17895                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17896                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17897
17898                         unselectable : "on",
17899                         cn : [
17900                             {
17901                                 cls: 'fc-event-inner',
17902                                 cn : [
17903     //                                {
17904     //                                  tag:'span',
17905     //                                  cls: 'fc-event-time',
17906     //                                  html : cells.length > 1 ? '' : ev.time
17907     //                                },
17908                                     {
17909                                       tag:'span',
17910                                       cls: 'fc-event-title',
17911                                       html : String.format('{0}', ev.title)
17912                                     }
17913
17914
17915                                 ]
17916                             },
17917                             {
17918                                 cls: 'ui-resizable-handle ui-resizable-e',
17919                                 html : '&nbsp;&nbsp;&nbsp'
17920                             }
17921
17922                         ]
17923                     };
17924
17925                     if (i == 0) {
17926                         cfg.cls += ' fc-event-start';
17927                     }
17928                     if ((i+1) == rows.length) {
17929                         cfg.cls += ' fc-event-end';
17930                     }
17931
17932                     var ctr = _this.el.select('.fc-event-container',true).first();
17933                     var cg = ctr.createChild(cfg);
17934
17935                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17936                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17937
17938                     var r = (c.more.length) ? 1 : 0;
17939                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17940                     cg.setWidth(ebox.right - sbox.x -2);
17941
17942                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17943                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17944                     cg.on('click', _this.onEventClick, _this, ev);
17945
17946                     ev.els.push(cg);
17947                     
17948                 }
17949                 
17950             }
17951             
17952             
17953             if(c.more.length){
17954                 var  cfg = {
17955                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17956                     style : 'position: absolute',
17957                     unselectable : "on",
17958                     cn : [
17959                         {
17960                             cls: 'fc-event-inner',
17961                             cn : [
17962                                 {
17963                                   tag:'span',
17964                                   cls: 'fc-event-title',
17965                                   html : 'More'
17966                                 }
17967
17968
17969                             ]
17970                         },
17971                         {
17972                             cls: 'ui-resizable-handle ui-resizable-e',
17973                             html : '&nbsp;&nbsp;&nbsp'
17974                         }
17975
17976                     ]
17977                 };
17978
17979                 var ctr = _this.el.select('.fc-event-container',true).first();
17980                 var cg = ctr.createChild(cfg);
17981
17982                 var sbox = c.select('.fc-day-content',true).first().getBox();
17983                 var ebox = c.select('.fc-day-content',true).first().getBox();
17984                 //Roo.log(cg);
17985                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17986                 cg.setWidth(ebox.right - sbox.x -2);
17987
17988                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17989                 
17990             }
17991             
17992         });
17993         
17994         
17995         
17996     },
17997     
17998     onEventEnter: function (e, el,event,d) {
17999         this.fireEvent('evententer', this, el, event);
18000     },
18001     
18002     onEventLeave: function (e, el,event,d) {
18003         this.fireEvent('eventleave', this, el, event);
18004     },
18005     
18006     onEventClick: function (e, el,event,d) {
18007         this.fireEvent('eventclick', this, el, event);
18008     },
18009     
18010     onMonthChange: function () {
18011         this.store.load();
18012     },
18013     
18014     onMoreEventClick: function(e, el, more)
18015     {
18016         var _this = this;
18017         
18018         this.calpopover.placement = 'right';
18019         this.calpopover.setTitle('More');
18020         
18021         this.calpopover.setContent('');
18022         
18023         var ctr = this.calpopover.el.select('.popover-content', true).first();
18024         
18025         Roo.each(more, function(m){
18026             var cfg = {
18027                 cls : 'fc-event-hori fc-event-draggable',
18028                 html : m.title
18029             };
18030             var cg = ctr.createChild(cfg);
18031             
18032             cg.on('click', _this.onEventClick, _this, m);
18033         });
18034         
18035         this.calpopover.show(el);
18036         
18037         
18038     },
18039     
18040     onLoad: function () 
18041     {   
18042         this.calevents = [];
18043         var cal = this;
18044         
18045         if(this.store.getCount() > 0){
18046             this.store.data.each(function(d){
18047                cal.addItem({
18048                     id : d.data.id,
18049                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18050                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18051                     time : d.data.start_time,
18052                     title : d.data.title,
18053                     description : d.data.description,
18054                     venue : d.data.venue
18055                 });
18056             });
18057         }
18058         
18059         this.renderEvents();
18060         
18061         if(this.calevents.length && this.loadMask){
18062             this.maskEl.hide();
18063         }
18064     },
18065     
18066     onBeforeLoad: function()
18067     {
18068         this.clearEvents();
18069         if(this.loadMask){
18070             this.maskEl.show();
18071         }
18072     }
18073 });
18074
18075  
18076  /*
18077  * - LGPL
18078  *
18079  * element
18080  * 
18081  */
18082
18083 /**
18084  * @class Roo.bootstrap.Popover
18085  * @extends Roo.bootstrap.Component
18086  * Bootstrap Popover class
18087  * @cfg {String} html contents of the popover   (or false to use children..)
18088  * @cfg {String} title of popover (or false to hide)
18089  * @cfg {String} placement how it is placed
18090  * @cfg {String} trigger click || hover (or false to trigger manually)
18091  * @cfg {String} over what (parent or false to trigger manually.)
18092  * @cfg {Number} delay - delay before showing
18093  
18094  * @constructor
18095  * Create a new Popover
18096  * @param {Object} config The config object
18097  */
18098
18099 Roo.bootstrap.Popover = function(config){
18100     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18101     
18102     this.addEvents({
18103         // raw events
18104          /**
18105          * @event show
18106          * After the popover show
18107          * 
18108          * @param {Roo.bootstrap.Popover} this
18109          */
18110         "show" : true,
18111         /**
18112          * @event hide
18113          * After the popover hide
18114          * 
18115          * @param {Roo.bootstrap.Popover} this
18116          */
18117         "hide" : true
18118     });
18119 };
18120
18121 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18122     
18123     title: 'Fill in a title',
18124     html: false,
18125     
18126     placement : 'right',
18127     trigger : 'hover', // hover
18128     
18129     delay : 0,
18130     
18131     over: 'parent',
18132     
18133     can_build_overlaid : false,
18134     
18135     getChildContainer : function()
18136     {
18137         return this.el.select('.popover-content',true).first();
18138     },
18139     
18140     getAutoCreate : function(){
18141          
18142         var cfg = {
18143            cls : 'popover roo-dynamic',
18144            style: 'display:block',
18145            cn : [
18146                 {
18147                     cls : 'arrow'
18148                 },
18149                 {
18150                     cls : 'popover-inner',
18151                     cn : [
18152                         {
18153                             tag: 'h3',
18154                             cls: 'popover-title popover-header',
18155                             html : this.title
18156                         },
18157                         {
18158                             cls : 'popover-content popover-body',
18159                             html : this.html
18160                         }
18161                     ]
18162                     
18163                 }
18164            ]
18165         };
18166         
18167         return cfg;
18168     },
18169     setTitle: function(str)
18170     {
18171         this.title = str;
18172         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18173     },
18174     setContent: function(str)
18175     {
18176         this.html = str;
18177         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18178     },
18179     // as it get's added to the bottom of the page.
18180     onRender : function(ct, position)
18181     {
18182         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18183         if(!this.el){
18184             var cfg = Roo.apply({},  this.getAutoCreate());
18185             cfg.id = Roo.id();
18186             
18187             if (this.cls) {
18188                 cfg.cls += ' ' + this.cls;
18189             }
18190             if (this.style) {
18191                 cfg.style = this.style;
18192             }
18193             //Roo.log("adding to ");
18194             this.el = Roo.get(document.body).createChild(cfg, position);
18195 //            Roo.log(this.el);
18196         }
18197         this.initEvents();
18198     },
18199     
18200     initEvents : function()
18201     {
18202         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18203         this.el.enableDisplayMode('block');
18204         this.el.hide();
18205         if (this.over === false) {
18206             return; 
18207         }
18208         if (this.triggers === false) {
18209             return;
18210         }
18211         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18212         var triggers = this.trigger ? this.trigger.split(' ') : [];
18213         Roo.each(triggers, function(trigger) {
18214         
18215             if (trigger == 'click') {
18216                 on_el.on('click', this.toggle, this);
18217             } else if (trigger != 'manual') {
18218                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18219                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18220       
18221                 on_el.on(eventIn  ,this.enter, this);
18222                 on_el.on(eventOut, this.leave, this);
18223             }
18224         }, this);
18225         
18226     },
18227     
18228     
18229     // private
18230     timeout : null,
18231     hoverState : null,
18232     
18233     toggle : function () {
18234         this.hoverState == 'in' ? this.leave() : this.enter();
18235     },
18236     
18237     enter : function () {
18238         
18239         clearTimeout(this.timeout);
18240     
18241         this.hoverState = 'in';
18242     
18243         if (!this.delay || !this.delay.show) {
18244             this.show();
18245             return;
18246         }
18247         var _t = this;
18248         this.timeout = setTimeout(function () {
18249             if (_t.hoverState == 'in') {
18250                 _t.show();
18251             }
18252         }, this.delay.show)
18253     },
18254     
18255     leave : function() {
18256         clearTimeout(this.timeout);
18257     
18258         this.hoverState = 'out';
18259     
18260         if (!this.delay || !this.delay.hide) {
18261             this.hide();
18262             return;
18263         }
18264         var _t = this;
18265         this.timeout = setTimeout(function () {
18266             if (_t.hoverState == 'out') {
18267                 _t.hide();
18268             }
18269         }, this.delay.hide)
18270     },
18271     
18272     show : function (on_el)
18273     {
18274         if (!on_el) {
18275             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18276         }
18277         
18278         // set content.
18279         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18280         if (this.html !== false) {
18281             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18282         }
18283         this.el.removeClass([
18284             'fade','top','bottom', 'left', 'right','in',
18285             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18286         ]);
18287         if (!this.title.length) {
18288             this.el.select('.popover-title',true).hide();
18289         }
18290         
18291         var placement = typeof this.placement == 'function' ?
18292             this.placement.call(this, this.el, on_el) :
18293             this.placement;
18294             
18295         var autoToken = /\s?auto?\s?/i;
18296         var autoPlace = autoToken.test(placement);
18297         if (autoPlace) {
18298             placement = placement.replace(autoToken, '') || 'top';
18299         }
18300         
18301         //this.el.detach()
18302         //this.el.setXY([0,0]);
18303         this.el.show();
18304         this.el.dom.style.display='block';
18305         this.el.addClass(placement);
18306         
18307         //this.el.appendTo(on_el);
18308         
18309         var p = this.getPosition();
18310         var box = this.el.getBox();
18311         
18312         if (autoPlace) {
18313             // fixme..
18314         }
18315         var align = Roo.bootstrap.Popover.alignment[placement];
18316         
18317 //        Roo.log(align);
18318         this.el.alignTo(on_el, align[0],align[1]);
18319         //var arrow = this.el.select('.arrow',true).first();
18320         //arrow.set(align[2], 
18321         
18322         this.el.addClass('in');
18323         
18324         
18325         if (this.el.hasClass('fade')) {
18326             // fade it?
18327         }
18328         
18329         this.hoverState = 'in';
18330         
18331         this.fireEvent('show', this);
18332         
18333     },
18334     hide : function()
18335     {
18336         this.el.setXY([0,0]);
18337         this.el.removeClass('in');
18338         this.el.hide();
18339         this.hoverState = null;
18340         
18341         this.fireEvent('hide', this);
18342     }
18343     
18344 });
18345
18346 Roo.bootstrap.Popover.alignment = {
18347     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18348     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18349     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18350     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18351 };
18352
18353  /*
18354  * - LGPL
18355  *
18356  * Progress
18357  * 
18358  */
18359
18360 /**
18361  * @class Roo.bootstrap.Progress
18362  * @extends Roo.bootstrap.Component
18363  * Bootstrap Progress class
18364  * @cfg {Boolean} striped striped of the progress bar
18365  * @cfg {Boolean} active animated of the progress bar
18366  * 
18367  * 
18368  * @constructor
18369  * Create a new Progress
18370  * @param {Object} config The config object
18371  */
18372
18373 Roo.bootstrap.Progress = function(config){
18374     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18375 };
18376
18377 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18378     
18379     striped : false,
18380     active: false,
18381     
18382     getAutoCreate : function(){
18383         var cfg = {
18384             tag: 'div',
18385             cls: 'progress'
18386         };
18387         
18388         
18389         if(this.striped){
18390             cfg.cls += ' progress-striped';
18391         }
18392       
18393         if(this.active){
18394             cfg.cls += ' active';
18395         }
18396         
18397         
18398         return cfg;
18399     }
18400    
18401 });
18402
18403  
18404
18405  /*
18406  * - LGPL
18407  *
18408  * ProgressBar
18409  * 
18410  */
18411
18412 /**
18413  * @class Roo.bootstrap.ProgressBar
18414  * @extends Roo.bootstrap.Component
18415  * Bootstrap ProgressBar class
18416  * @cfg {Number} aria_valuenow aria-value now
18417  * @cfg {Number} aria_valuemin aria-value min
18418  * @cfg {Number} aria_valuemax aria-value max
18419  * @cfg {String} label label for the progress bar
18420  * @cfg {String} panel (success | info | warning | danger )
18421  * @cfg {String} role role of the progress bar
18422  * @cfg {String} sr_only text
18423  * 
18424  * 
18425  * @constructor
18426  * Create a new ProgressBar
18427  * @param {Object} config The config object
18428  */
18429
18430 Roo.bootstrap.ProgressBar = function(config){
18431     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18432 };
18433
18434 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18435     
18436     aria_valuenow : 0,
18437     aria_valuemin : 0,
18438     aria_valuemax : 100,
18439     label : false,
18440     panel : false,
18441     role : false,
18442     sr_only: false,
18443     
18444     getAutoCreate : function()
18445     {
18446         
18447         var cfg = {
18448             tag: 'div',
18449             cls: 'progress-bar',
18450             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18451         };
18452         
18453         if(this.sr_only){
18454             cfg.cn = {
18455                 tag: 'span',
18456                 cls: 'sr-only',
18457                 html: this.sr_only
18458             }
18459         }
18460         
18461         if(this.role){
18462             cfg.role = this.role;
18463         }
18464         
18465         if(this.aria_valuenow){
18466             cfg['aria-valuenow'] = this.aria_valuenow;
18467         }
18468         
18469         if(this.aria_valuemin){
18470             cfg['aria-valuemin'] = this.aria_valuemin;
18471         }
18472         
18473         if(this.aria_valuemax){
18474             cfg['aria-valuemax'] = this.aria_valuemax;
18475         }
18476         
18477         if(this.label && !this.sr_only){
18478             cfg.html = this.label;
18479         }
18480         
18481         if(this.panel){
18482             cfg.cls += ' progress-bar-' + this.panel;
18483         }
18484         
18485         return cfg;
18486     },
18487     
18488     update : function(aria_valuenow)
18489     {
18490         this.aria_valuenow = aria_valuenow;
18491         
18492         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18493     }
18494    
18495 });
18496
18497  
18498
18499  /*
18500  * - LGPL
18501  *
18502  * column
18503  * 
18504  */
18505
18506 /**
18507  * @class Roo.bootstrap.TabGroup
18508  * @extends Roo.bootstrap.Column
18509  * Bootstrap Column class
18510  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18511  * @cfg {Boolean} carousel true to make the group behave like a carousel
18512  * @cfg {Boolean} bullets show bullets for the panels
18513  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18514  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18515  * @cfg {Boolean} showarrow (true|false) show arrow default true
18516  * 
18517  * @constructor
18518  * Create a new TabGroup
18519  * @param {Object} config The config object
18520  */
18521
18522 Roo.bootstrap.TabGroup = function(config){
18523     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18524     if (!this.navId) {
18525         this.navId = Roo.id();
18526     }
18527     this.tabs = [];
18528     Roo.bootstrap.TabGroup.register(this);
18529     
18530 };
18531
18532 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18533     
18534     carousel : false,
18535     transition : false,
18536     bullets : 0,
18537     timer : 0,
18538     autoslide : false,
18539     slideFn : false,
18540     slideOnTouch : false,
18541     showarrow : true,
18542     
18543     getAutoCreate : function()
18544     {
18545         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18546         
18547         cfg.cls += ' tab-content';
18548         
18549         if (this.carousel) {
18550             cfg.cls += ' carousel slide';
18551             
18552             cfg.cn = [{
18553                cls : 'carousel-inner',
18554                cn : []
18555             }];
18556         
18557             if(this.bullets  && !Roo.isTouch){
18558                 
18559                 var bullets = {
18560                     cls : 'carousel-bullets',
18561                     cn : []
18562                 };
18563                
18564                 if(this.bullets_cls){
18565                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18566                 }
18567                 
18568                 bullets.cn.push({
18569                     cls : 'clear'
18570                 });
18571                 
18572                 cfg.cn[0].cn.push(bullets);
18573             }
18574             
18575             if(this.showarrow){
18576                 cfg.cn[0].cn.push({
18577                     tag : 'div',
18578                     class : 'carousel-arrow',
18579                     cn : [
18580                         {
18581                             tag : 'div',
18582                             class : 'carousel-prev',
18583                             cn : [
18584                                 {
18585                                     tag : 'i',
18586                                     class : 'fa fa-chevron-left'
18587                                 }
18588                             ]
18589                         },
18590                         {
18591                             tag : 'div',
18592                             class : 'carousel-next',
18593                             cn : [
18594                                 {
18595                                     tag : 'i',
18596                                     class : 'fa fa-chevron-right'
18597                                 }
18598                             ]
18599                         }
18600                     ]
18601                 });
18602             }
18603             
18604         }
18605         
18606         return cfg;
18607     },
18608     
18609     initEvents:  function()
18610     {
18611 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18612 //            this.el.on("touchstart", this.onTouchStart, this);
18613 //        }
18614         
18615         if(this.autoslide){
18616             var _this = this;
18617             
18618             this.slideFn = window.setInterval(function() {
18619                 _this.showPanelNext();
18620             }, this.timer);
18621         }
18622         
18623         if(this.showarrow){
18624             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18625             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18626         }
18627         
18628         
18629     },
18630     
18631 //    onTouchStart : function(e, el, o)
18632 //    {
18633 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18634 //            return;
18635 //        }
18636 //        
18637 //        this.showPanelNext();
18638 //    },
18639     
18640     
18641     getChildContainer : function()
18642     {
18643         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18644     },
18645     
18646     /**
18647     * register a Navigation item
18648     * @param {Roo.bootstrap.NavItem} the navitem to add
18649     */
18650     register : function(item)
18651     {
18652         this.tabs.push( item);
18653         item.navId = this.navId; // not really needed..
18654         this.addBullet();
18655     
18656     },
18657     
18658     getActivePanel : function()
18659     {
18660         var r = false;
18661         Roo.each(this.tabs, function(t) {
18662             if (t.active) {
18663                 r = t;
18664                 return false;
18665             }
18666             return null;
18667         });
18668         return r;
18669         
18670     },
18671     getPanelByName : function(n)
18672     {
18673         var r = false;
18674         Roo.each(this.tabs, function(t) {
18675             if (t.tabId == n) {
18676                 r = t;
18677                 return false;
18678             }
18679             return null;
18680         });
18681         return r;
18682     },
18683     indexOfPanel : function(p)
18684     {
18685         var r = false;
18686         Roo.each(this.tabs, function(t,i) {
18687             if (t.tabId == p.tabId) {
18688                 r = i;
18689                 return false;
18690             }
18691             return null;
18692         });
18693         return r;
18694     },
18695     /**
18696      * show a specific panel
18697      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18698      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18699      */
18700     showPanel : function (pan)
18701     {
18702         if(this.transition || typeof(pan) == 'undefined'){
18703             Roo.log("waiting for the transitionend");
18704             return false;
18705         }
18706         
18707         if (typeof(pan) == 'number') {
18708             pan = this.tabs[pan];
18709         }
18710         
18711         if (typeof(pan) == 'string') {
18712             pan = this.getPanelByName(pan);
18713         }
18714         
18715         var cur = this.getActivePanel();
18716         
18717         if(!pan || !cur){
18718             Roo.log('pan or acitve pan is undefined');
18719             return false;
18720         }
18721         
18722         if (pan.tabId == this.getActivePanel().tabId) {
18723             return true;
18724         }
18725         
18726         if (false === cur.fireEvent('beforedeactivate')) {
18727             return false;
18728         }
18729         
18730         if(this.bullets > 0 && !Roo.isTouch){
18731             this.setActiveBullet(this.indexOfPanel(pan));
18732         }
18733         
18734         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18735             
18736             //class="carousel-item carousel-item-next carousel-item-left"
18737             
18738             this.transition = true;
18739             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18740             var lr = dir == 'next' ? 'left' : 'right';
18741             pan.el.addClass(dir); // or prev
18742             pan.el.addClass('carousel-item-' + dir); // or prev
18743             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18744             cur.el.addClass(lr); // or right
18745             pan.el.addClass(lr);
18746             cur.el.addClass('carousel-item-' +lr); // or right
18747             pan.el.addClass('carousel-item-' +lr);
18748             
18749             
18750             var _this = this;
18751             cur.el.on('transitionend', function() {
18752                 Roo.log("trans end?");
18753                 
18754                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18755                 pan.setActive(true);
18756                 
18757                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18758                 cur.setActive(false);
18759                 
18760                 _this.transition = false;
18761                 
18762             }, this, { single:  true } );
18763             
18764             return true;
18765         }
18766         
18767         cur.setActive(false);
18768         pan.setActive(true);
18769         
18770         return true;
18771         
18772     },
18773     showPanelNext : function()
18774     {
18775         var i = this.indexOfPanel(this.getActivePanel());
18776         
18777         if (i >= this.tabs.length - 1 && !this.autoslide) {
18778             return;
18779         }
18780         
18781         if (i >= this.tabs.length - 1 && this.autoslide) {
18782             i = -1;
18783         }
18784         
18785         this.showPanel(this.tabs[i+1]);
18786     },
18787     
18788     showPanelPrev : function()
18789     {
18790         var i = this.indexOfPanel(this.getActivePanel());
18791         
18792         if (i  < 1 && !this.autoslide) {
18793             return;
18794         }
18795         
18796         if (i < 1 && this.autoslide) {
18797             i = this.tabs.length;
18798         }
18799         
18800         this.showPanel(this.tabs[i-1]);
18801     },
18802     
18803     
18804     addBullet: function()
18805     {
18806         if(!this.bullets || Roo.isTouch){
18807             return;
18808         }
18809         var ctr = this.el.select('.carousel-bullets',true).first();
18810         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18811         var bullet = ctr.createChild({
18812             cls : 'bullet bullet-' + i
18813         },ctr.dom.lastChild);
18814         
18815         
18816         var _this = this;
18817         
18818         bullet.on('click', (function(e, el, o, ii, t){
18819
18820             e.preventDefault();
18821
18822             this.showPanel(ii);
18823
18824             if(this.autoslide && this.slideFn){
18825                 clearInterval(this.slideFn);
18826                 this.slideFn = window.setInterval(function() {
18827                     _this.showPanelNext();
18828                 }, this.timer);
18829             }
18830
18831         }).createDelegate(this, [i, bullet], true));
18832                 
18833         
18834     },
18835      
18836     setActiveBullet : function(i)
18837     {
18838         if(Roo.isTouch){
18839             return;
18840         }
18841         
18842         Roo.each(this.el.select('.bullet', true).elements, function(el){
18843             el.removeClass('selected');
18844         });
18845
18846         var bullet = this.el.select('.bullet-' + i, true).first();
18847         
18848         if(!bullet){
18849             return;
18850         }
18851         
18852         bullet.addClass('selected');
18853     }
18854     
18855     
18856   
18857 });
18858
18859  
18860
18861  
18862  
18863 Roo.apply(Roo.bootstrap.TabGroup, {
18864     
18865     groups: {},
18866      /**
18867     * register a Navigation Group
18868     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18869     */
18870     register : function(navgrp)
18871     {
18872         this.groups[navgrp.navId] = navgrp;
18873         
18874     },
18875     /**
18876     * fetch a Navigation Group based on the navigation ID
18877     * if one does not exist , it will get created.
18878     * @param {string} the navgroup to add
18879     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18880     */
18881     get: function(navId) {
18882         if (typeof(this.groups[navId]) == 'undefined') {
18883             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18884         }
18885         return this.groups[navId] ;
18886     }
18887     
18888     
18889     
18890 });
18891
18892  /*
18893  * - LGPL
18894  *
18895  * TabPanel
18896  * 
18897  */
18898
18899 /**
18900  * @class Roo.bootstrap.TabPanel
18901  * @extends Roo.bootstrap.Component
18902  * Bootstrap TabPanel class
18903  * @cfg {Boolean} active panel active
18904  * @cfg {String} html panel content
18905  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18906  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18907  * @cfg {String} href click to link..
18908  * 
18909  * 
18910  * @constructor
18911  * Create a new TabPanel
18912  * @param {Object} config The config object
18913  */
18914
18915 Roo.bootstrap.TabPanel = function(config){
18916     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18917     this.addEvents({
18918         /**
18919              * @event changed
18920              * Fires when the active status changes
18921              * @param {Roo.bootstrap.TabPanel} this
18922              * @param {Boolean} state the new state
18923             
18924          */
18925         'changed': true,
18926         /**
18927              * @event beforedeactivate
18928              * Fires before a tab is de-activated - can be used to do validation on a form.
18929              * @param {Roo.bootstrap.TabPanel} this
18930              * @return {Boolean} false if there is an error
18931             
18932          */
18933         'beforedeactivate': true
18934      });
18935     
18936     this.tabId = this.tabId || Roo.id();
18937   
18938 };
18939
18940 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18941     
18942     active: false,
18943     html: false,
18944     tabId: false,
18945     navId : false,
18946     href : '',
18947     
18948     getAutoCreate : function(){
18949         
18950         
18951         var cfg = {
18952             tag: 'div',
18953             // item is needed for carousel - not sure if it has any effect otherwise
18954             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18955             html: this.html || ''
18956         };
18957         
18958         if(this.active){
18959             cfg.cls += ' active';
18960         }
18961         
18962         if(this.tabId){
18963             cfg.tabId = this.tabId;
18964         }
18965         
18966         
18967         
18968         return cfg;
18969     },
18970     
18971     initEvents:  function()
18972     {
18973         var p = this.parent();
18974         
18975         this.navId = this.navId || p.navId;
18976         
18977         if (typeof(this.navId) != 'undefined') {
18978             // not really needed.. but just in case.. parent should be a NavGroup.
18979             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18980             
18981             tg.register(this);
18982             
18983             var i = tg.tabs.length - 1;
18984             
18985             if(this.active && tg.bullets > 0 && i < tg.bullets){
18986                 tg.setActiveBullet(i);
18987             }
18988         }
18989         
18990         this.el.on('click', this.onClick, this);
18991         
18992         if(Roo.isTouch){
18993             this.el.on("touchstart", this.onTouchStart, this);
18994             this.el.on("touchmove", this.onTouchMove, this);
18995             this.el.on("touchend", this.onTouchEnd, this);
18996         }
18997         
18998     },
18999     
19000     onRender : function(ct, position)
19001     {
19002         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19003     },
19004     
19005     setActive : function(state)
19006     {
19007         Roo.log("panel - set active " + this.tabId + "=" + state);
19008         
19009         this.active = state;
19010         if (!state) {
19011             this.el.removeClass('active');
19012             
19013         } else  if (!this.el.hasClass('active')) {
19014             this.el.addClass('active');
19015         }
19016         
19017         this.fireEvent('changed', this, state);
19018     },
19019     
19020     onClick : function(e)
19021     {
19022         e.preventDefault();
19023         
19024         if(!this.href.length){
19025             return;
19026         }
19027         
19028         window.location.href = this.href;
19029     },
19030     
19031     startX : 0,
19032     startY : 0,
19033     endX : 0,
19034     endY : 0,
19035     swiping : false,
19036     
19037     onTouchStart : function(e)
19038     {
19039         this.swiping = false;
19040         
19041         this.startX = e.browserEvent.touches[0].clientX;
19042         this.startY = e.browserEvent.touches[0].clientY;
19043     },
19044     
19045     onTouchMove : function(e)
19046     {
19047         this.swiping = true;
19048         
19049         this.endX = e.browserEvent.touches[0].clientX;
19050         this.endY = e.browserEvent.touches[0].clientY;
19051     },
19052     
19053     onTouchEnd : function(e)
19054     {
19055         if(!this.swiping){
19056             this.onClick(e);
19057             return;
19058         }
19059         
19060         var tabGroup = this.parent();
19061         
19062         if(this.endX > this.startX){ // swiping right
19063             tabGroup.showPanelPrev();
19064             return;
19065         }
19066         
19067         if(this.startX > this.endX){ // swiping left
19068             tabGroup.showPanelNext();
19069             return;
19070         }
19071     }
19072     
19073     
19074 });
19075  
19076
19077  
19078
19079  /*
19080  * - LGPL
19081  *
19082  * DateField
19083  * 
19084  */
19085
19086 /**
19087  * @class Roo.bootstrap.DateField
19088  * @extends Roo.bootstrap.Input
19089  * Bootstrap DateField class
19090  * @cfg {Number} weekStart default 0
19091  * @cfg {String} viewMode default empty, (months|years)
19092  * @cfg {String} minViewMode default empty, (months|years)
19093  * @cfg {Number} startDate default -Infinity
19094  * @cfg {Number} endDate default Infinity
19095  * @cfg {Boolean} todayHighlight default false
19096  * @cfg {Boolean} todayBtn default false
19097  * @cfg {Boolean} calendarWeeks default false
19098  * @cfg {Object} daysOfWeekDisabled default empty
19099  * @cfg {Boolean} singleMode default false (true | false)
19100  * 
19101  * @cfg {Boolean} keyboardNavigation default true
19102  * @cfg {String} language default en
19103  * 
19104  * @constructor
19105  * Create a new DateField
19106  * @param {Object} config The config object
19107  */
19108
19109 Roo.bootstrap.DateField = function(config){
19110     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19111      this.addEvents({
19112             /**
19113              * @event show
19114              * Fires when this field show.
19115              * @param {Roo.bootstrap.DateField} this
19116              * @param {Mixed} date The date value
19117              */
19118             show : true,
19119             /**
19120              * @event show
19121              * Fires when this field hide.
19122              * @param {Roo.bootstrap.DateField} this
19123              * @param {Mixed} date The date value
19124              */
19125             hide : true,
19126             /**
19127              * @event select
19128              * Fires when select a date.
19129              * @param {Roo.bootstrap.DateField} this
19130              * @param {Mixed} date The date value
19131              */
19132             select : true,
19133             /**
19134              * @event beforeselect
19135              * Fires when before select a date.
19136              * @param {Roo.bootstrap.DateField} this
19137              * @param {Mixed} date The date value
19138              */
19139             beforeselect : true
19140         });
19141 };
19142
19143 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19144     
19145     /**
19146      * @cfg {String} format
19147      * The default date format string which can be overriden for localization support.  The format must be
19148      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19149      */
19150     format : "m/d/y",
19151     /**
19152      * @cfg {String} altFormats
19153      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19154      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19155      */
19156     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19157     
19158     weekStart : 0,
19159     
19160     viewMode : '',
19161     
19162     minViewMode : '',
19163     
19164     todayHighlight : false,
19165     
19166     todayBtn: false,
19167     
19168     language: 'en',
19169     
19170     keyboardNavigation: true,
19171     
19172     calendarWeeks: false,
19173     
19174     startDate: -Infinity,
19175     
19176     endDate: Infinity,
19177     
19178     daysOfWeekDisabled: [],
19179     
19180     _events: [],
19181     
19182     singleMode : false,
19183     
19184     UTCDate: function()
19185     {
19186         return new Date(Date.UTC.apply(Date, arguments));
19187     },
19188     
19189     UTCToday: function()
19190     {
19191         var today = new Date();
19192         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19193     },
19194     
19195     getDate: function() {
19196             var d = this.getUTCDate();
19197             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19198     },
19199     
19200     getUTCDate: function() {
19201             return this.date;
19202     },
19203     
19204     setDate: function(d) {
19205             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19206     },
19207     
19208     setUTCDate: function(d) {
19209             this.date = d;
19210             this.setValue(this.formatDate(this.date));
19211     },
19212         
19213     onRender: function(ct, position)
19214     {
19215         
19216         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19217         
19218         this.language = this.language || 'en';
19219         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19220         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19221         
19222         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19223         this.format = this.format || 'm/d/y';
19224         this.isInline = false;
19225         this.isInput = true;
19226         this.component = this.el.select('.add-on', true).first() || false;
19227         this.component = (this.component && this.component.length === 0) ? false : this.component;
19228         this.hasInput = this.component && this.inputEl().length;
19229         
19230         if (typeof(this.minViewMode === 'string')) {
19231             switch (this.minViewMode) {
19232                 case 'months':
19233                     this.minViewMode = 1;
19234                     break;
19235                 case 'years':
19236                     this.minViewMode = 2;
19237                     break;
19238                 default:
19239                     this.minViewMode = 0;
19240                     break;
19241             }
19242         }
19243         
19244         if (typeof(this.viewMode === 'string')) {
19245             switch (this.viewMode) {
19246                 case 'months':
19247                     this.viewMode = 1;
19248                     break;
19249                 case 'years':
19250                     this.viewMode = 2;
19251                     break;
19252                 default:
19253                     this.viewMode = 0;
19254                     break;
19255             }
19256         }
19257                 
19258         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19259         
19260 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19261         
19262         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19263         
19264         this.picker().on('mousedown', this.onMousedown, this);
19265         this.picker().on('click', this.onClick, this);
19266         
19267         this.picker().addClass('datepicker-dropdown');
19268         
19269         this.startViewMode = this.viewMode;
19270         
19271         if(this.singleMode){
19272             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19273                 v.setVisibilityMode(Roo.Element.DISPLAY);
19274                 v.hide();
19275             });
19276             
19277             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19278                 v.setStyle('width', '189px');
19279             });
19280         }
19281         
19282         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19283             if(!this.calendarWeeks){
19284                 v.remove();
19285                 return;
19286             }
19287             
19288             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19289             v.attr('colspan', function(i, val){
19290                 return parseInt(val) + 1;
19291             });
19292         });
19293                         
19294         
19295         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19296         
19297         this.setStartDate(this.startDate);
19298         this.setEndDate(this.endDate);
19299         
19300         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19301         
19302         this.fillDow();
19303         this.fillMonths();
19304         this.update();
19305         this.showMode();
19306         
19307         if(this.isInline) {
19308             this.showPopup();
19309         }
19310     },
19311     
19312     picker : function()
19313     {
19314         return this.pickerEl;
19315 //        return this.el.select('.datepicker', true).first();
19316     },
19317     
19318     fillDow: function()
19319     {
19320         var dowCnt = this.weekStart;
19321         
19322         var dow = {
19323             tag: 'tr',
19324             cn: [
19325                 
19326             ]
19327         };
19328         
19329         if(this.calendarWeeks){
19330             dow.cn.push({
19331                 tag: 'th',
19332                 cls: 'cw',
19333                 html: '&nbsp;'
19334             })
19335         }
19336         
19337         while (dowCnt < this.weekStart + 7) {
19338             dow.cn.push({
19339                 tag: 'th',
19340                 cls: 'dow',
19341                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19342             });
19343         }
19344         
19345         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19346     },
19347     
19348     fillMonths: function()
19349     {    
19350         var i = 0;
19351         var months = this.picker().select('>.datepicker-months td', true).first();
19352         
19353         months.dom.innerHTML = '';
19354         
19355         while (i < 12) {
19356             var month = {
19357                 tag: 'span',
19358                 cls: 'month',
19359                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19360             };
19361             
19362             months.createChild(month);
19363         }
19364         
19365     },
19366     
19367     update: function()
19368     {
19369         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;
19370         
19371         if (this.date < this.startDate) {
19372             this.viewDate = new Date(this.startDate);
19373         } else if (this.date > this.endDate) {
19374             this.viewDate = new Date(this.endDate);
19375         } else {
19376             this.viewDate = new Date(this.date);
19377         }
19378         
19379         this.fill();
19380     },
19381     
19382     fill: function() 
19383     {
19384         var d = new Date(this.viewDate),
19385                 year = d.getUTCFullYear(),
19386                 month = d.getUTCMonth(),
19387                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19388                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19389                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19390                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19391                 currentDate = this.date && this.date.valueOf(),
19392                 today = this.UTCToday();
19393         
19394         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19395         
19396 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19397         
19398 //        this.picker.select('>tfoot th.today').
19399 //                                              .text(dates[this.language].today)
19400 //                                              .toggle(this.todayBtn !== false);
19401     
19402         this.updateNavArrows();
19403         this.fillMonths();
19404                                                 
19405         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19406         
19407         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19408          
19409         prevMonth.setUTCDate(day);
19410         
19411         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19412         
19413         var nextMonth = new Date(prevMonth);
19414         
19415         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19416         
19417         nextMonth = nextMonth.valueOf();
19418         
19419         var fillMonths = false;
19420         
19421         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19422         
19423         while(prevMonth.valueOf() <= nextMonth) {
19424             var clsName = '';
19425             
19426             if (prevMonth.getUTCDay() === this.weekStart) {
19427                 if(fillMonths){
19428                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19429                 }
19430                     
19431                 fillMonths = {
19432                     tag: 'tr',
19433                     cn: []
19434                 };
19435                 
19436                 if(this.calendarWeeks){
19437                     // ISO 8601: First week contains first thursday.
19438                     // ISO also states week starts on Monday, but we can be more abstract here.
19439                     var
19440                     // Start of current week: based on weekstart/current date
19441                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19442                     // Thursday of this week
19443                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19444                     // First Thursday of year, year from thursday
19445                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19446                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19447                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19448                     
19449                     fillMonths.cn.push({
19450                         tag: 'td',
19451                         cls: 'cw',
19452                         html: calWeek
19453                     });
19454                 }
19455             }
19456             
19457             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19458                 clsName += ' old';
19459             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19460                 clsName += ' new';
19461             }
19462             if (this.todayHighlight &&
19463                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19464                 prevMonth.getUTCMonth() == today.getMonth() &&
19465                 prevMonth.getUTCDate() == today.getDate()) {
19466                 clsName += ' today';
19467             }
19468             
19469             if (currentDate && prevMonth.valueOf() === currentDate) {
19470                 clsName += ' active';
19471             }
19472             
19473             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19474                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19475                     clsName += ' disabled';
19476             }
19477             
19478             fillMonths.cn.push({
19479                 tag: 'td',
19480                 cls: 'day ' + clsName,
19481                 html: prevMonth.getDate()
19482             });
19483             
19484             prevMonth.setDate(prevMonth.getDate()+1);
19485         }
19486           
19487         var currentYear = this.date && this.date.getUTCFullYear();
19488         var currentMonth = this.date && this.date.getUTCMonth();
19489         
19490         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19491         
19492         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19493             v.removeClass('active');
19494             
19495             if(currentYear === year && k === currentMonth){
19496                 v.addClass('active');
19497             }
19498             
19499             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19500                 v.addClass('disabled');
19501             }
19502             
19503         });
19504         
19505         
19506         year = parseInt(year/10, 10) * 10;
19507         
19508         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19509         
19510         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19511         
19512         year -= 1;
19513         for (var i = -1; i < 11; i++) {
19514             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19515                 tag: 'span',
19516                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19517                 html: year
19518             });
19519             
19520             year += 1;
19521         }
19522     },
19523     
19524     showMode: function(dir) 
19525     {
19526         if (dir) {
19527             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19528         }
19529         
19530         Roo.each(this.picker().select('>div',true).elements, function(v){
19531             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19532             v.hide();
19533         });
19534         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19535     },
19536     
19537     place: function()
19538     {
19539         if(this.isInline) {
19540             return;
19541         }
19542         
19543         this.picker().removeClass(['bottom', 'top']);
19544         
19545         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19546             /*
19547              * place to the top of element!
19548              *
19549              */
19550             
19551             this.picker().addClass('top');
19552             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19553             
19554             return;
19555         }
19556         
19557         this.picker().addClass('bottom');
19558         
19559         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19560     },
19561     
19562     parseDate : function(value)
19563     {
19564         if(!value || value instanceof Date){
19565             return value;
19566         }
19567         var v = Date.parseDate(value, this.format);
19568         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19569             v = Date.parseDate(value, 'Y-m-d');
19570         }
19571         if(!v && this.altFormats){
19572             if(!this.altFormatsArray){
19573                 this.altFormatsArray = this.altFormats.split("|");
19574             }
19575             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19576                 v = Date.parseDate(value, this.altFormatsArray[i]);
19577             }
19578         }
19579         return v;
19580     },
19581     
19582     formatDate : function(date, fmt)
19583     {   
19584         return (!date || !(date instanceof Date)) ?
19585         date : date.dateFormat(fmt || this.format);
19586     },
19587     
19588     onFocus : function()
19589     {
19590         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19591         this.showPopup();
19592     },
19593     
19594     onBlur : function()
19595     {
19596         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19597         
19598         var d = this.inputEl().getValue();
19599         
19600         this.setValue(d);
19601                 
19602         this.hidePopup();
19603     },
19604     
19605     showPopup : function()
19606     {
19607         this.picker().show();
19608         this.update();
19609         this.place();
19610         
19611         this.fireEvent('showpopup', this, this.date);
19612     },
19613     
19614     hidePopup : function()
19615     {
19616         if(this.isInline) {
19617             return;
19618         }
19619         this.picker().hide();
19620         this.viewMode = this.startViewMode;
19621         this.showMode();
19622         
19623         this.fireEvent('hidepopup', this, this.date);
19624         
19625     },
19626     
19627     onMousedown: function(e)
19628     {
19629         e.stopPropagation();
19630         e.preventDefault();
19631     },
19632     
19633     keyup: function(e)
19634     {
19635         Roo.bootstrap.DateField.superclass.keyup.call(this);
19636         this.update();
19637     },
19638
19639     setValue: function(v)
19640     {
19641         if(this.fireEvent('beforeselect', this, v) !== false){
19642             var d = new Date(this.parseDate(v) ).clearTime();
19643         
19644             if(isNaN(d.getTime())){
19645                 this.date = this.viewDate = '';
19646                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19647                 return;
19648             }
19649
19650             v = this.formatDate(d);
19651
19652             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19653
19654             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19655
19656             this.update();
19657
19658             this.fireEvent('select', this, this.date);
19659         }
19660     },
19661     
19662     getValue: function()
19663     {
19664         return this.formatDate(this.date);
19665     },
19666     
19667     fireKey: function(e)
19668     {
19669         if (!this.picker().isVisible()){
19670             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19671                 this.showPopup();
19672             }
19673             return;
19674         }
19675         
19676         var dateChanged = false,
19677         dir, day, month,
19678         newDate, newViewDate;
19679         
19680         switch(e.keyCode){
19681             case 27: // escape
19682                 this.hidePopup();
19683                 e.preventDefault();
19684                 break;
19685             case 37: // left
19686             case 39: // right
19687                 if (!this.keyboardNavigation) {
19688                     break;
19689                 }
19690                 dir = e.keyCode == 37 ? -1 : 1;
19691                 
19692                 if (e.ctrlKey){
19693                     newDate = this.moveYear(this.date, dir);
19694                     newViewDate = this.moveYear(this.viewDate, dir);
19695                 } else if (e.shiftKey){
19696                     newDate = this.moveMonth(this.date, dir);
19697                     newViewDate = this.moveMonth(this.viewDate, dir);
19698                 } else {
19699                     newDate = new Date(this.date);
19700                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19701                     newViewDate = new Date(this.viewDate);
19702                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19703                 }
19704                 if (this.dateWithinRange(newDate)){
19705                     this.date = newDate;
19706                     this.viewDate = newViewDate;
19707                     this.setValue(this.formatDate(this.date));
19708 //                    this.update();
19709                     e.preventDefault();
19710                     dateChanged = true;
19711                 }
19712                 break;
19713             case 38: // up
19714             case 40: // down
19715                 if (!this.keyboardNavigation) {
19716                     break;
19717                 }
19718                 dir = e.keyCode == 38 ? -1 : 1;
19719                 if (e.ctrlKey){
19720                     newDate = this.moveYear(this.date, dir);
19721                     newViewDate = this.moveYear(this.viewDate, dir);
19722                 } else if (e.shiftKey){
19723                     newDate = this.moveMonth(this.date, dir);
19724                     newViewDate = this.moveMonth(this.viewDate, dir);
19725                 } else {
19726                     newDate = new Date(this.date);
19727                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19728                     newViewDate = new Date(this.viewDate);
19729                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19730                 }
19731                 if (this.dateWithinRange(newDate)){
19732                     this.date = newDate;
19733                     this.viewDate = newViewDate;
19734                     this.setValue(this.formatDate(this.date));
19735 //                    this.update();
19736                     e.preventDefault();
19737                     dateChanged = true;
19738                 }
19739                 break;
19740             case 13: // enter
19741                 this.setValue(this.formatDate(this.date));
19742                 this.hidePopup();
19743                 e.preventDefault();
19744                 break;
19745             case 9: // tab
19746                 this.setValue(this.formatDate(this.date));
19747                 this.hidePopup();
19748                 break;
19749             case 16: // shift
19750             case 17: // ctrl
19751             case 18: // alt
19752                 break;
19753             default :
19754                 this.hidePopup();
19755                 
19756         }
19757     },
19758     
19759     
19760     onClick: function(e) 
19761     {
19762         e.stopPropagation();
19763         e.preventDefault();
19764         
19765         var target = e.getTarget();
19766         
19767         if(target.nodeName.toLowerCase() === 'i'){
19768             target = Roo.get(target).dom.parentNode;
19769         }
19770         
19771         var nodeName = target.nodeName;
19772         var className = target.className;
19773         var html = target.innerHTML;
19774         //Roo.log(nodeName);
19775         
19776         switch(nodeName.toLowerCase()) {
19777             case 'th':
19778                 switch(className) {
19779                     case 'switch':
19780                         this.showMode(1);
19781                         break;
19782                     case 'prev':
19783                     case 'next':
19784                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19785                         switch(this.viewMode){
19786                                 case 0:
19787                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19788                                         break;
19789                                 case 1:
19790                                 case 2:
19791                                         this.viewDate = this.moveYear(this.viewDate, dir);
19792                                         break;
19793                         }
19794                         this.fill();
19795                         break;
19796                     case 'today':
19797                         var date = new Date();
19798                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19799 //                        this.fill()
19800                         this.setValue(this.formatDate(this.date));
19801                         
19802                         this.hidePopup();
19803                         break;
19804                 }
19805                 break;
19806             case 'span':
19807                 if (className.indexOf('disabled') < 0) {
19808                     this.viewDate.setUTCDate(1);
19809                     if (className.indexOf('month') > -1) {
19810                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19811                     } else {
19812                         var year = parseInt(html, 10) || 0;
19813                         this.viewDate.setUTCFullYear(year);
19814                         
19815                     }
19816                     
19817                     if(this.singleMode){
19818                         this.setValue(this.formatDate(this.viewDate));
19819                         this.hidePopup();
19820                         return;
19821                     }
19822                     
19823                     this.showMode(-1);
19824                     this.fill();
19825                 }
19826                 break;
19827                 
19828             case 'td':
19829                 //Roo.log(className);
19830                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19831                     var day = parseInt(html, 10) || 1;
19832                     var year = this.viewDate.getUTCFullYear(),
19833                         month = this.viewDate.getUTCMonth();
19834
19835                     if (className.indexOf('old') > -1) {
19836                         if(month === 0 ){
19837                             month = 11;
19838                             year -= 1;
19839                         }else{
19840                             month -= 1;
19841                         }
19842                     } else if (className.indexOf('new') > -1) {
19843                         if (month == 11) {
19844                             month = 0;
19845                             year += 1;
19846                         } else {
19847                             month += 1;
19848                         }
19849                     }
19850                     //Roo.log([year,month,day]);
19851                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19852                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19853 //                    this.fill();
19854                     //Roo.log(this.formatDate(this.date));
19855                     this.setValue(this.formatDate(this.date));
19856                     this.hidePopup();
19857                 }
19858                 break;
19859         }
19860     },
19861     
19862     setStartDate: function(startDate)
19863     {
19864         this.startDate = startDate || -Infinity;
19865         if (this.startDate !== -Infinity) {
19866             this.startDate = this.parseDate(this.startDate);
19867         }
19868         this.update();
19869         this.updateNavArrows();
19870     },
19871
19872     setEndDate: function(endDate)
19873     {
19874         this.endDate = endDate || Infinity;
19875         if (this.endDate !== Infinity) {
19876             this.endDate = this.parseDate(this.endDate);
19877         }
19878         this.update();
19879         this.updateNavArrows();
19880     },
19881     
19882     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19883     {
19884         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19885         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19886             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19887         }
19888         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19889             return parseInt(d, 10);
19890         });
19891         this.update();
19892         this.updateNavArrows();
19893     },
19894     
19895     updateNavArrows: function() 
19896     {
19897         if(this.singleMode){
19898             return;
19899         }
19900         
19901         var d = new Date(this.viewDate),
19902         year = d.getUTCFullYear(),
19903         month = d.getUTCMonth();
19904         
19905         Roo.each(this.picker().select('.prev', true).elements, function(v){
19906             v.show();
19907             switch (this.viewMode) {
19908                 case 0:
19909
19910                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19911                         v.hide();
19912                     }
19913                     break;
19914                 case 1:
19915                 case 2:
19916                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19917                         v.hide();
19918                     }
19919                     break;
19920             }
19921         });
19922         
19923         Roo.each(this.picker().select('.next', true).elements, function(v){
19924             v.show();
19925             switch (this.viewMode) {
19926                 case 0:
19927
19928                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19929                         v.hide();
19930                     }
19931                     break;
19932                 case 1:
19933                 case 2:
19934                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19935                         v.hide();
19936                     }
19937                     break;
19938             }
19939         })
19940     },
19941     
19942     moveMonth: function(date, dir)
19943     {
19944         if (!dir) {
19945             return date;
19946         }
19947         var new_date = new Date(date.valueOf()),
19948         day = new_date.getUTCDate(),
19949         month = new_date.getUTCMonth(),
19950         mag = Math.abs(dir),
19951         new_month, test;
19952         dir = dir > 0 ? 1 : -1;
19953         if (mag == 1){
19954             test = dir == -1
19955             // If going back one month, make sure month is not current month
19956             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19957             ? function(){
19958                 return new_date.getUTCMonth() == month;
19959             }
19960             // If going forward one month, make sure month is as expected
19961             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19962             : function(){
19963                 return new_date.getUTCMonth() != new_month;
19964             };
19965             new_month = month + dir;
19966             new_date.setUTCMonth(new_month);
19967             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19968             if (new_month < 0 || new_month > 11) {
19969                 new_month = (new_month + 12) % 12;
19970             }
19971         } else {
19972             // For magnitudes >1, move one month at a time...
19973             for (var i=0; i<mag; i++) {
19974                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19975                 new_date = this.moveMonth(new_date, dir);
19976             }
19977             // ...then reset the day, keeping it in the new month
19978             new_month = new_date.getUTCMonth();
19979             new_date.setUTCDate(day);
19980             test = function(){
19981                 return new_month != new_date.getUTCMonth();
19982             };
19983         }
19984         // Common date-resetting loop -- if date is beyond end of month, make it
19985         // end of month
19986         while (test()){
19987             new_date.setUTCDate(--day);
19988             new_date.setUTCMonth(new_month);
19989         }
19990         return new_date;
19991     },
19992
19993     moveYear: function(date, dir)
19994     {
19995         return this.moveMonth(date, dir*12);
19996     },
19997
19998     dateWithinRange: function(date)
19999     {
20000         return date >= this.startDate && date <= this.endDate;
20001     },
20002
20003     
20004     remove: function() 
20005     {
20006         this.picker().remove();
20007     },
20008     
20009     validateValue : function(value)
20010     {
20011         if(this.getVisibilityEl().hasClass('hidden')){
20012             return true;
20013         }
20014         
20015         if(value.length < 1)  {
20016             if(this.allowBlank){
20017                 return true;
20018             }
20019             return false;
20020         }
20021         
20022         if(value.length < this.minLength){
20023             return false;
20024         }
20025         if(value.length > this.maxLength){
20026             return false;
20027         }
20028         if(this.vtype){
20029             var vt = Roo.form.VTypes;
20030             if(!vt[this.vtype](value, this)){
20031                 return false;
20032             }
20033         }
20034         if(typeof this.validator == "function"){
20035             var msg = this.validator(value);
20036             if(msg !== true){
20037                 return false;
20038             }
20039         }
20040         
20041         if(this.regex && !this.regex.test(value)){
20042             return false;
20043         }
20044         
20045         if(typeof(this.parseDate(value)) == 'undefined'){
20046             return false;
20047         }
20048         
20049         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20050             return false;
20051         }      
20052         
20053         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20054             return false;
20055         } 
20056         
20057         
20058         return true;
20059     },
20060     
20061     reset : function()
20062     {
20063         this.date = this.viewDate = '';
20064         
20065         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20066     }
20067    
20068 });
20069
20070 Roo.apply(Roo.bootstrap.DateField,  {
20071     
20072     head : {
20073         tag: 'thead',
20074         cn: [
20075         {
20076             tag: 'tr',
20077             cn: [
20078             {
20079                 tag: 'th',
20080                 cls: 'prev',
20081                 html: '<i class="fa fa-arrow-left"/>'
20082             },
20083             {
20084                 tag: 'th',
20085                 cls: 'switch',
20086                 colspan: '5'
20087             },
20088             {
20089                 tag: 'th',
20090                 cls: 'next',
20091                 html: '<i class="fa fa-arrow-right"/>'
20092             }
20093
20094             ]
20095         }
20096         ]
20097     },
20098     
20099     content : {
20100         tag: 'tbody',
20101         cn: [
20102         {
20103             tag: 'tr',
20104             cn: [
20105             {
20106                 tag: 'td',
20107                 colspan: '7'
20108             }
20109             ]
20110         }
20111         ]
20112     },
20113     
20114     footer : {
20115         tag: 'tfoot',
20116         cn: [
20117         {
20118             tag: 'tr',
20119             cn: [
20120             {
20121                 tag: 'th',
20122                 colspan: '7',
20123                 cls: 'today'
20124             }
20125                     
20126             ]
20127         }
20128         ]
20129     },
20130     
20131     dates:{
20132         en: {
20133             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20134             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20135             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20136             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20137             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20138             today: "Today"
20139         }
20140     },
20141     
20142     modes: [
20143     {
20144         clsName: 'days',
20145         navFnc: 'Month',
20146         navStep: 1
20147     },
20148     {
20149         clsName: 'months',
20150         navFnc: 'FullYear',
20151         navStep: 1
20152     },
20153     {
20154         clsName: 'years',
20155         navFnc: 'FullYear',
20156         navStep: 10
20157     }]
20158 });
20159
20160 Roo.apply(Roo.bootstrap.DateField,  {
20161   
20162     template : {
20163         tag: 'div',
20164         cls: 'datepicker dropdown-menu roo-dynamic',
20165         cn: [
20166         {
20167             tag: 'div',
20168             cls: 'datepicker-days',
20169             cn: [
20170             {
20171                 tag: 'table',
20172                 cls: 'table-condensed',
20173                 cn:[
20174                 Roo.bootstrap.DateField.head,
20175                 {
20176                     tag: 'tbody'
20177                 },
20178                 Roo.bootstrap.DateField.footer
20179                 ]
20180             }
20181             ]
20182         },
20183         {
20184             tag: 'div',
20185             cls: 'datepicker-months',
20186             cn: [
20187             {
20188                 tag: 'table',
20189                 cls: 'table-condensed',
20190                 cn:[
20191                 Roo.bootstrap.DateField.head,
20192                 Roo.bootstrap.DateField.content,
20193                 Roo.bootstrap.DateField.footer
20194                 ]
20195             }
20196             ]
20197         },
20198         {
20199             tag: 'div',
20200             cls: 'datepicker-years',
20201             cn: [
20202             {
20203                 tag: 'table',
20204                 cls: 'table-condensed',
20205                 cn:[
20206                 Roo.bootstrap.DateField.head,
20207                 Roo.bootstrap.DateField.content,
20208                 Roo.bootstrap.DateField.footer
20209                 ]
20210             }
20211             ]
20212         }
20213         ]
20214     }
20215 });
20216
20217  
20218
20219  /*
20220  * - LGPL
20221  *
20222  * TimeField
20223  * 
20224  */
20225
20226 /**
20227  * @class Roo.bootstrap.TimeField
20228  * @extends Roo.bootstrap.Input
20229  * Bootstrap DateField class
20230  * 
20231  * 
20232  * @constructor
20233  * Create a new TimeField
20234  * @param {Object} config The config object
20235  */
20236
20237 Roo.bootstrap.TimeField = function(config){
20238     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20239     this.addEvents({
20240             /**
20241              * @event show
20242              * Fires when this field show.
20243              * @param {Roo.bootstrap.DateField} thisthis
20244              * @param {Mixed} date The date value
20245              */
20246             show : true,
20247             /**
20248              * @event show
20249              * Fires when this field hide.
20250              * @param {Roo.bootstrap.DateField} this
20251              * @param {Mixed} date The date value
20252              */
20253             hide : true,
20254             /**
20255              * @event select
20256              * Fires when select a date.
20257              * @param {Roo.bootstrap.DateField} this
20258              * @param {Mixed} date The date value
20259              */
20260             select : true
20261         });
20262 };
20263
20264 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20265     
20266     /**
20267      * @cfg {String} format
20268      * The default time format string which can be overriden for localization support.  The format must be
20269      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20270      */
20271     format : "H:i",
20272        
20273     onRender: function(ct, position)
20274     {
20275         
20276         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20277                 
20278         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20279         
20280         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20281         
20282         this.pop = this.picker().select('>.datepicker-time',true).first();
20283         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20284         
20285         this.picker().on('mousedown', this.onMousedown, this);
20286         this.picker().on('click', this.onClick, this);
20287         
20288         this.picker().addClass('datepicker-dropdown');
20289     
20290         this.fillTime();
20291         this.update();
20292             
20293         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20294         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20295         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20296         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20297         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20298         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20299
20300     },
20301     
20302     fireKey: function(e){
20303         if (!this.picker().isVisible()){
20304             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20305                 this.show();
20306             }
20307             return;
20308         }
20309
20310         e.preventDefault();
20311         
20312         switch(e.keyCode){
20313             case 27: // escape
20314                 this.hide();
20315                 break;
20316             case 37: // left
20317             case 39: // right
20318                 this.onTogglePeriod();
20319                 break;
20320             case 38: // up
20321                 this.onIncrementMinutes();
20322                 break;
20323             case 40: // down
20324                 this.onDecrementMinutes();
20325                 break;
20326             case 13: // enter
20327             case 9: // tab
20328                 this.setTime();
20329                 break;
20330         }
20331     },
20332     
20333     onClick: function(e) {
20334         e.stopPropagation();
20335         e.preventDefault();
20336     },
20337     
20338     picker : function()
20339     {
20340         return this.el.select('.datepicker', true).first();
20341     },
20342     
20343     fillTime: function()
20344     {    
20345         var time = this.pop.select('tbody', true).first();
20346         
20347         time.dom.innerHTML = '';
20348         
20349         time.createChild({
20350             tag: 'tr',
20351             cn: [
20352                 {
20353                     tag: 'td',
20354                     cn: [
20355                         {
20356                             tag: 'a',
20357                             href: '#',
20358                             cls: 'btn',
20359                             cn: [
20360                                 {
20361                                     tag: 'span',
20362                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20363                                 }
20364                             ]
20365                         } 
20366                     ]
20367                 },
20368                 {
20369                     tag: 'td',
20370                     cls: 'separator'
20371                 },
20372                 {
20373                     tag: 'td',
20374                     cn: [
20375                         {
20376                             tag: 'a',
20377                             href: '#',
20378                             cls: 'btn',
20379                             cn: [
20380                                 {
20381                                     tag: 'span',
20382                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20383                                 }
20384                             ]
20385                         }
20386                     ]
20387                 },
20388                 {
20389                     tag: 'td',
20390                     cls: 'separator'
20391                 }
20392             ]
20393         });
20394         
20395         time.createChild({
20396             tag: 'tr',
20397             cn: [
20398                 {
20399                     tag: 'td',
20400                     cn: [
20401                         {
20402                             tag: 'span',
20403                             cls: 'timepicker-hour',
20404                             html: '00'
20405                         }  
20406                     ]
20407                 },
20408                 {
20409                     tag: 'td',
20410                     cls: 'separator',
20411                     html: ':'
20412                 },
20413                 {
20414                     tag: 'td',
20415                     cn: [
20416                         {
20417                             tag: 'span',
20418                             cls: 'timepicker-minute',
20419                             html: '00'
20420                         }  
20421                     ]
20422                 },
20423                 {
20424                     tag: 'td',
20425                     cls: 'separator'
20426                 },
20427                 {
20428                     tag: 'td',
20429                     cn: [
20430                         {
20431                             tag: 'button',
20432                             type: 'button',
20433                             cls: 'btn btn-primary period',
20434                             html: 'AM'
20435                             
20436                         }
20437                     ]
20438                 }
20439             ]
20440         });
20441         
20442         time.createChild({
20443             tag: 'tr',
20444             cn: [
20445                 {
20446                     tag: 'td',
20447                     cn: [
20448                         {
20449                             tag: 'a',
20450                             href: '#',
20451                             cls: 'btn',
20452                             cn: [
20453                                 {
20454                                     tag: 'span',
20455                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20456                                 }
20457                             ]
20458                         }
20459                     ]
20460                 },
20461                 {
20462                     tag: 'td',
20463                     cls: 'separator'
20464                 },
20465                 {
20466                     tag: 'td',
20467                     cn: [
20468                         {
20469                             tag: 'a',
20470                             href: '#',
20471                             cls: 'btn',
20472                             cn: [
20473                                 {
20474                                     tag: 'span',
20475                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20476                                 }
20477                             ]
20478                         }
20479                     ]
20480                 },
20481                 {
20482                     tag: 'td',
20483                     cls: 'separator'
20484                 }
20485             ]
20486         });
20487         
20488     },
20489     
20490     update: function()
20491     {
20492         
20493         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20494         
20495         this.fill();
20496     },
20497     
20498     fill: function() 
20499     {
20500         var hours = this.time.getHours();
20501         var minutes = this.time.getMinutes();
20502         var period = 'AM';
20503         
20504         if(hours > 11){
20505             period = 'PM';
20506         }
20507         
20508         if(hours == 0){
20509             hours = 12;
20510         }
20511         
20512         
20513         if(hours > 12){
20514             hours = hours - 12;
20515         }
20516         
20517         if(hours < 10){
20518             hours = '0' + hours;
20519         }
20520         
20521         if(minutes < 10){
20522             minutes = '0' + minutes;
20523         }
20524         
20525         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20526         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20527         this.pop.select('button', true).first().dom.innerHTML = period;
20528         
20529     },
20530     
20531     place: function()
20532     {   
20533         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20534         
20535         var cls = ['bottom'];
20536         
20537         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20538             cls.pop();
20539             cls.push('top');
20540         }
20541         
20542         cls.push('right');
20543         
20544         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20545             cls.pop();
20546             cls.push('left');
20547         }
20548         
20549         this.picker().addClass(cls.join('-'));
20550         
20551         var _this = this;
20552         
20553         Roo.each(cls, function(c){
20554             if(c == 'bottom'){
20555                 _this.picker().setTop(_this.inputEl().getHeight());
20556                 return;
20557             }
20558             if(c == 'top'){
20559                 _this.picker().setTop(0 - _this.picker().getHeight());
20560                 return;
20561             }
20562             
20563             if(c == 'left'){
20564                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20565                 return;
20566             }
20567             if(c == 'right'){
20568                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20569                 return;
20570             }
20571         });
20572         
20573     },
20574   
20575     onFocus : function()
20576     {
20577         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20578         this.show();
20579     },
20580     
20581     onBlur : function()
20582     {
20583         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20584         this.hide();
20585     },
20586     
20587     show : function()
20588     {
20589         this.picker().show();
20590         this.pop.show();
20591         this.update();
20592         this.place();
20593         
20594         this.fireEvent('show', this, this.date);
20595     },
20596     
20597     hide : function()
20598     {
20599         this.picker().hide();
20600         this.pop.hide();
20601         
20602         this.fireEvent('hide', this, this.date);
20603     },
20604     
20605     setTime : function()
20606     {
20607         this.hide();
20608         this.setValue(this.time.format(this.format));
20609         
20610         this.fireEvent('select', this, this.date);
20611         
20612         
20613     },
20614     
20615     onMousedown: function(e){
20616         e.stopPropagation();
20617         e.preventDefault();
20618     },
20619     
20620     onIncrementHours: function()
20621     {
20622         Roo.log('onIncrementHours');
20623         this.time = this.time.add(Date.HOUR, 1);
20624         this.update();
20625         
20626     },
20627     
20628     onDecrementHours: function()
20629     {
20630         Roo.log('onDecrementHours');
20631         this.time = this.time.add(Date.HOUR, -1);
20632         this.update();
20633     },
20634     
20635     onIncrementMinutes: function()
20636     {
20637         Roo.log('onIncrementMinutes');
20638         this.time = this.time.add(Date.MINUTE, 1);
20639         this.update();
20640     },
20641     
20642     onDecrementMinutes: function()
20643     {
20644         Roo.log('onDecrementMinutes');
20645         this.time = this.time.add(Date.MINUTE, -1);
20646         this.update();
20647     },
20648     
20649     onTogglePeriod: function()
20650     {
20651         Roo.log('onTogglePeriod');
20652         this.time = this.time.add(Date.HOUR, 12);
20653         this.update();
20654     }
20655     
20656    
20657 });
20658
20659 Roo.apply(Roo.bootstrap.TimeField,  {
20660     
20661     content : {
20662         tag: 'tbody',
20663         cn: [
20664             {
20665                 tag: 'tr',
20666                 cn: [
20667                 {
20668                     tag: 'td',
20669                     colspan: '7'
20670                 }
20671                 ]
20672             }
20673         ]
20674     },
20675     
20676     footer : {
20677         tag: 'tfoot',
20678         cn: [
20679             {
20680                 tag: 'tr',
20681                 cn: [
20682                 {
20683                     tag: 'th',
20684                     colspan: '7',
20685                     cls: '',
20686                     cn: [
20687                         {
20688                             tag: 'button',
20689                             cls: 'btn btn-info ok',
20690                             html: 'OK'
20691                         }
20692                     ]
20693                 }
20694
20695                 ]
20696             }
20697         ]
20698     }
20699 });
20700
20701 Roo.apply(Roo.bootstrap.TimeField,  {
20702   
20703     template : {
20704         tag: 'div',
20705         cls: 'datepicker dropdown-menu',
20706         cn: [
20707             {
20708                 tag: 'div',
20709                 cls: 'datepicker-time',
20710                 cn: [
20711                 {
20712                     tag: 'table',
20713                     cls: 'table-condensed',
20714                     cn:[
20715                     Roo.bootstrap.TimeField.content,
20716                     Roo.bootstrap.TimeField.footer
20717                     ]
20718                 }
20719                 ]
20720             }
20721         ]
20722     }
20723 });
20724
20725  
20726
20727  /*
20728  * - LGPL
20729  *
20730  * MonthField
20731  * 
20732  */
20733
20734 /**
20735  * @class Roo.bootstrap.MonthField
20736  * @extends Roo.bootstrap.Input
20737  * Bootstrap MonthField class
20738  * 
20739  * @cfg {String} language default en
20740  * 
20741  * @constructor
20742  * Create a new MonthField
20743  * @param {Object} config The config object
20744  */
20745
20746 Roo.bootstrap.MonthField = function(config){
20747     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20748     
20749     this.addEvents({
20750         /**
20751          * @event show
20752          * Fires when this field show.
20753          * @param {Roo.bootstrap.MonthField} this
20754          * @param {Mixed} date The date value
20755          */
20756         show : true,
20757         /**
20758          * @event show
20759          * Fires when this field hide.
20760          * @param {Roo.bootstrap.MonthField} this
20761          * @param {Mixed} date The date value
20762          */
20763         hide : true,
20764         /**
20765          * @event select
20766          * Fires when select a date.
20767          * @param {Roo.bootstrap.MonthField} this
20768          * @param {String} oldvalue The old value
20769          * @param {String} newvalue The new value
20770          */
20771         select : true
20772     });
20773 };
20774
20775 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20776     
20777     onRender: function(ct, position)
20778     {
20779         
20780         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20781         
20782         this.language = this.language || 'en';
20783         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20784         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20785         
20786         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20787         this.isInline = false;
20788         this.isInput = true;
20789         this.component = this.el.select('.add-on', true).first() || false;
20790         this.component = (this.component && this.component.length === 0) ? false : this.component;
20791         this.hasInput = this.component && this.inputEL().length;
20792         
20793         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20794         
20795         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20796         
20797         this.picker().on('mousedown', this.onMousedown, this);
20798         this.picker().on('click', this.onClick, this);
20799         
20800         this.picker().addClass('datepicker-dropdown');
20801         
20802         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20803             v.setStyle('width', '189px');
20804         });
20805         
20806         this.fillMonths();
20807         
20808         this.update();
20809         
20810         if(this.isInline) {
20811             this.show();
20812         }
20813         
20814     },
20815     
20816     setValue: function(v, suppressEvent)
20817     {   
20818         var o = this.getValue();
20819         
20820         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20821         
20822         this.update();
20823
20824         if(suppressEvent !== true){
20825             this.fireEvent('select', this, o, v);
20826         }
20827         
20828     },
20829     
20830     getValue: function()
20831     {
20832         return this.value;
20833     },
20834     
20835     onClick: function(e) 
20836     {
20837         e.stopPropagation();
20838         e.preventDefault();
20839         
20840         var target = e.getTarget();
20841         
20842         if(target.nodeName.toLowerCase() === 'i'){
20843             target = Roo.get(target).dom.parentNode;
20844         }
20845         
20846         var nodeName = target.nodeName;
20847         var className = target.className;
20848         var html = target.innerHTML;
20849         
20850         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20851             return;
20852         }
20853         
20854         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20855         
20856         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20857         
20858         this.hide();
20859                         
20860     },
20861     
20862     picker : function()
20863     {
20864         return this.pickerEl;
20865     },
20866     
20867     fillMonths: function()
20868     {    
20869         var i = 0;
20870         var months = this.picker().select('>.datepicker-months td', true).first();
20871         
20872         months.dom.innerHTML = '';
20873         
20874         while (i < 12) {
20875             var month = {
20876                 tag: 'span',
20877                 cls: 'month',
20878                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20879             };
20880             
20881             months.createChild(month);
20882         }
20883         
20884     },
20885     
20886     update: function()
20887     {
20888         var _this = this;
20889         
20890         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20891             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20892         }
20893         
20894         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20895             e.removeClass('active');
20896             
20897             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20898                 e.addClass('active');
20899             }
20900         })
20901     },
20902     
20903     place: function()
20904     {
20905         if(this.isInline) {
20906             return;
20907         }
20908         
20909         this.picker().removeClass(['bottom', 'top']);
20910         
20911         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20912             /*
20913              * place to the top of element!
20914              *
20915              */
20916             
20917             this.picker().addClass('top');
20918             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20919             
20920             return;
20921         }
20922         
20923         this.picker().addClass('bottom');
20924         
20925         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20926     },
20927     
20928     onFocus : function()
20929     {
20930         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20931         this.show();
20932     },
20933     
20934     onBlur : function()
20935     {
20936         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20937         
20938         var d = this.inputEl().getValue();
20939         
20940         this.setValue(d);
20941                 
20942         this.hide();
20943     },
20944     
20945     show : function()
20946     {
20947         this.picker().show();
20948         this.picker().select('>.datepicker-months', true).first().show();
20949         this.update();
20950         this.place();
20951         
20952         this.fireEvent('show', this, this.date);
20953     },
20954     
20955     hide : function()
20956     {
20957         if(this.isInline) {
20958             return;
20959         }
20960         this.picker().hide();
20961         this.fireEvent('hide', this, this.date);
20962         
20963     },
20964     
20965     onMousedown: function(e)
20966     {
20967         e.stopPropagation();
20968         e.preventDefault();
20969     },
20970     
20971     keyup: function(e)
20972     {
20973         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20974         this.update();
20975     },
20976
20977     fireKey: function(e)
20978     {
20979         if (!this.picker().isVisible()){
20980             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20981                 this.show();
20982             }
20983             return;
20984         }
20985         
20986         var dir;
20987         
20988         switch(e.keyCode){
20989             case 27: // escape
20990                 this.hide();
20991                 e.preventDefault();
20992                 break;
20993             case 37: // left
20994             case 39: // right
20995                 dir = e.keyCode == 37 ? -1 : 1;
20996                 
20997                 this.vIndex = this.vIndex + dir;
20998                 
20999                 if(this.vIndex < 0){
21000                     this.vIndex = 0;
21001                 }
21002                 
21003                 if(this.vIndex > 11){
21004                     this.vIndex = 11;
21005                 }
21006                 
21007                 if(isNaN(this.vIndex)){
21008                     this.vIndex = 0;
21009                 }
21010                 
21011                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21012                 
21013                 break;
21014             case 38: // up
21015             case 40: // down
21016                 
21017                 dir = e.keyCode == 38 ? -1 : 1;
21018                 
21019                 this.vIndex = this.vIndex + dir * 4;
21020                 
21021                 if(this.vIndex < 0){
21022                     this.vIndex = 0;
21023                 }
21024                 
21025                 if(this.vIndex > 11){
21026                     this.vIndex = 11;
21027                 }
21028                 
21029                 if(isNaN(this.vIndex)){
21030                     this.vIndex = 0;
21031                 }
21032                 
21033                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21034                 break;
21035                 
21036             case 13: // enter
21037                 
21038                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21039                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21040                 }
21041                 
21042                 this.hide();
21043                 e.preventDefault();
21044                 break;
21045             case 9: // tab
21046                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21047                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21048                 }
21049                 this.hide();
21050                 break;
21051             case 16: // shift
21052             case 17: // ctrl
21053             case 18: // alt
21054                 break;
21055             default :
21056                 this.hide();
21057                 
21058         }
21059     },
21060     
21061     remove: function() 
21062     {
21063         this.picker().remove();
21064     }
21065    
21066 });
21067
21068 Roo.apply(Roo.bootstrap.MonthField,  {
21069     
21070     content : {
21071         tag: 'tbody',
21072         cn: [
21073         {
21074             tag: 'tr',
21075             cn: [
21076             {
21077                 tag: 'td',
21078                 colspan: '7'
21079             }
21080             ]
21081         }
21082         ]
21083     },
21084     
21085     dates:{
21086         en: {
21087             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21088             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21089         }
21090     }
21091 });
21092
21093 Roo.apply(Roo.bootstrap.MonthField,  {
21094   
21095     template : {
21096         tag: 'div',
21097         cls: 'datepicker dropdown-menu roo-dynamic',
21098         cn: [
21099             {
21100                 tag: 'div',
21101                 cls: 'datepicker-months',
21102                 cn: [
21103                 {
21104                     tag: 'table',
21105                     cls: 'table-condensed',
21106                     cn:[
21107                         Roo.bootstrap.DateField.content
21108                     ]
21109                 }
21110                 ]
21111             }
21112         ]
21113     }
21114 });
21115
21116  
21117
21118  
21119  /*
21120  * - LGPL
21121  *
21122  * CheckBox
21123  * 
21124  */
21125
21126 /**
21127  * @class Roo.bootstrap.CheckBox
21128  * @extends Roo.bootstrap.Input
21129  * Bootstrap CheckBox class
21130  * 
21131  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21132  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21133  * @cfg {String} boxLabel The text that appears beside the checkbox
21134  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21135  * @cfg {Boolean} checked initnal the element
21136  * @cfg {Boolean} inline inline the element (default false)
21137  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21138  * @cfg {String} tooltip label tooltip
21139  * 
21140  * @constructor
21141  * Create a new CheckBox
21142  * @param {Object} config The config object
21143  */
21144
21145 Roo.bootstrap.CheckBox = function(config){
21146     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21147    
21148     this.addEvents({
21149         /**
21150         * @event check
21151         * Fires when the element is checked or unchecked.
21152         * @param {Roo.bootstrap.CheckBox} this This input
21153         * @param {Boolean} checked The new checked value
21154         */
21155        check : true,
21156        /**
21157         * @event click
21158         * Fires when the element is click.
21159         * @param {Roo.bootstrap.CheckBox} this This input
21160         */
21161        click : true
21162     });
21163     
21164 };
21165
21166 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21167   
21168     inputType: 'checkbox',
21169     inputValue: 1,
21170     valueOff: 0,
21171     boxLabel: false,
21172     checked: false,
21173     weight : false,
21174     inline: false,
21175     tooltip : '',
21176     
21177     // checkbox success does not make any sense really.. 
21178     invalidClass : "",
21179     validClass : "",
21180     
21181     
21182     getAutoCreate : function()
21183     {
21184         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21185         
21186         var id = Roo.id();
21187         
21188         var cfg = {};
21189         
21190         cfg.cls = 'form-group ' + this.inputType; //input-group
21191         
21192         if(this.inline){
21193             cfg.cls += ' ' + this.inputType + '-inline';
21194         }
21195         
21196         var input =  {
21197             tag: 'input',
21198             id : id,
21199             type : this.inputType,
21200             value : this.inputValue,
21201             cls : 'roo-' + this.inputType, //'form-box',
21202             placeholder : this.placeholder || ''
21203             
21204         };
21205         
21206         if(this.inputType != 'radio'){
21207             var hidden =  {
21208                 tag: 'input',
21209                 type : 'hidden',
21210                 cls : 'roo-hidden-value',
21211                 value : this.checked ? this.inputValue : this.valueOff
21212             };
21213         }
21214         
21215             
21216         if (this.weight) { // Validity check?
21217             cfg.cls += " " + this.inputType + "-" + this.weight;
21218         }
21219         
21220         if (this.disabled) {
21221             input.disabled=true;
21222         }
21223         
21224         if(this.checked){
21225             input.checked = this.checked;
21226         }
21227         
21228         if (this.name) {
21229             
21230             input.name = this.name;
21231             
21232             if(this.inputType != 'radio'){
21233                 hidden.name = this.name;
21234                 input.name = '_hidden_' + this.name;
21235             }
21236         }
21237         
21238         if (this.size) {
21239             input.cls += ' input-' + this.size;
21240         }
21241         
21242         var settings=this;
21243         
21244         ['xs','sm','md','lg'].map(function(size){
21245             if (settings[size]) {
21246                 cfg.cls += ' col-' + size + '-' + settings[size];
21247             }
21248         });
21249         
21250         var inputblock = input;
21251          
21252         if (this.before || this.after) {
21253             
21254             inputblock = {
21255                 cls : 'input-group',
21256                 cn :  [] 
21257             };
21258             
21259             if (this.before) {
21260                 inputblock.cn.push({
21261                     tag :'span',
21262                     cls : 'input-group-addon',
21263                     html : this.before
21264                 });
21265             }
21266             
21267             inputblock.cn.push(input);
21268             
21269             if(this.inputType != 'radio'){
21270                 inputblock.cn.push(hidden);
21271             }
21272             
21273             if (this.after) {
21274                 inputblock.cn.push({
21275                     tag :'span',
21276                     cls : 'input-group-addon',
21277                     html : this.after
21278                 });
21279             }
21280             
21281         }
21282         var boxLabelCfg = false;
21283         
21284         if(this.boxLabel){
21285            
21286             boxLabelCfg = {
21287                 tag: 'label',
21288                 //'for': id, // box label is handled by onclick - so no for...
21289                 cls: 'box-label',
21290                 html: this.boxLabel
21291             };
21292             if(this.tooltip){
21293                 boxLabelCfg.tooltip = this.tooltip;
21294             }
21295              
21296         }
21297         
21298         
21299         if (align ==='left' && this.fieldLabel.length) {
21300 //                Roo.log("left and has label");
21301             cfg.cn = [
21302                 {
21303                     tag: 'label',
21304                     'for' :  id,
21305                     cls : 'control-label',
21306                     html : this.fieldLabel
21307                 },
21308                 {
21309                     cls : "", 
21310                     cn: [
21311                         inputblock
21312                     ]
21313                 }
21314             ];
21315             
21316             if (boxLabelCfg) {
21317                 cfg.cn[1].cn.push(boxLabelCfg);
21318             }
21319             
21320             if(this.labelWidth > 12){
21321                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21322             }
21323             
21324             if(this.labelWidth < 13 && this.labelmd == 0){
21325                 this.labelmd = this.labelWidth;
21326             }
21327             
21328             if(this.labellg > 0){
21329                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21330                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21331             }
21332             
21333             if(this.labelmd > 0){
21334                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21335                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21336             }
21337             
21338             if(this.labelsm > 0){
21339                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21340                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21341             }
21342             
21343             if(this.labelxs > 0){
21344                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21345                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21346             }
21347             
21348         } else if ( this.fieldLabel.length) {
21349 //                Roo.log(" label");
21350                 cfg.cn = [
21351                    
21352                     {
21353                         tag: this.boxLabel ? 'span' : 'label',
21354                         'for': id,
21355                         cls: 'control-label box-input-label',
21356                         //cls : 'input-group-addon',
21357                         html : this.fieldLabel
21358                     },
21359                     
21360                     inputblock
21361                     
21362                 ];
21363                 if (boxLabelCfg) {
21364                     cfg.cn.push(boxLabelCfg);
21365                 }
21366
21367         } else {
21368             
21369 //                Roo.log(" no label && no align");
21370                 cfg.cn = [  inputblock ] ;
21371                 if (boxLabelCfg) {
21372                     cfg.cn.push(boxLabelCfg);
21373                 }
21374
21375                 
21376         }
21377         
21378        
21379         
21380         if(this.inputType != 'radio'){
21381             cfg.cn.push(hidden);
21382         }
21383         
21384         return cfg;
21385         
21386     },
21387     
21388     /**
21389      * return the real input element.
21390      */
21391     inputEl: function ()
21392     {
21393         return this.el.select('input.roo-' + this.inputType,true).first();
21394     },
21395     hiddenEl: function ()
21396     {
21397         return this.el.select('input.roo-hidden-value',true).first();
21398     },
21399     
21400     labelEl: function()
21401     {
21402         return this.el.select('label.control-label',true).first();
21403     },
21404     /* depricated... */
21405     
21406     label: function()
21407     {
21408         return this.labelEl();
21409     },
21410     
21411     boxLabelEl: function()
21412     {
21413         return this.el.select('label.box-label',true).first();
21414     },
21415     
21416     initEvents : function()
21417     {
21418 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21419         
21420         this.inputEl().on('click', this.onClick,  this);
21421         
21422         if (this.boxLabel) { 
21423             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21424         }
21425         
21426         this.startValue = this.getValue();
21427         
21428         if(this.groupId){
21429             Roo.bootstrap.CheckBox.register(this);
21430         }
21431     },
21432     
21433     onClick : function(e)
21434     {   
21435         if(this.fireEvent('click', this, e) !== false){
21436             this.setChecked(!this.checked);
21437         }
21438         
21439     },
21440     
21441     setChecked : function(state,suppressEvent)
21442     {
21443         this.startValue = this.getValue();
21444
21445         if(this.inputType == 'radio'){
21446             
21447             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21448                 e.dom.checked = false;
21449             });
21450             
21451             this.inputEl().dom.checked = true;
21452             
21453             this.inputEl().dom.value = this.inputValue;
21454             
21455             if(suppressEvent !== true){
21456                 this.fireEvent('check', this, true);
21457             }
21458             
21459             this.validate();
21460             
21461             return;
21462         }
21463         
21464         this.checked = state;
21465         
21466         this.inputEl().dom.checked = state;
21467         
21468         
21469         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21470         
21471         if(suppressEvent !== true){
21472             this.fireEvent('check', this, state);
21473         }
21474         
21475         this.validate();
21476     },
21477     
21478     getValue : function()
21479     {
21480         if(this.inputType == 'radio'){
21481             return this.getGroupValue();
21482         }
21483         
21484         return this.hiddenEl().dom.value;
21485         
21486     },
21487     
21488     getGroupValue : function()
21489     {
21490         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21491             return '';
21492         }
21493         
21494         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21495     },
21496     
21497     setValue : function(v,suppressEvent)
21498     {
21499         if(this.inputType == 'radio'){
21500             this.setGroupValue(v, suppressEvent);
21501             return;
21502         }
21503         
21504         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21505         
21506         this.validate();
21507     },
21508     
21509     setGroupValue : function(v, suppressEvent)
21510     {
21511         this.startValue = this.getValue();
21512         
21513         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21514             e.dom.checked = false;
21515             
21516             if(e.dom.value == v){
21517                 e.dom.checked = true;
21518             }
21519         });
21520         
21521         if(suppressEvent !== true){
21522             this.fireEvent('check', this, true);
21523         }
21524
21525         this.validate();
21526         
21527         return;
21528     },
21529     
21530     validate : function()
21531     {
21532         if(this.getVisibilityEl().hasClass('hidden')){
21533             return true;
21534         }
21535         
21536         if(
21537                 this.disabled || 
21538                 (this.inputType == 'radio' && this.validateRadio()) ||
21539                 (this.inputType == 'checkbox' && this.validateCheckbox())
21540         ){
21541             this.markValid();
21542             return true;
21543         }
21544         
21545         this.markInvalid();
21546         return false;
21547     },
21548     
21549     validateRadio : function()
21550     {
21551         if(this.getVisibilityEl().hasClass('hidden')){
21552             return true;
21553         }
21554         
21555         if(this.allowBlank){
21556             return true;
21557         }
21558         
21559         var valid = false;
21560         
21561         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21562             if(!e.dom.checked){
21563                 return;
21564             }
21565             
21566             valid = true;
21567             
21568             return false;
21569         });
21570         
21571         return valid;
21572     },
21573     
21574     validateCheckbox : function()
21575     {
21576         if(!this.groupId){
21577             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21578             //return (this.getValue() == this.inputValue) ? true : false;
21579         }
21580         
21581         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21582         
21583         if(!group){
21584             return false;
21585         }
21586         
21587         var r = false;
21588         
21589         for(var i in group){
21590             if(group[i].el.isVisible(true)){
21591                 r = false;
21592                 break;
21593             }
21594             
21595             r = true;
21596         }
21597         
21598         for(var i in group){
21599             if(r){
21600                 break;
21601             }
21602             
21603             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21604         }
21605         
21606         return r;
21607     },
21608     
21609     /**
21610      * Mark this field as valid
21611      */
21612     markValid : function()
21613     {
21614         var _this = this;
21615         
21616         this.fireEvent('valid', this);
21617         
21618         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21619         
21620         if(this.groupId){
21621             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21622         }
21623         
21624         if(label){
21625             label.markValid();
21626         }
21627
21628         if(this.inputType == 'radio'){
21629             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21630                 var fg = e.findParent('.form-group', false, true);
21631                 if (Roo.bootstrap.version == 3) {
21632                     fg.removeClass([_this.invalidClass, _this.validClass]);
21633                     fg.addClass(_this.validClass);
21634                 } else {
21635                     fg.removeClass(['is-valid', 'is-invalid']);
21636                     fg.addClass('is-valid');
21637                 }
21638             });
21639             
21640             return;
21641         }
21642
21643         if(!this.groupId){
21644             var fg = this.el.findParent('.form-group', false, true);
21645             if (Roo.bootstrap.version == 3) {
21646                 fg.removeClass([this.invalidClass, this.validClass]);
21647                 fg.addClass(this.validClass);
21648             } else {
21649                 fg.removeClass(['is-valid', 'is-invalid']);
21650                 fg.addClass('is-valid');
21651             }
21652             return;
21653         }
21654         
21655         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21656         
21657         if(!group){
21658             return;
21659         }
21660         
21661         for(var i in group){
21662             var fg = group[i].el.findParent('.form-group', false, true);
21663             if (Roo.bootstrap.version == 3) {
21664                 fg.removeClass([this.invalidClass, this.validClass]);
21665                 fg.addClass(this.validClass);
21666             } else {
21667                 fg.removeClass(['is-valid', 'is-invalid']);
21668                 fg.addClass('is-valid');
21669             }
21670         }
21671     },
21672     
21673      /**
21674      * Mark this field as invalid
21675      * @param {String} msg The validation message
21676      */
21677     markInvalid : function(msg)
21678     {
21679         if(this.allowBlank){
21680             return;
21681         }
21682         
21683         var _this = this;
21684         
21685         this.fireEvent('invalid', this, msg);
21686         
21687         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21688         
21689         if(this.groupId){
21690             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21691         }
21692         
21693         if(label){
21694             label.markInvalid();
21695         }
21696             
21697         if(this.inputType == 'radio'){
21698             
21699             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21700                 var fg = e.findParent('.form-group', false, true);
21701                 if (Roo.bootstrap.version == 3) {
21702                     fg.removeClass([_this.invalidClass, _this.validClass]);
21703                     fg.addClass(_this.invalidClass);
21704                 } else {
21705                     fg.removeClass(['is-invalid', 'is-valid']);
21706                     fg.addClass('is-invalid');
21707                 }
21708             });
21709             
21710             return;
21711         }
21712         
21713         if(!this.groupId){
21714             var fg = this.el.findParent('.form-group', false, true);
21715             if (Roo.bootstrap.version == 3) {
21716                 fg.removeClass([_this.invalidClass, _this.validClass]);
21717                 fg.addClass(_this.invalidClass);
21718             } else {
21719                 fg.removeClass(['is-invalid', 'is-valid']);
21720                 fg.addClass('is-invalid');
21721             }
21722             return;
21723         }
21724         
21725         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21726         
21727         if(!group){
21728             return;
21729         }
21730         
21731         for(var i in group){
21732             var fg = group[i].el.findParent('.form-group', false, true);
21733             if (Roo.bootstrap.version == 3) {
21734                 fg.removeClass([_this.invalidClass, _this.validClass]);
21735                 fg.addClass(_this.invalidClass);
21736             } else {
21737                 fg.removeClass(['is-invalid', 'is-valid']);
21738                 fg.addClass('is-invalid');
21739             }
21740         }
21741         
21742     },
21743     
21744     clearInvalid : function()
21745     {
21746         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21747         
21748         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21749         
21750         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21751         
21752         if (label && label.iconEl) {
21753             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21754             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21755         }
21756     },
21757     
21758     disable : function()
21759     {
21760         if(this.inputType != 'radio'){
21761             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21762             return;
21763         }
21764         
21765         var _this = this;
21766         
21767         if(this.rendered){
21768             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21769                 _this.getActionEl().addClass(this.disabledClass);
21770                 e.dom.disabled = true;
21771             });
21772         }
21773         
21774         this.disabled = true;
21775         this.fireEvent("disable", this);
21776         return this;
21777     },
21778
21779     enable : function()
21780     {
21781         if(this.inputType != 'radio'){
21782             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21783             return;
21784         }
21785         
21786         var _this = this;
21787         
21788         if(this.rendered){
21789             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21790                 _this.getActionEl().removeClass(this.disabledClass);
21791                 e.dom.disabled = false;
21792             });
21793         }
21794         
21795         this.disabled = false;
21796         this.fireEvent("enable", this);
21797         return this;
21798     },
21799     
21800     setBoxLabel : function(v)
21801     {
21802         this.boxLabel = v;
21803         
21804         if(this.rendered){
21805             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21806         }
21807     }
21808
21809 });
21810
21811 Roo.apply(Roo.bootstrap.CheckBox, {
21812     
21813     groups: {},
21814     
21815      /**
21816     * register a CheckBox Group
21817     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21818     */
21819     register : function(checkbox)
21820     {
21821         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21822             this.groups[checkbox.groupId] = {};
21823         }
21824         
21825         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21826             return;
21827         }
21828         
21829         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21830         
21831     },
21832     /**
21833     * fetch a CheckBox Group based on the group ID
21834     * @param {string} the group ID
21835     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21836     */
21837     get: function(groupId) {
21838         if (typeof(this.groups[groupId]) == 'undefined') {
21839             return false;
21840         }
21841         
21842         return this.groups[groupId] ;
21843     }
21844     
21845     
21846 });
21847 /*
21848  * - LGPL
21849  *
21850  * RadioItem
21851  * 
21852  */
21853
21854 /**
21855  * @class Roo.bootstrap.Radio
21856  * @extends Roo.bootstrap.Component
21857  * Bootstrap Radio class
21858  * @cfg {String} boxLabel - the label associated
21859  * @cfg {String} value - the value of radio
21860  * 
21861  * @constructor
21862  * Create a new Radio
21863  * @param {Object} config The config object
21864  */
21865 Roo.bootstrap.Radio = function(config){
21866     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21867     
21868 };
21869
21870 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21871     
21872     boxLabel : '',
21873     
21874     value : '',
21875     
21876     getAutoCreate : function()
21877     {
21878         var cfg = {
21879             tag : 'div',
21880             cls : 'form-group radio',
21881             cn : [
21882                 {
21883                     tag : 'label',
21884                     cls : 'box-label',
21885                     html : this.boxLabel
21886                 }
21887             ]
21888         };
21889         
21890         return cfg;
21891     },
21892     
21893     initEvents : function() 
21894     {
21895         this.parent().register(this);
21896         
21897         this.el.on('click', this.onClick, this);
21898         
21899     },
21900     
21901     onClick : function(e)
21902     {
21903         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21904             this.setChecked(true);
21905         }
21906     },
21907     
21908     setChecked : function(state, suppressEvent)
21909     {
21910         this.parent().setValue(this.value, suppressEvent);
21911         
21912     },
21913     
21914     setBoxLabel : function(v)
21915     {
21916         this.boxLabel = v;
21917         
21918         if(this.rendered){
21919             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21920         }
21921     }
21922     
21923 });
21924  
21925
21926  /*
21927  * - LGPL
21928  *
21929  * Input
21930  * 
21931  */
21932
21933 /**
21934  * @class Roo.bootstrap.SecurePass
21935  * @extends Roo.bootstrap.Input
21936  * Bootstrap SecurePass class
21937  *
21938  * 
21939  * @constructor
21940  * Create a new SecurePass
21941  * @param {Object} config The config object
21942  */
21943  
21944 Roo.bootstrap.SecurePass = function (config) {
21945     // these go here, so the translation tool can replace them..
21946     this.errors = {
21947         PwdEmpty: "Please type a password, and then retype it to confirm.",
21948         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21949         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21950         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21951         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21952         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21953         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21954         TooWeak: "Your password is Too Weak."
21955     },
21956     this.meterLabel = "Password strength:";
21957     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21958     this.meterClass = [
21959         "roo-password-meter-tooweak", 
21960         "roo-password-meter-weak", 
21961         "roo-password-meter-medium", 
21962         "roo-password-meter-strong", 
21963         "roo-password-meter-grey"
21964     ];
21965     
21966     this.errors = {};
21967     
21968     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21969 }
21970
21971 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21972     /**
21973      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21974      * {
21975      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21976      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21977      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21978      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21979      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21980      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21981      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21982      * })
21983      */
21984     // private
21985     
21986     meterWidth: 300,
21987     errorMsg :'',    
21988     errors: false,
21989     imageRoot: '/',
21990     /**
21991      * @cfg {String/Object} Label for the strength meter (defaults to
21992      * 'Password strength:')
21993      */
21994     // private
21995     meterLabel: '',
21996     /**
21997      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21998      * ['Weak', 'Medium', 'Strong'])
21999      */
22000     // private    
22001     pwdStrengths: false,    
22002     // private
22003     strength: 0,
22004     // private
22005     _lastPwd: null,
22006     // private
22007     kCapitalLetter: 0,
22008     kSmallLetter: 1,
22009     kDigit: 2,
22010     kPunctuation: 3,
22011     
22012     insecure: false,
22013     // private
22014     initEvents: function ()
22015     {
22016         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22017
22018         if (this.el.is('input[type=password]') && Roo.isSafari) {
22019             this.el.on('keydown', this.SafariOnKeyDown, this);
22020         }
22021
22022         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22023     },
22024     // private
22025     onRender: function (ct, position)
22026     {
22027         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22028         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22029         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22030
22031         this.trigger.createChild({
22032                    cn: [
22033                     {
22034                     //id: 'PwdMeter',
22035                     tag: 'div',
22036                     cls: 'roo-password-meter-grey col-xs-12',
22037                     style: {
22038                         //width: 0,
22039                         //width: this.meterWidth + 'px'                                                
22040                         }
22041                     },
22042                     {                            
22043                          cls: 'roo-password-meter-text'                          
22044                     }
22045                 ]            
22046         });
22047
22048          
22049         if (this.hideTrigger) {
22050             this.trigger.setDisplayed(false);
22051         }
22052         this.setSize(this.width || '', this.height || '');
22053     },
22054     // private
22055     onDestroy: function ()
22056     {
22057         if (this.trigger) {
22058             this.trigger.removeAllListeners();
22059             this.trigger.remove();
22060         }
22061         if (this.wrap) {
22062             this.wrap.remove();
22063         }
22064         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22065     },
22066     // private
22067     checkStrength: function ()
22068     {
22069         var pwd = this.inputEl().getValue();
22070         if (pwd == this._lastPwd) {
22071             return;
22072         }
22073
22074         var strength;
22075         if (this.ClientSideStrongPassword(pwd)) {
22076             strength = 3;
22077         } else if (this.ClientSideMediumPassword(pwd)) {
22078             strength = 2;
22079         } else if (this.ClientSideWeakPassword(pwd)) {
22080             strength = 1;
22081         } else {
22082             strength = 0;
22083         }
22084         
22085         Roo.log('strength1: ' + strength);
22086         
22087         //var pm = this.trigger.child('div/div/div').dom;
22088         var pm = this.trigger.child('div/div');
22089         pm.removeClass(this.meterClass);
22090         pm.addClass(this.meterClass[strength]);
22091                 
22092         
22093         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22094                 
22095         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22096         
22097         this._lastPwd = pwd;
22098     },
22099     reset: function ()
22100     {
22101         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22102         
22103         this._lastPwd = '';
22104         
22105         var pm = this.trigger.child('div/div');
22106         pm.removeClass(this.meterClass);
22107         pm.addClass('roo-password-meter-grey');        
22108         
22109         
22110         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22111         
22112         pt.innerHTML = '';
22113         this.inputEl().dom.type='password';
22114     },
22115     // private
22116     validateValue: function (value)
22117     {
22118         
22119         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22120             return false;
22121         }
22122         if (value.length == 0) {
22123             if (this.allowBlank) {
22124                 this.clearInvalid();
22125                 return true;
22126             }
22127
22128             this.markInvalid(this.errors.PwdEmpty);
22129             this.errorMsg = this.errors.PwdEmpty;
22130             return false;
22131         }
22132         
22133         if(this.insecure){
22134             return true;
22135         }
22136         
22137         if ('[\x21-\x7e]*'.match(value)) {
22138             this.markInvalid(this.errors.PwdBadChar);
22139             this.errorMsg = this.errors.PwdBadChar;
22140             return false;
22141         }
22142         if (value.length < 6) {
22143             this.markInvalid(this.errors.PwdShort);
22144             this.errorMsg = this.errors.PwdShort;
22145             return false;
22146         }
22147         if (value.length > 16) {
22148             this.markInvalid(this.errors.PwdLong);
22149             this.errorMsg = this.errors.PwdLong;
22150             return false;
22151         }
22152         var strength;
22153         if (this.ClientSideStrongPassword(value)) {
22154             strength = 3;
22155         } else if (this.ClientSideMediumPassword(value)) {
22156             strength = 2;
22157         } else if (this.ClientSideWeakPassword(value)) {
22158             strength = 1;
22159         } else {
22160             strength = 0;
22161         }
22162
22163         
22164         if (strength < 2) {
22165             //this.markInvalid(this.errors.TooWeak);
22166             this.errorMsg = this.errors.TooWeak;
22167             //return false;
22168         }
22169         
22170         
22171         console.log('strength2: ' + strength);
22172         
22173         //var pm = this.trigger.child('div/div/div').dom;
22174         
22175         var pm = this.trigger.child('div/div');
22176         pm.removeClass(this.meterClass);
22177         pm.addClass(this.meterClass[strength]);
22178                 
22179         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22180                 
22181         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22182         
22183         this.errorMsg = ''; 
22184         return true;
22185     },
22186     // private
22187     CharacterSetChecks: function (type)
22188     {
22189         this.type = type;
22190         this.fResult = false;
22191     },
22192     // private
22193     isctype: function (character, type)
22194     {
22195         switch (type) {  
22196             case this.kCapitalLetter:
22197                 if (character >= 'A' && character <= 'Z') {
22198                     return true;
22199                 }
22200                 break;
22201             
22202             case this.kSmallLetter:
22203                 if (character >= 'a' && character <= 'z') {
22204                     return true;
22205                 }
22206                 break;
22207             
22208             case this.kDigit:
22209                 if (character >= '0' && character <= '9') {
22210                     return true;
22211                 }
22212                 break;
22213             
22214             case this.kPunctuation:
22215                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22216                     return true;
22217                 }
22218                 break;
22219             
22220             default:
22221                 return false;
22222         }
22223
22224     },
22225     // private
22226     IsLongEnough: function (pwd, size)
22227     {
22228         return !(pwd == null || isNaN(size) || pwd.length < size);
22229     },
22230     // private
22231     SpansEnoughCharacterSets: function (word, nb)
22232     {
22233         if (!this.IsLongEnough(word, nb))
22234         {
22235             return false;
22236         }
22237
22238         var characterSetChecks = new Array(
22239             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22240             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22241         );
22242         
22243         for (var index = 0; index < word.length; ++index) {
22244             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22245                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22246                     characterSetChecks[nCharSet].fResult = true;
22247                     break;
22248                 }
22249             }
22250         }
22251
22252         var nCharSets = 0;
22253         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22254             if (characterSetChecks[nCharSet].fResult) {
22255                 ++nCharSets;
22256             }
22257         }
22258
22259         if (nCharSets < nb) {
22260             return false;
22261         }
22262         return true;
22263     },
22264     // private
22265     ClientSideStrongPassword: function (pwd)
22266     {
22267         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22268     },
22269     // private
22270     ClientSideMediumPassword: function (pwd)
22271     {
22272         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22273     },
22274     // private
22275     ClientSideWeakPassword: function (pwd)
22276     {
22277         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22278     }
22279           
22280 })//<script type="text/javascript">
22281
22282 /*
22283  * Based  Ext JS Library 1.1.1
22284  * Copyright(c) 2006-2007, Ext JS, LLC.
22285  * LGPL
22286  *
22287  */
22288  
22289 /**
22290  * @class Roo.HtmlEditorCore
22291  * @extends Roo.Component
22292  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22293  *
22294  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22295  */
22296
22297 Roo.HtmlEditorCore = function(config){
22298     
22299     
22300     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22301     
22302     
22303     this.addEvents({
22304         /**
22305          * @event initialize
22306          * Fires when the editor is fully initialized (including the iframe)
22307          * @param {Roo.HtmlEditorCore} this
22308          */
22309         initialize: true,
22310         /**
22311          * @event activate
22312          * Fires when the editor is first receives the focus. Any insertion must wait
22313          * until after this event.
22314          * @param {Roo.HtmlEditorCore} this
22315          */
22316         activate: true,
22317          /**
22318          * @event beforesync
22319          * Fires before the textarea is updated with content from the editor iframe. Return false
22320          * to cancel the sync.
22321          * @param {Roo.HtmlEditorCore} this
22322          * @param {String} html
22323          */
22324         beforesync: true,
22325          /**
22326          * @event beforepush
22327          * Fires before the iframe editor is updated with content from the textarea. Return false
22328          * to cancel the push.
22329          * @param {Roo.HtmlEditorCore} this
22330          * @param {String} html
22331          */
22332         beforepush: true,
22333          /**
22334          * @event sync
22335          * Fires when the textarea is updated with content from the editor iframe.
22336          * @param {Roo.HtmlEditorCore} this
22337          * @param {String} html
22338          */
22339         sync: true,
22340          /**
22341          * @event push
22342          * Fires when the iframe editor is updated with content from the textarea.
22343          * @param {Roo.HtmlEditorCore} this
22344          * @param {String} html
22345          */
22346         push: true,
22347         
22348         /**
22349          * @event editorevent
22350          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22351          * @param {Roo.HtmlEditorCore} this
22352          */
22353         editorevent: true
22354         
22355     });
22356     
22357     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22358     
22359     // defaults : white / black...
22360     this.applyBlacklists();
22361     
22362     
22363     
22364 };
22365
22366
22367 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22368
22369
22370      /**
22371      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22372      */
22373     
22374     owner : false,
22375     
22376      /**
22377      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22378      *                        Roo.resizable.
22379      */
22380     resizable : false,
22381      /**
22382      * @cfg {Number} height (in pixels)
22383      */   
22384     height: 300,
22385    /**
22386      * @cfg {Number} width (in pixels)
22387      */   
22388     width: 500,
22389     
22390     /**
22391      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22392      * 
22393      */
22394     stylesheets: false,
22395     
22396     // id of frame..
22397     frameId: false,
22398     
22399     // private properties
22400     validationEvent : false,
22401     deferHeight: true,
22402     initialized : false,
22403     activated : false,
22404     sourceEditMode : false,
22405     onFocus : Roo.emptyFn,
22406     iframePad:3,
22407     hideMode:'offsets',
22408     
22409     clearUp: true,
22410     
22411     // blacklist + whitelisted elements..
22412     black: false,
22413     white: false,
22414      
22415     bodyCls : '',
22416
22417     /**
22418      * Protected method that will not generally be called directly. It
22419      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22420      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22421      */
22422     getDocMarkup : function(){
22423         // body styles..
22424         var st = '';
22425         
22426         // inherit styels from page...?? 
22427         if (this.stylesheets === false) {
22428             
22429             Roo.get(document.head).select('style').each(function(node) {
22430                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22431             });
22432             
22433             Roo.get(document.head).select('link').each(function(node) { 
22434                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22435             });
22436             
22437         } else if (!this.stylesheets.length) {
22438                 // simple..
22439                 st = '<style type="text/css">' +
22440                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22441                    '</style>';
22442         } else { 
22443             st = '<style type="text/css">' +
22444                     this.stylesheets +
22445                 '</style>';
22446         }
22447         
22448         st +=  '<style type="text/css">' +
22449             'IMG { cursor: pointer } ' +
22450         '</style>';
22451
22452         var cls = 'roo-htmleditor-body';
22453         
22454         if(this.bodyCls.length){
22455             cls += ' ' + this.bodyCls;
22456         }
22457         
22458         return '<html><head>' + st  +
22459             //<style type="text/css">' +
22460             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22461             //'</style>' +
22462             ' </head><body class="' +  cls + '"></body></html>';
22463     },
22464
22465     // private
22466     onRender : function(ct, position)
22467     {
22468         var _t = this;
22469         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22470         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22471         
22472         
22473         this.el.dom.style.border = '0 none';
22474         this.el.dom.setAttribute('tabIndex', -1);
22475         this.el.addClass('x-hidden hide');
22476         
22477         
22478         
22479         if(Roo.isIE){ // fix IE 1px bogus margin
22480             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22481         }
22482        
22483         
22484         this.frameId = Roo.id();
22485         
22486          
22487         
22488         var iframe = this.owner.wrap.createChild({
22489             tag: 'iframe',
22490             cls: 'form-control', // bootstrap..
22491             id: this.frameId,
22492             name: this.frameId,
22493             frameBorder : 'no',
22494             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22495         }, this.el
22496         );
22497         
22498         
22499         this.iframe = iframe.dom;
22500
22501          this.assignDocWin();
22502         
22503         this.doc.designMode = 'on';
22504        
22505         this.doc.open();
22506         this.doc.write(this.getDocMarkup());
22507         this.doc.close();
22508
22509         
22510         var task = { // must defer to wait for browser to be ready
22511             run : function(){
22512                 //console.log("run task?" + this.doc.readyState);
22513                 this.assignDocWin();
22514                 if(this.doc.body || this.doc.readyState == 'complete'){
22515                     try {
22516                         this.doc.designMode="on";
22517                     } catch (e) {
22518                         return;
22519                     }
22520                     Roo.TaskMgr.stop(task);
22521                     this.initEditor.defer(10, this);
22522                 }
22523             },
22524             interval : 10,
22525             duration: 10000,
22526             scope: this
22527         };
22528         Roo.TaskMgr.start(task);
22529
22530     },
22531
22532     // private
22533     onResize : function(w, h)
22534     {
22535          Roo.log('resize: ' +w + ',' + h );
22536         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22537         if(!this.iframe){
22538             return;
22539         }
22540         if(typeof w == 'number'){
22541             
22542             this.iframe.style.width = w + 'px';
22543         }
22544         if(typeof h == 'number'){
22545             
22546             this.iframe.style.height = h + 'px';
22547             if(this.doc){
22548                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22549             }
22550         }
22551         
22552     },
22553
22554     /**
22555      * Toggles the editor between standard and source edit mode.
22556      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22557      */
22558     toggleSourceEdit : function(sourceEditMode){
22559         
22560         this.sourceEditMode = sourceEditMode === true;
22561         
22562         if(this.sourceEditMode){
22563  
22564             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22565             
22566         }else{
22567             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22568             //this.iframe.className = '';
22569             this.deferFocus();
22570         }
22571         //this.setSize(this.owner.wrap.getSize());
22572         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22573     },
22574
22575     
22576   
22577
22578     /**
22579      * Protected method that will not generally be called directly. If you need/want
22580      * custom HTML cleanup, this is the method you should override.
22581      * @param {String} html The HTML to be cleaned
22582      * return {String} The cleaned HTML
22583      */
22584     cleanHtml : function(html){
22585         html = String(html);
22586         if(html.length > 5){
22587             if(Roo.isSafari){ // strip safari nonsense
22588                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22589             }
22590         }
22591         if(html == '&nbsp;'){
22592             html = '';
22593         }
22594         return html;
22595     },
22596
22597     /**
22598      * HTML Editor -> Textarea
22599      * Protected method that will not generally be called directly. Syncs the contents
22600      * of the editor iframe with the textarea.
22601      */
22602     syncValue : function(){
22603         if(this.initialized){
22604             var bd = (this.doc.body || this.doc.documentElement);
22605             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22606             var html = bd.innerHTML;
22607             if(Roo.isSafari){
22608                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22609                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22610                 if(m && m[1]){
22611                     html = '<div style="'+m[0]+'">' + html + '</div>';
22612                 }
22613             }
22614             html = this.cleanHtml(html);
22615             // fix up the special chars.. normaly like back quotes in word...
22616             // however we do not want to do this with chinese..
22617             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22618                 
22619                 var cc = match.charCodeAt();
22620
22621                 // Get the character value, handling surrogate pairs
22622                 if (match.length == 2) {
22623                     // It's a surrogate pair, calculate the Unicode code point
22624                     var high = match.charCodeAt(0) - 0xD800;
22625                     var low  = match.charCodeAt(1) - 0xDC00;
22626                     cc = (high * 0x400) + low + 0x10000;
22627                 }  else if (
22628                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22629                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22630                     (cc >= 0xf900 && cc < 0xfb00 )
22631                 ) {
22632                         return match;
22633                 }  
22634          
22635                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22636                 return "&#" + cc + ";";
22637                 
22638                 
22639             });
22640             
22641             
22642              
22643             if(this.owner.fireEvent('beforesync', this, html) !== false){
22644                 this.el.dom.value = html;
22645                 this.owner.fireEvent('sync', this, html);
22646             }
22647         }
22648     },
22649
22650     /**
22651      * Protected method that will not generally be called directly. Pushes the value of the textarea
22652      * into the iframe editor.
22653      */
22654     pushValue : function(){
22655         if(this.initialized){
22656             var v = this.el.dom.value.trim();
22657             
22658 //            if(v.length < 1){
22659 //                v = '&#160;';
22660 //            }
22661             
22662             if(this.owner.fireEvent('beforepush', this, v) !== false){
22663                 var d = (this.doc.body || this.doc.documentElement);
22664                 d.innerHTML = v;
22665                 this.cleanUpPaste();
22666                 this.el.dom.value = d.innerHTML;
22667                 this.owner.fireEvent('push', this, v);
22668             }
22669         }
22670     },
22671
22672     // private
22673     deferFocus : function(){
22674         this.focus.defer(10, this);
22675     },
22676
22677     // doc'ed in Field
22678     focus : function(){
22679         if(this.win && !this.sourceEditMode){
22680             this.win.focus();
22681         }else{
22682             this.el.focus();
22683         }
22684     },
22685     
22686     assignDocWin: function()
22687     {
22688         var iframe = this.iframe;
22689         
22690          if(Roo.isIE){
22691             this.doc = iframe.contentWindow.document;
22692             this.win = iframe.contentWindow;
22693         } else {
22694 //            if (!Roo.get(this.frameId)) {
22695 //                return;
22696 //            }
22697 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22698 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22699             
22700             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22701                 return;
22702             }
22703             
22704             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22705             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22706         }
22707     },
22708     
22709     // private
22710     initEditor : function(){
22711         //console.log("INIT EDITOR");
22712         this.assignDocWin();
22713         
22714         
22715         
22716         this.doc.designMode="on";
22717         this.doc.open();
22718         this.doc.write(this.getDocMarkup());
22719         this.doc.close();
22720         
22721         var dbody = (this.doc.body || this.doc.documentElement);
22722         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22723         // this copies styles from the containing element into thsi one..
22724         // not sure why we need all of this..
22725         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22726         
22727         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22728         //ss['background-attachment'] = 'fixed'; // w3c
22729         dbody.bgProperties = 'fixed'; // ie
22730         //Roo.DomHelper.applyStyles(dbody, ss);
22731         Roo.EventManager.on(this.doc, {
22732             //'mousedown': this.onEditorEvent,
22733             'mouseup': this.onEditorEvent,
22734             'dblclick': this.onEditorEvent,
22735             'click': this.onEditorEvent,
22736             'keyup': this.onEditorEvent,
22737             buffer:100,
22738             scope: this
22739         });
22740         if(Roo.isGecko){
22741             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22742         }
22743         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22744             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22745         }
22746         this.initialized = true;
22747
22748         this.owner.fireEvent('initialize', this);
22749         this.pushValue();
22750     },
22751
22752     // private
22753     onDestroy : function(){
22754         
22755         
22756         
22757         if(this.rendered){
22758             
22759             //for (var i =0; i < this.toolbars.length;i++) {
22760             //    // fixme - ask toolbars for heights?
22761             //    this.toolbars[i].onDestroy();
22762            // }
22763             
22764             //this.wrap.dom.innerHTML = '';
22765             //this.wrap.remove();
22766         }
22767     },
22768
22769     // private
22770     onFirstFocus : function(){
22771         
22772         this.assignDocWin();
22773         
22774         
22775         this.activated = true;
22776          
22777     
22778         if(Roo.isGecko){ // prevent silly gecko errors
22779             this.win.focus();
22780             var s = this.win.getSelection();
22781             if(!s.focusNode || s.focusNode.nodeType != 3){
22782                 var r = s.getRangeAt(0);
22783                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22784                 r.collapse(true);
22785                 this.deferFocus();
22786             }
22787             try{
22788                 this.execCmd('useCSS', true);
22789                 this.execCmd('styleWithCSS', false);
22790             }catch(e){}
22791         }
22792         this.owner.fireEvent('activate', this);
22793     },
22794
22795     // private
22796     adjustFont: function(btn){
22797         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22798         //if(Roo.isSafari){ // safari
22799         //    adjust *= 2;
22800        // }
22801         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22802         if(Roo.isSafari){ // safari
22803             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22804             v =  (v < 10) ? 10 : v;
22805             v =  (v > 48) ? 48 : v;
22806             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22807             
22808         }
22809         
22810         
22811         v = Math.max(1, v+adjust);
22812         
22813         this.execCmd('FontSize', v  );
22814     },
22815
22816     onEditorEvent : function(e)
22817     {
22818         this.owner.fireEvent('editorevent', this, e);
22819       //  this.updateToolbar();
22820         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22821     },
22822
22823     insertTag : function(tg)
22824     {
22825         // could be a bit smarter... -> wrap the current selected tRoo..
22826         if (tg.toLowerCase() == 'span' ||
22827             tg.toLowerCase() == 'code' ||
22828             tg.toLowerCase() == 'sup' ||
22829             tg.toLowerCase() == 'sub' 
22830             ) {
22831             
22832             range = this.createRange(this.getSelection());
22833             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22834             wrappingNode.appendChild(range.extractContents());
22835             range.insertNode(wrappingNode);
22836
22837             return;
22838             
22839             
22840             
22841         }
22842         this.execCmd("formatblock",   tg);
22843         
22844     },
22845     
22846     insertText : function(txt)
22847     {
22848         
22849         
22850         var range = this.createRange();
22851         range.deleteContents();
22852                //alert(Sender.getAttribute('label'));
22853                
22854         range.insertNode(this.doc.createTextNode(txt));
22855     } ,
22856     
22857      
22858
22859     /**
22860      * Executes a Midas editor command on the editor document and performs necessary focus and
22861      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22862      * @param {String} cmd The Midas command
22863      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22864      */
22865     relayCmd : function(cmd, value){
22866         this.win.focus();
22867         this.execCmd(cmd, value);
22868         this.owner.fireEvent('editorevent', this);
22869         //this.updateToolbar();
22870         this.owner.deferFocus();
22871     },
22872
22873     /**
22874      * Executes a Midas editor command directly on the editor document.
22875      * For visual commands, you should use {@link #relayCmd} instead.
22876      * <b>This should only be called after the editor is initialized.</b>
22877      * @param {String} cmd The Midas command
22878      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22879      */
22880     execCmd : function(cmd, value){
22881         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22882         this.syncValue();
22883     },
22884  
22885  
22886    
22887     /**
22888      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22889      * to insert tRoo.
22890      * @param {String} text | dom node.. 
22891      */
22892     insertAtCursor : function(text)
22893     {
22894         
22895         if(!this.activated){
22896             return;
22897         }
22898         /*
22899         if(Roo.isIE){
22900             this.win.focus();
22901             var r = this.doc.selection.createRange();
22902             if(r){
22903                 r.collapse(true);
22904                 r.pasteHTML(text);
22905                 this.syncValue();
22906                 this.deferFocus();
22907             
22908             }
22909             return;
22910         }
22911         */
22912         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22913             this.win.focus();
22914             
22915             
22916             // from jquery ui (MIT licenced)
22917             var range, node;
22918             var win = this.win;
22919             
22920             if (win.getSelection && win.getSelection().getRangeAt) {
22921                 range = win.getSelection().getRangeAt(0);
22922                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22923                 range.insertNode(node);
22924             } else if (win.document.selection && win.document.selection.createRange) {
22925                 // no firefox support
22926                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22927                 win.document.selection.createRange().pasteHTML(txt);
22928             } else {
22929                 // no firefox support
22930                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22931                 this.execCmd('InsertHTML', txt);
22932             } 
22933             
22934             this.syncValue();
22935             
22936             this.deferFocus();
22937         }
22938     },
22939  // private
22940     mozKeyPress : function(e){
22941         if(e.ctrlKey){
22942             var c = e.getCharCode(), cmd;
22943           
22944             if(c > 0){
22945                 c = String.fromCharCode(c).toLowerCase();
22946                 switch(c){
22947                     case 'b':
22948                         cmd = 'bold';
22949                         break;
22950                     case 'i':
22951                         cmd = 'italic';
22952                         break;
22953                     
22954                     case 'u':
22955                         cmd = 'underline';
22956                         break;
22957                     
22958                     case 'v':
22959                         this.cleanUpPaste.defer(100, this);
22960                         return;
22961                         
22962                 }
22963                 if(cmd){
22964                     this.win.focus();
22965                     this.execCmd(cmd);
22966                     this.deferFocus();
22967                     e.preventDefault();
22968                 }
22969                 
22970             }
22971         }
22972     },
22973
22974     // private
22975     fixKeys : function(){ // load time branching for fastest keydown performance
22976         if(Roo.isIE){
22977             return function(e){
22978                 var k = e.getKey(), r;
22979                 if(k == e.TAB){
22980                     e.stopEvent();
22981                     r = this.doc.selection.createRange();
22982                     if(r){
22983                         r.collapse(true);
22984                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22985                         this.deferFocus();
22986                     }
22987                     return;
22988                 }
22989                 
22990                 if(k == e.ENTER){
22991                     r = this.doc.selection.createRange();
22992                     if(r){
22993                         var target = r.parentElement();
22994                         if(!target || target.tagName.toLowerCase() != 'li'){
22995                             e.stopEvent();
22996                             r.pasteHTML('<br />');
22997                             r.collapse(false);
22998                             r.select();
22999                         }
23000                     }
23001                 }
23002                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23003                     this.cleanUpPaste.defer(100, this);
23004                     return;
23005                 }
23006                 
23007                 
23008             };
23009         }else if(Roo.isOpera){
23010             return function(e){
23011                 var k = e.getKey();
23012                 if(k == e.TAB){
23013                     e.stopEvent();
23014                     this.win.focus();
23015                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23016                     this.deferFocus();
23017                 }
23018                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23019                     this.cleanUpPaste.defer(100, this);
23020                     return;
23021                 }
23022                 
23023             };
23024         }else if(Roo.isSafari){
23025             return function(e){
23026                 var k = e.getKey();
23027                 
23028                 if(k == e.TAB){
23029                     e.stopEvent();
23030                     this.execCmd('InsertText','\t');
23031                     this.deferFocus();
23032                     return;
23033                 }
23034                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23035                     this.cleanUpPaste.defer(100, this);
23036                     return;
23037                 }
23038                 
23039              };
23040         }
23041     }(),
23042     
23043     getAllAncestors: function()
23044     {
23045         var p = this.getSelectedNode();
23046         var a = [];
23047         if (!p) {
23048             a.push(p); // push blank onto stack..
23049             p = this.getParentElement();
23050         }
23051         
23052         
23053         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23054             a.push(p);
23055             p = p.parentNode;
23056         }
23057         a.push(this.doc.body);
23058         return a;
23059     },
23060     lastSel : false,
23061     lastSelNode : false,
23062     
23063     
23064     getSelection : function() 
23065     {
23066         this.assignDocWin();
23067         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23068     },
23069     
23070     getSelectedNode: function() 
23071     {
23072         // this may only work on Gecko!!!
23073         
23074         // should we cache this!!!!
23075         
23076         
23077         
23078          
23079         var range = this.createRange(this.getSelection()).cloneRange();
23080         
23081         if (Roo.isIE) {
23082             var parent = range.parentElement();
23083             while (true) {
23084                 var testRange = range.duplicate();
23085                 testRange.moveToElementText(parent);
23086                 if (testRange.inRange(range)) {
23087                     break;
23088                 }
23089                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23090                     break;
23091                 }
23092                 parent = parent.parentElement;
23093             }
23094             return parent;
23095         }
23096         
23097         // is ancestor a text element.
23098         var ac =  range.commonAncestorContainer;
23099         if (ac.nodeType == 3) {
23100             ac = ac.parentNode;
23101         }
23102         
23103         var ar = ac.childNodes;
23104          
23105         var nodes = [];
23106         var other_nodes = [];
23107         var has_other_nodes = false;
23108         for (var i=0;i<ar.length;i++) {
23109             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23110                 continue;
23111             }
23112             // fullly contained node.
23113             
23114             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23115                 nodes.push(ar[i]);
23116                 continue;
23117             }
23118             
23119             // probably selected..
23120             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23121                 other_nodes.push(ar[i]);
23122                 continue;
23123             }
23124             // outer..
23125             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23126                 continue;
23127             }
23128             
23129             
23130             has_other_nodes = true;
23131         }
23132         if (!nodes.length && other_nodes.length) {
23133             nodes= other_nodes;
23134         }
23135         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23136             return false;
23137         }
23138         
23139         return nodes[0];
23140     },
23141     createRange: function(sel)
23142     {
23143         // this has strange effects when using with 
23144         // top toolbar - not sure if it's a great idea.
23145         //this.editor.contentWindow.focus();
23146         if (typeof sel != "undefined") {
23147             try {
23148                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23149             } catch(e) {
23150                 return this.doc.createRange();
23151             }
23152         } else {
23153             return this.doc.createRange();
23154         }
23155     },
23156     getParentElement: function()
23157     {
23158         
23159         this.assignDocWin();
23160         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23161         
23162         var range = this.createRange(sel);
23163          
23164         try {
23165             var p = range.commonAncestorContainer;
23166             while (p.nodeType == 3) { // text node
23167                 p = p.parentNode;
23168             }
23169             return p;
23170         } catch (e) {
23171             return null;
23172         }
23173     
23174     },
23175     /***
23176      *
23177      * Range intersection.. the hard stuff...
23178      *  '-1' = before
23179      *  '0' = hits..
23180      *  '1' = after.
23181      *         [ -- selected range --- ]
23182      *   [fail]                        [fail]
23183      *
23184      *    basically..
23185      *      if end is before start or  hits it. fail.
23186      *      if start is after end or hits it fail.
23187      *
23188      *   if either hits (but other is outside. - then it's not 
23189      *   
23190      *    
23191      **/
23192     
23193     
23194     // @see http://www.thismuchiknow.co.uk/?p=64.
23195     rangeIntersectsNode : function(range, node)
23196     {
23197         var nodeRange = node.ownerDocument.createRange();
23198         try {
23199             nodeRange.selectNode(node);
23200         } catch (e) {
23201             nodeRange.selectNodeContents(node);
23202         }
23203     
23204         var rangeStartRange = range.cloneRange();
23205         rangeStartRange.collapse(true);
23206     
23207         var rangeEndRange = range.cloneRange();
23208         rangeEndRange.collapse(false);
23209     
23210         var nodeStartRange = nodeRange.cloneRange();
23211         nodeStartRange.collapse(true);
23212     
23213         var nodeEndRange = nodeRange.cloneRange();
23214         nodeEndRange.collapse(false);
23215     
23216         return rangeStartRange.compareBoundaryPoints(
23217                  Range.START_TO_START, nodeEndRange) == -1 &&
23218                rangeEndRange.compareBoundaryPoints(
23219                  Range.START_TO_START, nodeStartRange) == 1;
23220         
23221          
23222     },
23223     rangeCompareNode : function(range, node)
23224     {
23225         var nodeRange = node.ownerDocument.createRange();
23226         try {
23227             nodeRange.selectNode(node);
23228         } catch (e) {
23229             nodeRange.selectNodeContents(node);
23230         }
23231         
23232         
23233         range.collapse(true);
23234     
23235         nodeRange.collapse(true);
23236      
23237         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23238         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23239          
23240         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23241         
23242         var nodeIsBefore   =  ss == 1;
23243         var nodeIsAfter    = ee == -1;
23244         
23245         if (nodeIsBefore && nodeIsAfter) {
23246             return 0; // outer
23247         }
23248         if (!nodeIsBefore && nodeIsAfter) {
23249             return 1; //right trailed.
23250         }
23251         
23252         if (nodeIsBefore && !nodeIsAfter) {
23253             return 2;  // left trailed.
23254         }
23255         // fully contined.
23256         return 3;
23257     },
23258
23259     // private? - in a new class?
23260     cleanUpPaste :  function()
23261     {
23262         // cleans up the whole document..
23263         Roo.log('cleanuppaste');
23264         
23265         this.cleanUpChildren(this.doc.body);
23266         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23267         if (clean != this.doc.body.innerHTML) {
23268             this.doc.body.innerHTML = clean;
23269         }
23270         
23271     },
23272     
23273     cleanWordChars : function(input) {// change the chars to hex code
23274         var he = Roo.HtmlEditorCore;
23275         
23276         var output = input;
23277         Roo.each(he.swapCodes, function(sw) { 
23278             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23279             
23280             output = output.replace(swapper, sw[1]);
23281         });
23282         
23283         return output;
23284     },
23285     
23286     
23287     cleanUpChildren : function (n)
23288     {
23289         if (!n.childNodes.length) {
23290             return;
23291         }
23292         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23293            this.cleanUpChild(n.childNodes[i]);
23294         }
23295     },
23296     
23297     
23298         
23299     
23300     cleanUpChild : function (node)
23301     {
23302         var ed = this;
23303         //console.log(node);
23304         if (node.nodeName == "#text") {
23305             // clean up silly Windows -- stuff?
23306             return; 
23307         }
23308         if (node.nodeName == "#comment") {
23309             node.parentNode.removeChild(node);
23310             // clean up silly Windows -- stuff?
23311             return; 
23312         }
23313         var lcname = node.tagName.toLowerCase();
23314         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23315         // whitelist of tags..
23316         
23317         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23318             // remove node.
23319             node.parentNode.removeChild(node);
23320             return;
23321             
23322         }
23323         
23324         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23325         
23326         // spans with no attributes - just remove them..
23327         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23328             remove_keep_children = true;
23329         }
23330         
23331         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23332         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23333         
23334         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23335         //    remove_keep_children = true;
23336         //}
23337         
23338         if (remove_keep_children) {
23339             this.cleanUpChildren(node);
23340             // inserts everything just before this node...
23341             while (node.childNodes.length) {
23342                 var cn = node.childNodes[0];
23343                 node.removeChild(cn);
23344                 node.parentNode.insertBefore(cn, node);
23345             }
23346             node.parentNode.removeChild(node);
23347             return;
23348         }
23349         
23350         if (!node.attributes || !node.attributes.length) {
23351             
23352           
23353             
23354             
23355             this.cleanUpChildren(node);
23356             return;
23357         }
23358         
23359         function cleanAttr(n,v)
23360         {
23361             
23362             if (v.match(/^\./) || v.match(/^\//)) {
23363                 return;
23364             }
23365             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23366                 return;
23367             }
23368             if (v.match(/^#/)) {
23369                 return;
23370             }
23371 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23372             node.removeAttribute(n);
23373             
23374         }
23375         
23376         var cwhite = this.cwhite;
23377         var cblack = this.cblack;
23378             
23379         function cleanStyle(n,v)
23380         {
23381             if (v.match(/expression/)) { //XSS?? should we even bother..
23382                 node.removeAttribute(n);
23383                 return;
23384             }
23385             
23386             var parts = v.split(/;/);
23387             var clean = [];
23388             
23389             Roo.each(parts, function(p) {
23390                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23391                 if (!p.length) {
23392                     return true;
23393                 }
23394                 var l = p.split(':').shift().replace(/\s+/g,'');
23395                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23396                 
23397                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23398 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23399                     //node.removeAttribute(n);
23400                     return true;
23401                 }
23402                 //Roo.log()
23403                 // only allow 'c whitelisted system attributes'
23404                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23405 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23406                     //node.removeAttribute(n);
23407                     return true;
23408                 }
23409                 
23410                 
23411                  
23412                 
23413                 clean.push(p);
23414                 return true;
23415             });
23416             if (clean.length) { 
23417                 node.setAttribute(n, clean.join(';'));
23418             } else {
23419                 node.removeAttribute(n);
23420             }
23421             
23422         }
23423         
23424         
23425         for (var i = node.attributes.length-1; i > -1 ; i--) {
23426             var a = node.attributes[i];
23427             //console.log(a);
23428             
23429             if (a.name.toLowerCase().substr(0,2)=='on')  {
23430                 node.removeAttribute(a.name);
23431                 continue;
23432             }
23433             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23434                 node.removeAttribute(a.name);
23435                 continue;
23436             }
23437             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23438                 cleanAttr(a.name,a.value); // fixme..
23439                 continue;
23440             }
23441             if (a.name == 'style') {
23442                 cleanStyle(a.name,a.value);
23443                 continue;
23444             }
23445             /// clean up MS crap..
23446             // tecnically this should be a list of valid class'es..
23447             
23448             
23449             if (a.name == 'class') {
23450                 if (a.value.match(/^Mso/)) {
23451                     node.removeAttribute('class');
23452                 }
23453                 
23454                 if (a.value.match(/^body$/)) {
23455                     node.removeAttribute('class');
23456                 }
23457                 continue;
23458             }
23459             
23460             // style cleanup!?
23461             // class cleanup?
23462             
23463         }
23464         
23465         
23466         this.cleanUpChildren(node);
23467         
23468         
23469     },
23470     
23471     /**
23472      * Clean up MS wordisms...
23473      */
23474     cleanWord : function(node)
23475     {
23476         if (!node) {
23477             this.cleanWord(this.doc.body);
23478             return;
23479         }
23480         
23481         if(
23482                 node.nodeName == 'SPAN' &&
23483                 !node.hasAttributes() &&
23484                 node.childNodes.length == 1 &&
23485                 node.firstChild.nodeName == "#text"  
23486         ) {
23487             var textNode = node.firstChild;
23488             node.removeChild(textNode);
23489             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23490                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23491             }
23492             node.parentNode.insertBefore(textNode, node);
23493             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23494                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23495             }
23496             node.parentNode.removeChild(node);
23497         }
23498         
23499         if (node.nodeName == "#text") {
23500             // clean up silly Windows -- stuff?
23501             return; 
23502         }
23503         if (node.nodeName == "#comment") {
23504             node.parentNode.removeChild(node);
23505             // clean up silly Windows -- stuff?
23506             return; 
23507         }
23508         
23509         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23510             node.parentNode.removeChild(node);
23511             return;
23512         }
23513         //Roo.log(node.tagName);
23514         // remove - but keep children..
23515         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23516             //Roo.log('-- removed');
23517             while (node.childNodes.length) {
23518                 var cn = node.childNodes[0];
23519                 node.removeChild(cn);
23520                 node.parentNode.insertBefore(cn, node);
23521                 // move node to parent - and clean it..
23522                 this.cleanWord(cn);
23523             }
23524             node.parentNode.removeChild(node);
23525             /// no need to iterate chidlren = it's got none..
23526             //this.iterateChildren(node, this.cleanWord);
23527             return;
23528         }
23529         // clean styles
23530         if (node.className.length) {
23531             
23532             var cn = node.className.split(/\W+/);
23533             var cna = [];
23534             Roo.each(cn, function(cls) {
23535                 if (cls.match(/Mso[a-zA-Z]+/)) {
23536                     return;
23537                 }
23538                 cna.push(cls);
23539             });
23540             node.className = cna.length ? cna.join(' ') : '';
23541             if (!cna.length) {
23542                 node.removeAttribute("class");
23543             }
23544         }
23545         
23546         if (node.hasAttribute("lang")) {
23547             node.removeAttribute("lang");
23548         }
23549         
23550         if (node.hasAttribute("style")) {
23551             
23552             var styles = node.getAttribute("style").split(";");
23553             var nstyle = [];
23554             Roo.each(styles, function(s) {
23555                 if (!s.match(/:/)) {
23556                     return;
23557                 }
23558                 var kv = s.split(":");
23559                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23560                     return;
23561                 }
23562                 // what ever is left... we allow.
23563                 nstyle.push(s);
23564             });
23565             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23566             if (!nstyle.length) {
23567                 node.removeAttribute('style');
23568             }
23569         }
23570         this.iterateChildren(node, this.cleanWord);
23571         
23572         
23573         
23574     },
23575     /**
23576      * iterateChildren of a Node, calling fn each time, using this as the scole..
23577      * @param {DomNode} node node to iterate children of.
23578      * @param {Function} fn method of this class to call on each item.
23579      */
23580     iterateChildren : function(node, fn)
23581     {
23582         if (!node.childNodes.length) {
23583                 return;
23584         }
23585         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23586            fn.call(this, node.childNodes[i])
23587         }
23588     },
23589     
23590     
23591     /**
23592      * cleanTableWidths.
23593      *
23594      * Quite often pasting from word etc.. results in tables with column and widths.
23595      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23596      *
23597      */
23598     cleanTableWidths : function(node)
23599     {
23600          
23601          
23602         if (!node) {
23603             this.cleanTableWidths(this.doc.body);
23604             return;
23605         }
23606         
23607         // ignore list...
23608         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23609             return; 
23610         }
23611         Roo.log(node.tagName);
23612         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23613             this.iterateChildren(node, this.cleanTableWidths);
23614             return;
23615         }
23616         if (node.hasAttribute('width')) {
23617             node.removeAttribute('width');
23618         }
23619         
23620          
23621         if (node.hasAttribute("style")) {
23622             // pretty basic...
23623             
23624             var styles = node.getAttribute("style").split(";");
23625             var nstyle = [];
23626             Roo.each(styles, function(s) {
23627                 if (!s.match(/:/)) {
23628                     return;
23629                 }
23630                 var kv = s.split(":");
23631                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23632                     return;
23633                 }
23634                 // what ever is left... we allow.
23635                 nstyle.push(s);
23636             });
23637             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23638             if (!nstyle.length) {
23639                 node.removeAttribute('style');
23640             }
23641         }
23642         
23643         this.iterateChildren(node, this.cleanTableWidths);
23644         
23645         
23646     },
23647     
23648     
23649     
23650     
23651     domToHTML : function(currentElement, depth, nopadtext) {
23652         
23653         depth = depth || 0;
23654         nopadtext = nopadtext || false;
23655     
23656         if (!currentElement) {
23657             return this.domToHTML(this.doc.body);
23658         }
23659         
23660         //Roo.log(currentElement);
23661         var j;
23662         var allText = false;
23663         var nodeName = currentElement.nodeName;
23664         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23665         
23666         if  (nodeName == '#text') {
23667             
23668             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23669         }
23670         
23671         
23672         var ret = '';
23673         if (nodeName != 'BODY') {
23674              
23675             var i = 0;
23676             // Prints the node tagName, such as <A>, <IMG>, etc
23677             if (tagName) {
23678                 var attr = [];
23679                 for(i = 0; i < currentElement.attributes.length;i++) {
23680                     // quoting?
23681                     var aname = currentElement.attributes.item(i).name;
23682                     if (!currentElement.attributes.item(i).value.length) {
23683                         continue;
23684                     }
23685                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23686                 }
23687                 
23688                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23689             } 
23690             else {
23691                 
23692                 // eack
23693             }
23694         } else {
23695             tagName = false;
23696         }
23697         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23698             return ret;
23699         }
23700         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23701             nopadtext = true;
23702         }
23703         
23704         
23705         // Traverse the tree
23706         i = 0;
23707         var currentElementChild = currentElement.childNodes.item(i);
23708         var allText = true;
23709         var innerHTML  = '';
23710         lastnode = '';
23711         while (currentElementChild) {
23712             // Formatting code (indent the tree so it looks nice on the screen)
23713             var nopad = nopadtext;
23714             if (lastnode == 'SPAN') {
23715                 nopad  = true;
23716             }
23717             // text
23718             if  (currentElementChild.nodeName == '#text') {
23719                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23720                 toadd = nopadtext ? toadd : toadd.trim();
23721                 if (!nopad && toadd.length > 80) {
23722                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23723                 }
23724                 innerHTML  += toadd;
23725                 
23726                 i++;
23727                 currentElementChild = currentElement.childNodes.item(i);
23728                 lastNode = '';
23729                 continue;
23730             }
23731             allText = false;
23732             
23733             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23734                 
23735             // Recursively traverse the tree structure of the child node
23736             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23737             lastnode = currentElementChild.nodeName;
23738             i++;
23739             currentElementChild=currentElement.childNodes.item(i);
23740         }
23741         
23742         ret += innerHTML;
23743         
23744         if (!allText) {
23745                 // The remaining code is mostly for formatting the tree
23746             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23747         }
23748         
23749         
23750         if (tagName) {
23751             ret+= "</"+tagName+">";
23752         }
23753         return ret;
23754         
23755     },
23756         
23757     applyBlacklists : function()
23758     {
23759         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23760         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23761         
23762         this.white = [];
23763         this.black = [];
23764         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23765             if (b.indexOf(tag) > -1) {
23766                 return;
23767             }
23768             this.white.push(tag);
23769             
23770         }, this);
23771         
23772         Roo.each(w, function(tag) {
23773             if (b.indexOf(tag) > -1) {
23774                 return;
23775             }
23776             if (this.white.indexOf(tag) > -1) {
23777                 return;
23778             }
23779             this.white.push(tag);
23780             
23781         }, this);
23782         
23783         
23784         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23785             if (w.indexOf(tag) > -1) {
23786                 return;
23787             }
23788             this.black.push(tag);
23789             
23790         }, this);
23791         
23792         Roo.each(b, function(tag) {
23793             if (w.indexOf(tag) > -1) {
23794                 return;
23795             }
23796             if (this.black.indexOf(tag) > -1) {
23797                 return;
23798             }
23799             this.black.push(tag);
23800             
23801         }, this);
23802         
23803         
23804         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23805         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23806         
23807         this.cwhite = [];
23808         this.cblack = [];
23809         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23810             if (b.indexOf(tag) > -1) {
23811                 return;
23812             }
23813             this.cwhite.push(tag);
23814             
23815         }, this);
23816         
23817         Roo.each(w, function(tag) {
23818             if (b.indexOf(tag) > -1) {
23819                 return;
23820             }
23821             if (this.cwhite.indexOf(tag) > -1) {
23822                 return;
23823             }
23824             this.cwhite.push(tag);
23825             
23826         }, this);
23827         
23828         
23829         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23830             if (w.indexOf(tag) > -1) {
23831                 return;
23832             }
23833             this.cblack.push(tag);
23834             
23835         }, this);
23836         
23837         Roo.each(b, function(tag) {
23838             if (w.indexOf(tag) > -1) {
23839                 return;
23840             }
23841             if (this.cblack.indexOf(tag) > -1) {
23842                 return;
23843             }
23844             this.cblack.push(tag);
23845             
23846         }, this);
23847     },
23848     
23849     setStylesheets : function(stylesheets)
23850     {
23851         if(typeof(stylesheets) == 'string'){
23852             Roo.get(this.iframe.contentDocument.head).createChild({
23853                 tag : 'link',
23854                 rel : 'stylesheet',
23855                 type : 'text/css',
23856                 href : stylesheets
23857             });
23858             
23859             return;
23860         }
23861         var _this = this;
23862      
23863         Roo.each(stylesheets, function(s) {
23864             if(!s.length){
23865                 return;
23866             }
23867             
23868             Roo.get(_this.iframe.contentDocument.head).createChild({
23869                 tag : 'link',
23870                 rel : 'stylesheet',
23871                 type : 'text/css',
23872                 href : s
23873             });
23874         });
23875
23876         
23877     },
23878     
23879     removeStylesheets : function()
23880     {
23881         var _this = this;
23882         
23883         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23884             s.remove();
23885         });
23886     },
23887     
23888     setStyle : function(style)
23889     {
23890         Roo.get(this.iframe.contentDocument.head).createChild({
23891             tag : 'style',
23892             type : 'text/css',
23893             html : style
23894         });
23895
23896         return;
23897     }
23898     
23899     // hide stuff that is not compatible
23900     /**
23901      * @event blur
23902      * @hide
23903      */
23904     /**
23905      * @event change
23906      * @hide
23907      */
23908     /**
23909      * @event focus
23910      * @hide
23911      */
23912     /**
23913      * @event specialkey
23914      * @hide
23915      */
23916     /**
23917      * @cfg {String} fieldClass @hide
23918      */
23919     /**
23920      * @cfg {String} focusClass @hide
23921      */
23922     /**
23923      * @cfg {String} autoCreate @hide
23924      */
23925     /**
23926      * @cfg {String} inputType @hide
23927      */
23928     /**
23929      * @cfg {String} invalidClass @hide
23930      */
23931     /**
23932      * @cfg {String} invalidText @hide
23933      */
23934     /**
23935      * @cfg {String} msgFx @hide
23936      */
23937     /**
23938      * @cfg {String} validateOnBlur @hide
23939      */
23940 });
23941
23942 Roo.HtmlEditorCore.white = [
23943         'area', 'br', 'img', 'input', 'hr', 'wbr',
23944         
23945        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23946        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23947        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23948        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23949        'table',   'ul',         'xmp', 
23950        
23951        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23952       'thead',   'tr', 
23953      
23954       'dir', 'menu', 'ol', 'ul', 'dl',
23955        
23956       'embed',  'object'
23957 ];
23958
23959
23960 Roo.HtmlEditorCore.black = [
23961     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23962         'applet', // 
23963         'base',   'basefont', 'bgsound', 'blink',  'body', 
23964         'frame',  'frameset', 'head',    'html',   'ilayer', 
23965         'iframe', 'layer',  'link',     'meta',    'object',   
23966         'script', 'style' ,'title',  'xml' // clean later..
23967 ];
23968 Roo.HtmlEditorCore.clean = [
23969     'script', 'style', 'title', 'xml'
23970 ];
23971 Roo.HtmlEditorCore.remove = [
23972     'font'
23973 ];
23974 // attributes..
23975
23976 Roo.HtmlEditorCore.ablack = [
23977     'on'
23978 ];
23979     
23980 Roo.HtmlEditorCore.aclean = [ 
23981     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23982 ];
23983
23984 // protocols..
23985 Roo.HtmlEditorCore.pwhite= [
23986         'http',  'https',  'mailto'
23987 ];
23988
23989 // white listed style attributes.
23990 Roo.HtmlEditorCore.cwhite= [
23991       //  'text-align', /// default is to allow most things..
23992       
23993          
23994 //        'font-size'//??
23995 ];
23996
23997 // black listed style attributes.
23998 Roo.HtmlEditorCore.cblack= [
23999       //  'font-size' -- this can be set by the project 
24000 ];
24001
24002
24003 Roo.HtmlEditorCore.swapCodes   =[ 
24004     [    8211, "--" ], 
24005     [    8212, "--" ], 
24006     [    8216,  "'" ],  
24007     [    8217, "'" ],  
24008     [    8220, '"' ],  
24009     [    8221, '"' ],  
24010     [    8226, "*" ],  
24011     [    8230, "..." ]
24012 ]; 
24013
24014     /*
24015  * - LGPL
24016  *
24017  * HtmlEditor
24018  * 
24019  */
24020
24021 /**
24022  * @class Roo.bootstrap.HtmlEditor
24023  * @extends Roo.bootstrap.TextArea
24024  * Bootstrap HtmlEditor class
24025
24026  * @constructor
24027  * Create a new HtmlEditor
24028  * @param {Object} config The config object
24029  */
24030
24031 Roo.bootstrap.HtmlEditor = function(config){
24032     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24033     if (!this.toolbars) {
24034         this.toolbars = [];
24035     }
24036     
24037     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24038     this.addEvents({
24039             /**
24040              * @event initialize
24041              * Fires when the editor is fully initialized (including the iframe)
24042              * @param {HtmlEditor} this
24043              */
24044             initialize: true,
24045             /**
24046              * @event activate
24047              * Fires when the editor is first receives the focus. Any insertion must wait
24048              * until after this event.
24049              * @param {HtmlEditor} this
24050              */
24051             activate: true,
24052              /**
24053              * @event beforesync
24054              * Fires before the textarea is updated with content from the editor iframe. Return false
24055              * to cancel the sync.
24056              * @param {HtmlEditor} this
24057              * @param {String} html
24058              */
24059             beforesync: true,
24060              /**
24061              * @event beforepush
24062              * Fires before the iframe editor is updated with content from the textarea. Return false
24063              * to cancel the push.
24064              * @param {HtmlEditor} this
24065              * @param {String} html
24066              */
24067             beforepush: true,
24068              /**
24069              * @event sync
24070              * Fires when the textarea is updated with content from the editor iframe.
24071              * @param {HtmlEditor} this
24072              * @param {String} html
24073              */
24074             sync: true,
24075              /**
24076              * @event push
24077              * Fires when the iframe editor is updated with content from the textarea.
24078              * @param {HtmlEditor} this
24079              * @param {String} html
24080              */
24081             push: true,
24082              /**
24083              * @event editmodechange
24084              * Fires when the editor switches edit modes
24085              * @param {HtmlEditor} this
24086              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24087              */
24088             editmodechange: true,
24089             /**
24090              * @event editorevent
24091              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24092              * @param {HtmlEditor} this
24093              */
24094             editorevent: true,
24095             /**
24096              * @event firstfocus
24097              * Fires when on first focus - needed by toolbars..
24098              * @param {HtmlEditor} this
24099              */
24100             firstfocus: true,
24101             /**
24102              * @event autosave
24103              * Auto save the htmlEditor value as a file into Events
24104              * @param {HtmlEditor} this
24105              */
24106             autosave: true,
24107             /**
24108              * @event savedpreview
24109              * preview the saved version of htmlEditor
24110              * @param {HtmlEditor} this
24111              */
24112             savedpreview: true
24113         });
24114 };
24115
24116
24117 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24118     
24119     
24120       /**
24121      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24122      */
24123     toolbars : false,
24124     
24125      /**
24126     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24127     */
24128     btns : [],
24129    
24130      /**
24131      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24132      *                        Roo.resizable.
24133      */
24134     resizable : false,
24135      /**
24136      * @cfg {Number} height (in pixels)
24137      */   
24138     height: 300,
24139    /**
24140      * @cfg {Number} width (in pixels)
24141      */   
24142     width: false,
24143     
24144     /**
24145      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24146      * 
24147      */
24148     stylesheets: false,
24149     
24150     // id of frame..
24151     frameId: false,
24152     
24153     // private properties
24154     validationEvent : false,
24155     deferHeight: true,
24156     initialized : false,
24157     activated : false,
24158     
24159     onFocus : Roo.emptyFn,
24160     iframePad:3,
24161     hideMode:'offsets',
24162     
24163     tbContainer : false,
24164     
24165     bodyCls : '',
24166     
24167     toolbarContainer :function() {
24168         return this.wrap.select('.x-html-editor-tb',true).first();
24169     },
24170
24171     /**
24172      * Protected method that will not generally be called directly. It
24173      * is called when the editor creates its toolbar. Override this method if you need to
24174      * add custom toolbar buttons.
24175      * @param {HtmlEditor} editor
24176      */
24177     createToolbar : function(){
24178         Roo.log('renewing');
24179         Roo.log("create toolbars");
24180         
24181         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24182         this.toolbars[0].render(this.toolbarContainer());
24183         
24184         return;
24185         
24186 //        if (!editor.toolbars || !editor.toolbars.length) {
24187 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24188 //        }
24189 //        
24190 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24191 //            editor.toolbars[i] = Roo.factory(
24192 //                    typeof(editor.toolbars[i]) == 'string' ?
24193 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24194 //                Roo.bootstrap.HtmlEditor);
24195 //            editor.toolbars[i].init(editor);
24196 //        }
24197     },
24198
24199      
24200     // private
24201     onRender : function(ct, position)
24202     {
24203        // Roo.log("Call onRender: " + this.xtype);
24204         var _t = this;
24205         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24206       
24207         this.wrap = this.inputEl().wrap({
24208             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24209         });
24210         
24211         this.editorcore.onRender(ct, position);
24212          
24213         if (this.resizable) {
24214             this.resizeEl = new Roo.Resizable(this.wrap, {
24215                 pinned : true,
24216                 wrap: true,
24217                 dynamic : true,
24218                 minHeight : this.height,
24219                 height: this.height,
24220                 handles : this.resizable,
24221                 width: this.width,
24222                 listeners : {
24223                     resize : function(r, w, h) {
24224                         _t.onResize(w,h); // -something
24225                     }
24226                 }
24227             });
24228             
24229         }
24230         this.createToolbar(this);
24231        
24232         
24233         if(!this.width && this.resizable){
24234             this.setSize(this.wrap.getSize());
24235         }
24236         if (this.resizeEl) {
24237             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24238             // should trigger onReize..
24239         }
24240         
24241     },
24242
24243     // private
24244     onResize : function(w, h)
24245     {
24246         Roo.log('resize: ' +w + ',' + h );
24247         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24248         var ew = false;
24249         var eh = false;
24250         
24251         if(this.inputEl() ){
24252             if(typeof w == 'number'){
24253                 var aw = w - this.wrap.getFrameWidth('lr');
24254                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24255                 ew = aw;
24256             }
24257             if(typeof h == 'number'){
24258                  var tbh = -11;  // fixme it needs to tool bar size!
24259                 for (var i =0; i < this.toolbars.length;i++) {
24260                     // fixme - ask toolbars for heights?
24261                     tbh += this.toolbars[i].el.getHeight();
24262                     //if (this.toolbars[i].footer) {
24263                     //    tbh += this.toolbars[i].footer.el.getHeight();
24264                     //}
24265                 }
24266               
24267                 
24268                 
24269                 
24270                 
24271                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24272                 ah -= 5; // knock a few pixes off for look..
24273                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24274                 var eh = ah;
24275             }
24276         }
24277         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24278         this.editorcore.onResize(ew,eh);
24279         
24280     },
24281
24282     /**
24283      * Toggles the editor between standard and source edit mode.
24284      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24285      */
24286     toggleSourceEdit : function(sourceEditMode)
24287     {
24288         this.editorcore.toggleSourceEdit(sourceEditMode);
24289         
24290         if(this.editorcore.sourceEditMode){
24291             Roo.log('editor - showing textarea');
24292             
24293 //            Roo.log('in');
24294 //            Roo.log(this.syncValue());
24295             this.syncValue();
24296             this.inputEl().removeClass(['hide', 'x-hidden']);
24297             this.inputEl().dom.removeAttribute('tabIndex');
24298             this.inputEl().focus();
24299         }else{
24300             Roo.log('editor - hiding textarea');
24301 //            Roo.log('out')
24302 //            Roo.log(this.pushValue()); 
24303             this.pushValue();
24304             
24305             this.inputEl().addClass(['hide', 'x-hidden']);
24306             this.inputEl().dom.setAttribute('tabIndex', -1);
24307             //this.deferFocus();
24308         }
24309          
24310         if(this.resizable){
24311             this.setSize(this.wrap.getSize());
24312         }
24313         
24314         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24315     },
24316  
24317     // private (for BoxComponent)
24318     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24319
24320     // private (for BoxComponent)
24321     getResizeEl : function(){
24322         return this.wrap;
24323     },
24324
24325     // private (for BoxComponent)
24326     getPositionEl : function(){
24327         return this.wrap;
24328     },
24329
24330     // private
24331     initEvents : function(){
24332         this.originalValue = this.getValue();
24333     },
24334
24335 //    /**
24336 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24337 //     * @method
24338 //     */
24339 //    markInvalid : Roo.emptyFn,
24340 //    /**
24341 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24342 //     * @method
24343 //     */
24344 //    clearInvalid : Roo.emptyFn,
24345
24346     setValue : function(v){
24347         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24348         this.editorcore.pushValue();
24349     },
24350
24351      
24352     // private
24353     deferFocus : function(){
24354         this.focus.defer(10, this);
24355     },
24356
24357     // doc'ed in Field
24358     focus : function(){
24359         this.editorcore.focus();
24360         
24361     },
24362       
24363
24364     // private
24365     onDestroy : function(){
24366         
24367         
24368         
24369         if(this.rendered){
24370             
24371             for (var i =0; i < this.toolbars.length;i++) {
24372                 // fixme - ask toolbars for heights?
24373                 this.toolbars[i].onDestroy();
24374             }
24375             
24376             this.wrap.dom.innerHTML = '';
24377             this.wrap.remove();
24378         }
24379     },
24380
24381     // private
24382     onFirstFocus : function(){
24383         //Roo.log("onFirstFocus");
24384         this.editorcore.onFirstFocus();
24385          for (var i =0; i < this.toolbars.length;i++) {
24386             this.toolbars[i].onFirstFocus();
24387         }
24388         
24389     },
24390     
24391     // private
24392     syncValue : function()
24393     {   
24394         this.editorcore.syncValue();
24395     },
24396     
24397     pushValue : function()
24398     {   
24399         this.editorcore.pushValue();
24400     }
24401      
24402     
24403     // hide stuff that is not compatible
24404     /**
24405      * @event blur
24406      * @hide
24407      */
24408     /**
24409      * @event change
24410      * @hide
24411      */
24412     /**
24413      * @event focus
24414      * @hide
24415      */
24416     /**
24417      * @event specialkey
24418      * @hide
24419      */
24420     /**
24421      * @cfg {String} fieldClass @hide
24422      */
24423     /**
24424      * @cfg {String} focusClass @hide
24425      */
24426     /**
24427      * @cfg {String} autoCreate @hide
24428      */
24429     /**
24430      * @cfg {String} inputType @hide
24431      */
24432      
24433     /**
24434      * @cfg {String} invalidText @hide
24435      */
24436     /**
24437      * @cfg {String} msgFx @hide
24438      */
24439     /**
24440      * @cfg {String} validateOnBlur @hide
24441      */
24442 });
24443  
24444     
24445    
24446    
24447    
24448       
24449 Roo.namespace('Roo.bootstrap.htmleditor');
24450 /**
24451  * @class Roo.bootstrap.HtmlEditorToolbar1
24452  * Basic Toolbar
24453  * 
24454  * @example
24455  * Usage:
24456  *
24457  new Roo.bootstrap.HtmlEditor({
24458     ....
24459     toolbars : [
24460         new Roo.bootstrap.HtmlEditorToolbar1({
24461             disable : { fonts: 1 , format: 1, ..., ... , ...],
24462             btns : [ .... ]
24463         })
24464     }
24465      
24466  * 
24467  * @cfg {Object} disable List of elements to disable..
24468  * @cfg {Array} btns List of additional buttons.
24469  * 
24470  * 
24471  * NEEDS Extra CSS? 
24472  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24473  */
24474  
24475 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24476 {
24477     
24478     Roo.apply(this, config);
24479     
24480     // default disabled, based on 'good practice'..
24481     this.disable = this.disable || {};
24482     Roo.applyIf(this.disable, {
24483         fontSize : true,
24484         colors : true,
24485         specialElements : true
24486     });
24487     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24488     
24489     this.editor = config.editor;
24490     this.editorcore = config.editor.editorcore;
24491     
24492     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24493     
24494     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24495     // dont call parent... till later.
24496 }
24497 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24498      
24499     bar : true,
24500     
24501     editor : false,
24502     editorcore : false,
24503     
24504     
24505     formats : [
24506         "p" ,  
24507         "h1","h2","h3","h4","h5","h6", 
24508         "pre", "code", 
24509         "abbr", "acronym", "address", "cite", "samp", "var",
24510         'div','span'
24511     ],
24512     
24513     onRender : function(ct, position)
24514     {
24515        // Roo.log("Call onRender: " + this.xtype);
24516         
24517        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24518        Roo.log(this.el);
24519        this.el.dom.style.marginBottom = '0';
24520        var _this = this;
24521        var editorcore = this.editorcore;
24522        var editor= this.editor;
24523        
24524        var children = [];
24525        var btn = function(id,cmd , toggle, handler, html){
24526        
24527             var  event = toggle ? 'toggle' : 'click';
24528        
24529             var a = {
24530                 size : 'sm',
24531                 xtype: 'Button',
24532                 xns: Roo.bootstrap,
24533                 //glyphicon : id,
24534                 fa: id,
24535                 cmd : id || cmd,
24536                 enableToggle:toggle !== false,
24537                 html : html || '',
24538                 pressed : toggle ? false : null,
24539                 listeners : {}
24540             };
24541             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24542                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24543             };
24544             children.push(a);
24545             return a;
24546        }
24547        
24548     //    var cb_box = function...
24549         
24550         var style = {
24551                 xtype: 'Button',
24552                 size : 'sm',
24553                 xns: Roo.bootstrap,
24554                 fa : 'font',
24555                 //html : 'submit'
24556                 menu : {
24557                     xtype: 'Menu',
24558                     xns: Roo.bootstrap,
24559                     items:  []
24560                 }
24561         };
24562         Roo.each(this.formats, function(f) {
24563             style.menu.items.push({
24564                 xtype :'MenuItem',
24565                 xns: Roo.bootstrap,
24566                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24567                 tagname : f,
24568                 listeners : {
24569                     click : function()
24570                     {
24571                         editorcore.insertTag(this.tagname);
24572                         editor.focus();
24573                     }
24574                 }
24575                 
24576             });
24577         });
24578         children.push(style);   
24579         
24580         btn('bold',false,true);
24581         btn('italic',false,true);
24582         btn('align-left', 'justifyleft',true);
24583         btn('align-center', 'justifycenter',true);
24584         btn('align-right' , 'justifyright',true);
24585         btn('link', false, false, function(btn) {
24586             //Roo.log("create link?");
24587             var url = prompt(this.createLinkText, this.defaultLinkValue);
24588             if(url && url != 'http:/'+'/'){
24589                 this.editorcore.relayCmd('createlink', url);
24590             }
24591         }),
24592         btn('list','insertunorderedlist',true);
24593         btn('pencil', false,true, function(btn){
24594                 Roo.log(this);
24595                 this.toggleSourceEdit(btn.pressed);
24596         });
24597         
24598         if (this.editor.btns.length > 0) {
24599             for (var i = 0; i<this.editor.btns.length; i++) {
24600                 children.push(this.editor.btns[i]);
24601             }
24602         }
24603         
24604         /*
24605         var cog = {
24606                 xtype: 'Button',
24607                 size : 'sm',
24608                 xns: Roo.bootstrap,
24609                 glyphicon : 'cog',
24610                 //html : 'submit'
24611                 menu : {
24612                     xtype: 'Menu',
24613                     xns: Roo.bootstrap,
24614                     items:  []
24615                 }
24616         };
24617         
24618         cog.menu.items.push({
24619             xtype :'MenuItem',
24620             xns: Roo.bootstrap,
24621             html : Clean styles,
24622             tagname : f,
24623             listeners : {
24624                 click : function()
24625                 {
24626                     editorcore.insertTag(this.tagname);
24627                     editor.focus();
24628                 }
24629             }
24630             
24631         });
24632        */
24633         
24634          
24635        this.xtype = 'NavSimplebar';
24636         
24637         for(var i=0;i< children.length;i++) {
24638             
24639             this.buttons.add(this.addxtypeChild(children[i]));
24640             
24641         }
24642         
24643         editor.on('editorevent', this.updateToolbar, this);
24644     },
24645     onBtnClick : function(id)
24646     {
24647        this.editorcore.relayCmd(id);
24648        this.editorcore.focus();
24649     },
24650     
24651     /**
24652      * Protected method that will not generally be called directly. It triggers
24653      * a toolbar update by reading the markup state of the current selection in the editor.
24654      */
24655     updateToolbar: function(){
24656
24657         if(!this.editorcore.activated){
24658             this.editor.onFirstFocus(); // is this neeed?
24659             return;
24660         }
24661
24662         var btns = this.buttons; 
24663         var doc = this.editorcore.doc;
24664         btns.get('bold').setActive(doc.queryCommandState('bold'));
24665         btns.get('italic').setActive(doc.queryCommandState('italic'));
24666         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24667         
24668         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24669         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24670         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24671         
24672         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24673         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24674          /*
24675         
24676         var ans = this.editorcore.getAllAncestors();
24677         if (this.formatCombo) {
24678             
24679             
24680             var store = this.formatCombo.store;
24681             this.formatCombo.setValue("");
24682             for (var i =0; i < ans.length;i++) {
24683                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24684                     // select it..
24685                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24686                     break;
24687                 }
24688             }
24689         }
24690         
24691         
24692         
24693         // hides menus... - so this cant be on a menu...
24694         Roo.bootstrap.MenuMgr.hideAll();
24695         */
24696         Roo.bootstrap.MenuMgr.hideAll();
24697         //this.editorsyncValue();
24698     },
24699     onFirstFocus: function() {
24700         this.buttons.each(function(item){
24701            item.enable();
24702         });
24703     },
24704     toggleSourceEdit : function(sourceEditMode){
24705         
24706           
24707         if(sourceEditMode){
24708             Roo.log("disabling buttons");
24709            this.buttons.each( function(item){
24710                 if(item.cmd != 'pencil'){
24711                     item.disable();
24712                 }
24713             });
24714           
24715         }else{
24716             Roo.log("enabling buttons");
24717             if(this.editorcore.initialized){
24718                 this.buttons.each( function(item){
24719                     item.enable();
24720                 });
24721             }
24722             
24723         }
24724         Roo.log("calling toggole on editor");
24725         // tell the editor that it's been pressed..
24726         this.editor.toggleSourceEdit(sourceEditMode);
24727        
24728     }
24729 });
24730
24731
24732
24733
24734
24735 /**
24736  * @class Roo.bootstrap.Table.AbstractSelectionModel
24737  * @extends Roo.util.Observable
24738  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24739  * implemented by descendant classes.  This class should not be directly instantiated.
24740  * @constructor
24741  */
24742 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24743     this.locked = false;
24744     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24745 };
24746
24747
24748 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24749     /** @ignore Called by the grid automatically. Do not call directly. */
24750     init : function(grid){
24751         this.grid = grid;
24752         this.initEvents();
24753     },
24754
24755     /**
24756      * Locks the selections.
24757      */
24758     lock : function(){
24759         this.locked = true;
24760     },
24761
24762     /**
24763      * Unlocks the selections.
24764      */
24765     unlock : function(){
24766         this.locked = false;
24767     },
24768
24769     /**
24770      * Returns true if the selections are locked.
24771      * @return {Boolean}
24772      */
24773     isLocked : function(){
24774         return this.locked;
24775     }
24776 });
24777 /**
24778  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24779  * @class Roo.bootstrap.Table.RowSelectionModel
24780  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24781  * It supports multiple selections and keyboard selection/navigation. 
24782  * @constructor
24783  * @param {Object} config
24784  */
24785
24786 Roo.bootstrap.Table.RowSelectionModel = function(config){
24787     Roo.apply(this, config);
24788     this.selections = new Roo.util.MixedCollection(false, function(o){
24789         return o.id;
24790     });
24791
24792     this.last = false;
24793     this.lastActive = false;
24794
24795     this.addEvents({
24796         /**
24797              * @event selectionchange
24798              * Fires when the selection changes
24799              * @param {SelectionModel} this
24800              */
24801             "selectionchange" : true,
24802         /**
24803              * @event afterselectionchange
24804              * Fires after the selection changes (eg. by key press or clicking)
24805              * @param {SelectionModel} this
24806              */
24807             "afterselectionchange" : true,
24808         /**
24809              * @event beforerowselect
24810              * Fires when a row is selected being selected, return false to cancel.
24811              * @param {SelectionModel} this
24812              * @param {Number} rowIndex The selected index
24813              * @param {Boolean} keepExisting False if other selections will be cleared
24814              */
24815             "beforerowselect" : true,
24816         /**
24817              * @event rowselect
24818              * Fires when a row is selected.
24819              * @param {SelectionModel} this
24820              * @param {Number} rowIndex The selected index
24821              * @param {Roo.data.Record} r The record
24822              */
24823             "rowselect" : true,
24824         /**
24825              * @event rowdeselect
24826              * Fires when a row is deselected.
24827              * @param {SelectionModel} this
24828              * @param {Number} rowIndex The selected index
24829              */
24830         "rowdeselect" : true
24831     });
24832     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24833     this.locked = false;
24834  };
24835
24836 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24837     /**
24838      * @cfg {Boolean} singleSelect
24839      * True to allow selection of only one row at a time (defaults to false)
24840      */
24841     singleSelect : false,
24842
24843     // private
24844     initEvents : function()
24845     {
24846
24847         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24848         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24849         //}else{ // allow click to work like normal
24850          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24851         //}
24852         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24853         this.grid.on("rowclick", this.handleMouseDown, this);
24854         
24855         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24856             "up" : function(e){
24857                 if(!e.shiftKey){
24858                     this.selectPrevious(e.shiftKey);
24859                 }else if(this.last !== false && this.lastActive !== false){
24860                     var last = this.last;
24861                     this.selectRange(this.last,  this.lastActive-1);
24862                     this.grid.getView().focusRow(this.lastActive);
24863                     if(last !== false){
24864                         this.last = last;
24865                     }
24866                 }else{
24867                     this.selectFirstRow();
24868                 }
24869                 this.fireEvent("afterselectionchange", this);
24870             },
24871             "down" : function(e){
24872                 if(!e.shiftKey){
24873                     this.selectNext(e.shiftKey);
24874                 }else if(this.last !== false && this.lastActive !== false){
24875                     var last = this.last;
24876                     this.selectRange(this.last,  this.lastActive+1);
24877                     this.grid.getView().focusRow(this.lastActive);
24878                     if(last !== false){
24879                         this.last = last;
24880                     }
24881                 }else{
24882                     this.selectFirstRow();
24883                 }
24884                 this.fireEvent("afterselectionchange", this);
24885             },
24886             scope: this
24887         });
24888         this.grid.store.on('load', function(){
24889             this.selections.clear();
24890         },this);
24891         /*
24892         var view = this.grid.view;
24893         view.on("refresh", this.onRefresh, this);
24894         view.on("rowupdated", this.onRowUpdated, this);
24895         view.on("rowremoved", this.onRemove, this);
24896         */
24897     },
24898
24899     // private
24900     onRefresh : function()
24901     {
24902         var ds = this.grid.store, i, v = this.grid.view;
24903         var s = this.selections;
24904         s.each(function(r){
24905             if((i = ds.indexOfId(r.id)) != -1){
24906                 v.onRowSelect(i);
24907             }else{
24908                 s.remove(r);
24909             }
24910         });
24911     },
24912
24913     // private
24914     onRemove : function(v, index, r){
24915         this.selections.remove(r);
24916     },
24917
24918     // private
24919     onRowUpdated : function(v, index, r){
24920         if(this.isSelected(r)){
24921             v.onRowSelect(index);
24922         }
24923     },
24924
24925     /**
24926      * Select records.
24927      * @param {Array} records The records to select
24928      * @param {Boolean} keepExisting (optional) True to keep existing selections
24929      */
24930     selectRecords : function(records, keepExisting)
24931     {
24932         if(!keepExisting){
24933             this.clearSelections();
24934         }
24935             var ds = this.grid.store;
24936         for(var i = 0, len = records.length; i < len; i++){
24937             this.selectRow(ds.indexOf(records[i]), true);
24938         }
24939     },
24940
24941     /**
24942      * Gets the number of selected rows.
24943      * @return {Number}
24944      */
24945     getCount : function(){
24946         return this.selections.length;
24947     },
24948
24949     /**
24950      * Selects the first row in the grid.
24951      */
24952     selectFirstRow : function(){
24953         this.selectRow(0);
24954     },
24955
24956     /**
24957      * Select the last row.
24958      * @param {Boolean} keepExisting (optional) True to keep existing selections
24959      */
24960     selectLastRow : function(keepExisting){
24961         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24962         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24963     },
24964
24965     /**
24966      * Selects the row immediately following the last selected row.
24967      * @param {Boolean} keepExisting (optional) True to keep existing selections
24968      */
24969     selectNext : function(keepExisting)
24970     {
24971             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24972             this.selectRow(this.last+1, keepExisting);
24973             this.grid.getView().focusRow(this.last);
24974         }
24975     },
24976
24977     /**
24978      * Selects the row that precedes the last selected row.
24979      * @param {Boolean} keepExisting (optional) True to keep existing selections
24980      */
24981     selectPrevious : function(keepExisting){
24982         if(this.last){
24983             this.selectRow(this.last-1, keepExisting);
24984             this.grid.getView().focusRow(this.last);
24985         }
24986     },
24987
24988     /**
24989      * Returns the selected records
24990      * @return {Array} Array of selected records
24991      */
24992     getSelections : function(){
24993         return [].concat(this.selections.items);
24994     },
24995
24996     /**
24997      * Returns the first selected record.
24998      * @return {Record}
24999      */
25000     getSelected : function(){
25001         return this.selections.itemAt(0);
25002     },
25003
25004
25005     /**
25006      * Clears all selections.
25007      */
25008     clearSelections : function(fast)
25009     {
25010         if(this.locked) {
25011             return;
25012         }
25013         if(fast !== true){
25014                 var ds = this.grid.store;
25015             var s = this.selections;
25016             s.each(function(r){
25017                 this.deselectRow(ds.indexOfId(r.id));
25018             }, this);
25019             s.clear();
25020         }else{
25021             this.selections.clear();
25022         }
25023         this.last = false;
25024     },
25025
25026
25027     /**
25028      * Selects all rows.
25029      */
25030     selectAll : function(){
25031         if(this.locked) {
25032             return;
25033         }
25034         this.selections.clear();
25035         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25036             this.selectRow(i, true);
25037         }
25038     },
25039
25040     /**
25041      * Returns True if there is a selection.
25042      * @return {Boolean}
25043      */
25044     hasSelection : function(){
25045         return this.selections.length > 0;
25046     },
25047
25048     /**
25049      * Returns True if the specified row is selected.
25050      * @param {Number/Record} record The record or index of the record to check
25051      * @return {Boolean}
25052      */
25053     isSelected : function(index){
25054             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25055         return (r && this.selections.key(r.id) ? true : false);
25056     },
25057
25058     /**
25059      * Returns True if the specified record id is selected.
25060      * @param {String} id The id of record to check
25061      * @return {Boolean}
25062      */
25063     isIdSelected : function(id){
25064         return (this.selections.key(id) ? true : false);
25065     },
25066
25067
25068     // private
25069     handleMouseDBClick : function(e, t){
25070         
25071     },
25072     // private
25073     handleMouseDown : function(e, t)
25074     {
25075             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25076         if(this.isLocked() || rowIndex < 0 ){
25077             return;
25078         };
25079         if(e.shiftKey && this.last !== false){
25080             var last = this.last;
25081             this.selectRange(last, rowIndex, e.ctrlKey);
25082             this.last = last; // reset the last
25083             t.focus();
25084     
25085         }else{
25086             var isSelected = this.isSelected(rowIndex);
25087             //Roo.log("select row:" + rowIndex);
25088             if(isSelected){
25089                 this.deselectRow(rowIndex);
25090             } else {
25091                         this.selectRow(rowIndex, true);
25092             }
25093     
25094             /*
25095                 if(e.button !== 0 && isSelected){
25096                 alert('rowIndex 2: ' + rowIndex);
25097                     view.focusRow(rowIndex);
25098                 }else if(e.ctrlKey && isSelected){
25099                     this.deselectRow(rowIndex);
25100                 }else if(!isSelected){
25101                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25102                     view.focusRow(rowIndex);
25103                 }
25104             */
25105         }
25106         this.fireEvent("afterselectionchange", this);
25107     },
25108     // private
25109     handleDragableRowClick :  function(grid, rowIndex, e) 
25110     {
25111         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25112             this.selectRow(rowIndex, false);
25113             grid.view.focusRow(rowIndex);
25114              this.fireEvent("afterselectionchange", this);
25115         }
25116     },
25117     
25118     /**
25119      * Selects multiple rows.
25120      * @param {Array} rows Array of the indexes of the row to select
25121      * @param {Boolean} keepExisting (optional) True to keep existing selections
25122      */
25123     selectRows : function(rows, keepExisting){
25124         if(!keepExisting){
25125             this.clearSelections();
25126         }
25127         for(var i = 0, len = rows.length; i < len; i++){
25128             this.selectRow(rows[i], true);
25129         }
25130     },
25131
25132     /**
25133      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25134      * @param {Number} startRow The index of the first row in the range
25135      * @param {Number} endRow The index of the last row in the range
25136      * @param {Boolean} keepExisting (optional) True to retain existing selections
25137      */
25138     selectRange : function(startRow, endRow, keepExisting){
25139         if(this.locked) {
25140             return;
25141         }
25142         if(!keepExisting){
25143             this.clearSelections();
25144         }
25145         if(startRow <= endRow){
25146             for(var i = startRow; i <= endRow; i++){
25147                 this.selectRow(i, true);
25148             }
25149         }else{
25150             for(var i = startRow; i >= endRow; i--){
25151                 this.selectRow(i, true);
25152             }
25153         }
25154     },
25155
25156     /**
25157      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25158      * @param {Number} startRow The index of the first row in the range
25159      * @param {Number} endRow The index of the last row in the range
25160      */
25161     deselectRange : function(startRow, endRow, preventViewNotify){
25162         if(this.locked) {
25163             return;
25164         }
25165         for(var i = startRow; i <= endRow; i++){
25166             this.deselectRow(i, preventViewNotify);
25167         }
25168     },
25169
25170     /**
25171      * Selects a row.
25172      * @param {Number} row The index of the row to select
25173      * @param {Boolean} keepExisting (optional) True to keep existing selections
25174      */
25175     selectRow : function(index, keepExisting, preventViewNotify)
25176     {
25177             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25178             return;
25179         }
25180         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25181             if(!keepExisting || this.singleSelect){
25182                 this.clearSelections();
25183             }
25184             
25185             var r = this.grid.store.getAt(index);
25186             //console.log('selectRow - record id :' + r.id);
25187             
25188             this.selections.add(r);
25189             this.last = this.lastActive = index;
25190             if(!preventViewNotify){
25191                 var proxy = new Roo.Element(
25192                                 this.grid.getRowDom(index)
25193                 );
25194                 proxy.addClass('bg-info info');
25195             }
25196             this.fireEvent("rowselect", this, index, r);
25197             this.fireEvent("selectionchange", this);
25198         }
25199     },
25200
25201     /**
25202      * Deselects a row.
25203      * @param {Number} row The index of the row to deselect
25204      */
25205     deselectRow : function(index, preventViewNotify)
25206     {
25207         if(this.locked) {
25208             return;
25209         }
25210         if(this.last == index){
25211             this.last = false;
25212         }
25213         if(this.lastActive == index){
25214             this.lastActive = false;
25215         }
25216         
25217         var r = this.grid.store.getAt(index);
25218         if (!r) {
25219             return;
25220         }
25221         
25222         this.selections.remove(r);
25223         //.console.log('deselectRow - record id :' + r.id);
25224         if(!preventViewNotify){
25225         
25226             var proxy = new Roo.Element(
25227                 this.grid.getRowDom(index)
25228             );
25229             proxy.removeClass('bg-info info');
25230         }
25231         this.fireEvent("rowdeselect", this, index);
25232         this.fireEvent("selectionchange", this);
25233     },
25234
25235     // private
25236     restoreLast : function(){
25237         if(this._last){
25238             this.last = this._last;
25239         }
25240     },
25241
25242     // private
25243     acceptsNav : function(row, col, cm){
25244         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25245     },
25246
25247     // private
25248     onEditorKey : function(field, e){
25249         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25250         if(k == e.TAB){
25251             e.stopEvent();
25252             ed.completeEdit();
25253             if(e.shiftKey){
25254                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25255             }else{
25256                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25257             }
25258         }else if(k == e.ENTER && !e.ctrlKey){
25259             e.stopEvent();
25260             ed.completeEdit();
25261             if(e.shiftKey){
25262                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25263             }else{
25264                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25265             }
25266         }else if(k == e.ESC){
25267             ed.cancelEdit();
25268         }
25269         if(newCell){
25270             g.startEditing(newCell[0], newCell[1]);
25271         }
25272     }
25273 });
25274 /*
25275  * Based on:
25276  * Ext JS Library 1.1.1
25277  * Copyright(c) 2006-2007, Ext JS, LLC.
25278  *
25279  * Originally Released Under LGPL - original licence link has changed is not relivant.
25280  *
25281  * Fork - LGPL
25282  * <script type="text/javascript">
25283  */
25284  
25285 /**
25286  * @class Roo.bootstrap.PagingToolbar
25287  * @extends Roo.bootstrap.NavSimplebar
25288  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25289  * @constructor
25290  * Create a new PagingToolbar
25291  * @param {Object} config The config object
25292  * @param {Roo.data.Store} store
25293  */
25294 Roo.bootstrap.PagingToolbar = function(config)
25295 {
25296     // old args format still supported... - xtype is prefered..
25297         // created from xtype...
25298     
25299     this.ds = config.dataSource;
25300     
25301     if (config.store && !this.ds) {
25302         this.store= Roo.factory(config.store, Roo.data);
25303         this.ds = this.store;
25304         this.ds.xmodule = this.xmodule || false;
25305     }
25306     
25307     this.toolbarItems = [];
25308     if (config.items) {
25309         this.toolbarItems = config.items;
25310     }
25311     
25312     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25313     
25314     this.cursor = 0;
25315     
25316     if (this.ds) { 
25317         this.bind(this.ds);
25318     }
25319     
25320     if (Roo.bootstrap.version == 4) {
25321         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25322     } else {
25323         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25324     }
25325     
25326 };
25327
25328 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25329     /**
25330      * @cfg {Roo.data.Store} dataSource
25331      * The underlying data store providing the paged data
25332      */
25333     /**
25334      * @cfg {String/HTMLElement/Element} container
25335      * container The id or element that will contain the toolbar
25336      */
25337     /**
25338      * @cfg {Boolean} displayInfo
25339      * True to display the displayMsg (defaults to false)
25340      */
25341     /**
25342      * @cfg {Number} pageSize
25343      * The number of records to display per page (defaults to 20)
25344      */
25345     pageSize: 20,
25346     /**
25347      * @cfg {String} displayMsg
25348      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25349      */
25350     displayMsg : 'Displaying {0} - {1} of {2}',
25351     /**
25352      * @cfg {String} emptyMsg
25353      * The message to display when no records are found (defaults to "No data to display")
25354      */
25355     emptyMsg : 'No data to display',
25356     /**
25357      * Customizable piece of the default paging text (defaults to "Page")
25358      * @type String
25359      */
25360     beforePageText : "Page",
25361     /**
25362      * Customizable piece of the default paging text (defaults to "of %0")
25363      * @type String
25364      */
25365     afterPageText : "of {0}",
25366     /**
25367      * Customizable piece of the default paging text (defaults to "First Page")
25368      * @type String
25369      */
25370     firstText : "First Page",
25371     /**
25372      * Customizable piece of the default paging text (defaults to "Previous Page")
25373      * @type String
25374      */
25375     prevText : "Previous Page",
25376     /**
25377      * Customizable piece of the default paging text (defaults to "Next Page")
25378      * @type String
25379      */
25380     nextText : "Next Page",
25381     /**
25382      * Customizable piece of the default paging text (defaults to "Last Page")
25383      * @type String
25384      */
25385     lastText : "Last Page",
25386     /**
25387      * Customizable piece of the default paging text (defaults to "Refresh")
25388      * @type String
25389      */
25390     refreshText : "Refresh",
25391
25392     buttons : false,
25393     // private
25394     onRender : function(ct, position) 
25395     {
25396         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25397         this.navgroup.parentId = this.id;
25398         this.navgroup.onRender(this.el, null);
25399         // add the buttons to the navgroup
25400         
25401         if(this.displayInfo){
25402             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25403             this.displayEl = this.el.select('.x-paging-info', true).first();
25404 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25405 //            this.displayEl = navel.el.select('span',true).first();
25406         }
25407         
25408         var _this = this;
25409         
25410         if(this.buttons){
25411             Roo.each(_this.buttons, function(e){ // this might need to use render????
25412                Roo.factory(e).render(_this.el);
25413             });
25414         }
25415             
25416         Roo.each(_this.toolbarItems, function(e) {
25417             _this.navgroup.addItem(e);
25418         });
25419         
25420         
25421         this.first = this.navgroup.addItem({
25422             tooltip: this.firstText,
25423             cls: "prev btn-outline-secondary",
25424             html : ' <i class="fa fa-step-backward"></i>',
25425             disabled: true,
25426             preventDefault: true,
25427             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25428         });
25429         
25430         this.prev =  this.navgroup.addItem({
25431             tooltip: this.prevText,
25432             cls: "prev btn-outline-secondary",
25433             html : ' <i class="fa fa-backward"></i>',
25434             disabled: true,
25435             preventDefault: true,
25436             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25437         });
25438     //this.addSeparator();
25439         
25440         
25441         var field = this.navgroup.addItem( {
25442             tagtype : 'span',
25443             cls : 'x-paging-position  btn-outline-secondary',
25444              disabled: true,
25445             html : this.beforePageText  +
25446                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25447                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25448          } ); //?? escaped?
25449         
25450         this.field = field.el.select('input', true).first();
25451         this.field.on("keydown", this.onPagingKeydown, this);
25452         this.field.on("focus", function(){this.dom.select();});
25453     
25454     
25455         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25456         //this.field.setHeight(18);
25457         //this.addSeparator();
25458         this.next = this.navgroup.addItem({
25459             tooltip: this.nextText,
25460             cls: "next btn-outline-secondary",
25461             html : ' <i class="fa fa-forward"></i>',
25462             disabled: true,
25463             preventDefault: true,
25464             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25465         });
25466         this.last = this.navgroup.addItem({
25467             tooltip: this.lastText,
25468             html : ' <i class="fa fa-step-forward"></i>',
25469             cls: "next btn-outline-secondary",
25470             disabled: true,
25471             preventDefault: true,
25472             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25473         });
25474     //this.addSeparator();
25475         this.loading = this.navgroup.addItem({
25476             tooltip: this.refreshText,
25477             cls: "btn-outline-secondary",
25478             html : ' <i class="fa fa-refresh"></i>',
25479             preventDefault: true,
25480             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25481         });
25482         
25483     },
25484
25485     // private
25486     updateInfo : function(){
25487         if(this.displayEl){
25488             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25489             var msg = count == 0 ?
25490                 this.emptyMsg :
25491                 String.format(
25492                     this.displayMsg,
25493                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25494                 );
25495             this.displayEl.update(msg);
25496         }
25497     },
25498
25499     // private
25500     onLoad : function(ds, r, o)
25501     {
25502         this.cursor = o.params.start ? o.params.start : 0;
25503         
25504         var d = this.getPageData(),
25505             ap = d.activePage,
25506             ps = d.pages;
25507         
25508         
25509         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25510         this.field.dom.value = ap;
25511         this.first.setDisabled(ap == 1);
25512         this.prev.setDisabled(ap == 1);
25513         this.next.setDisabled(ap == ps);
25514         this.last.setDisabled(ap == ps);
25515         this.loading.enable();
25516         this.updateInfo();
25517     },
25518
25519     // private
25520     getPageData : function(){
25521         var total = this.ds.getTotalCount();
25522         return {
25523             total : total,
25524             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25525             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25526         };
25527     },
25528
25529     // private
25530     onLoadError : function(){
25531         this.loading.enable();
25532     },
25533
25534     // private
25535     onPagingKeydown : function(e){
25536         var k = e.getKey();
25537         var d = this.getPageData();
25538         if(k == e.RETURN){
25539             var v = this.field.dom.value, pageNum;
25540             if(!v || isNaN(pageNum = parseInt(v, 10))){
25541                 this.field.dom.value = d.activePage;
25542                 return;
25543             }
25544             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25545             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25546             e.stopEvent();
25547         }
25548         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))
25549         {
25550           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25551           this.field.dom.value = pageNum;
25552           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25553           e.stopEvent();
25554         }
25555         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25556         {
25557           var v = this.field.dom.value, pageNum; 
25558           var increment = (e.shiftKey) ? 10 : 1;
25559           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25560                 increment *= -1;
25561           }
25562           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25563             this.field.dom.value = d.activePage;
25564             return;
25565           }
25566           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25567           {
25568             this.field.dom.value = parseInt(v, 10) + increment;
25569             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25570             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25571           }
25572           e.stopEvent();
25573         }
25574     },
25575
25576     // private
25577     beforeLoad : function(){
25578         if(this.loading){
25579             this.loading.disable();
25580         }
25581     },
25582
25583     // private
25584     onClick : function(which){
25585         
25586         var ds = this.ds;
25587         if (!ds) {
25588             return;
25589         }
25590         
25591         switch(which){
25592             case "first":
25593                 ds.load({params:{start: 0, limit: this.pageSize}});
25594             break;
25595             case "prev":
25596                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25597             break;
25598             case "next":
25599                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25600             break;
25601             case "last":
25602                 var total = ds.getTotalCount();
25603                 var extra = total % this.pageSize;
25604                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25605                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25606             break;
25607             case "refresh":
25608                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25609             break;
25610         }
25611     },
25612
25613     /**
25614      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25615      * @param {Roo.data.Store} store The data store to unbind
25616      */
25617     unbind : function(ds){
25618         ds.un("beforeload", this.beforeLoad, this);
25619         ds.un("load", this.onLoad, this);
25620         ds.un("loadexception", this.onLoadError, this);
25621         ds.un("remove", this.updateInfo, this);
25622         ds.un("add", this.updateInfo, this);
25623         this.ds = undefined;
25624     },
25625
25626     /**
25627      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25628      * @param {Roo.data.Store} store The data store to bind
25629      */
25630     bind : function(ds){
25631         ds.on("beforeload", this.beforeLoad, this);
25632         ds.on("load", this.onLoad, this);
25633         ds.on("loadexception", this.onLoadError, this);
25634         ds.on("remove", this.updateInfo, this);
25635         ds.on("add", this.updateInfo, this);
25636         this.ds = ds;
25637     }
25638 });/*
25639  * - LGPL
25640  *
25641  * element
25642  * 
25643  */
25644
25645 /**
25646  * @class Roo.bootstrap.MessageBar
25647  * @extends Roo.bootstrap.Component
25648  * Bootstrap MessageBar class
25649  * @cfg {String} html contents of the MessageBar
25650  * @cfg {String} weight (info | success | warning | danger) default info
25651  * @cfg {String} beforeClass insert the bar before the given class
25652  * @cfg {Boolean} closable (true | false) default false
25653  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25654  * 
25655  * @constructor
25656  * Create a new Element
25657  * @param {Object} config The config object
25658  */
25659
25660 Roo.bootstrap.MessageBar = function(config){
25661     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25662 };
25663
25664 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25665     
25666     html: '',
25667     weight: 'info',
25668     closable: false,
25669     fixed: false,
25670     beforeClass: 'bootstrap-sticky-wrap',
25671     
25672     getAutoCreate : function(){
25673         
25674         var cfg = {
25675             tag: 'div',
25676             cls: 'alert alert-dismissable alert-' + this.weight,
25677             cn: [
25678                 {
25679                     tag: 'span',
25680                     cls: 'message',
25681                     html: this.html || ''
25682                 }
25683             ]
25684         };
25685         
25686         if(this.fixed){
25687             cfg.cls += ' alert-messages-fixed';
25688         }
25689         
25690         if(this.closable){
25691             cfg.cn.push({
25692                 tag: 'button',
25693                 cls: 'close',
25694                 html: 'x'
25695             });
25696         }
25697         
25698         return cfg;
25699     },
25700     
25701     onRender : function(ct, position)
25702     {
25703         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25704         
25705         if(!this.el){
25706             var cfg = Roo.apply({},  this.getAutoCreate());
25707             cfg.id = Roo.id();
25708             
25709             if (this.cls) {
25710                 cfg.cls += ' ' + this.cls;
25711             }
25712             if (this.style) {
25713                 cfg.style = this.style;
25714             }
25715             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25716             
25717             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25718         }
25719         
25720         this.el.select('>button.close').on('click', this.hide, this);
25721         
25722     },
25723     
25724     show : function()
25725     {
25726         if (!this.rendered) {
25727             this.render();
25728         }
25729         
25730         this.el.show();
25731         
25732         this.fireEvent('show', this);
25733         
25734     },
25735     
25736     hide : function()
25737     {
25738         if (!this.rendered) {
25739             this.render();
25740         }
25741         
25742         this.el.hide();
25743         
25744         this.fireEvent('hide', this);
25745     },
25746     
25747     update : function()
25748     {
25749 //        var e = this.el.dom.firstChild;
25750 //        
25751 //        if(this.closable){
25752 //            e = e.nextSibling;
25753 //        }
25754 //        
25755 //        e.data = this.html || '';
25756
25757         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25758     }
25759    
25760 });
25761
25762  
25763
25764      /*
25765  * - LGPL
25766  *
25767  * Graph
25768  * 
25769  */
25770
25771
25772 /**
25773  * @class Roo.bootstrap.Graph
25774  * @extends Roo.bootstrap.Component
25775  * Bootstrap Graph class
25776 > Prameters
25777  -sm {number} sm 4
25778  -md {number} md 5
25779  @cfg {String} graphtype  bar | vbar | pie
25780  @cfg {number} g_x coodinator | centre x (pie)
25781  @cfg {number} g_y coodinator | centre y (pie)
25782  @cfg {number} g_r radius (pie)
25783  @cfg {number} g_height height of the chart (respected by all elements in the set)
25784  @cfg {number} g_width width of the chart (respected by all elements in the set)
25785  @cfg {Object} title The title of the chart
25786     
25787  -{Array}  values
25788  -opts (object) options for the chart 
25789      o {
25790      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25791      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25792      o vgutter (number)
25793      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.
25794      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25795      o to
25796      o stretch (boolean)
25797      o }
25798  -opts (object) options for the pie
25799      o{
25800      o cut
25801      o startAngle (number)
25802      o endAngle (number)
25803      } 
25804  *
25805  * @constructor
25806  * Create a new Input
25807  * @param {Object} config The config object
25808  */
25809
25810 Roo.bootstrap.Graph = function(config){
25811     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25812     
25813     this.addEvents({
25814         // img events
25815         /**
25816          * @event click
25817          * The img click event for the img.
25818          * @param {Roo.EventObject} e
25819          */
25820         "click" : true
25821     });
25822 };
25823
25824 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25825     
25826     sm: 4,
25827     md: 5,
25828     graphtype: 'bar',
25829     g_height: 250,
25830     g_width: 400,
25831     g_x: 50,
25832     g_y: 50,
25833     g_r: 30,
25834     opts:{
25835         //g_colors: this.colors,
25836         g_type: 'soft',
25837         g_gutter: '20%'
25838
25839     },
25840     title : false,
25841
25842     getAutoCreate : function(){
25843         
25844         var cfg = {
25845             tag: 'div',
25846             html : null
25847         };
25848         
25849         
25850         return  cfg;
25851     },
25852
25853     onRender : function(ct,position){
25854         
25855         
25856         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25857         
25858         if (typeof(Raphael) == 'undefined') {
25859             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25860             return;
25861         }
25862         
25863         this.raphael = Raphael(this.el.dom);
25864         
25865                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25866                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25867                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25868                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25869                 /*
25870                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25871                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25872                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25873                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25874                 
25875                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25876                 r.barchart(330, 10, 300, 220, data1);
25877                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25878                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25879                 */
25880                 
25881                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25882                 // r.barchart(30, 30, 560, 250,  xdata, {
25883                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25884                 //     axis : "0 0 1 1",
25885                 //     axisxlabels :  xdata
25886                 //     //yvalues : cols,
25887                    
25888                 // });
25889 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25890 //        
25891 //        this.load(null,xdata,{
25892 //                axis : "0 0 1 1",
25893 //                axisxlabels :  xdata
25894 //                });
25895
25896     },
25897
25898     load : function(graphtype,xdata,opts)
25899     {
25900         this.raphael.clear();
25901         if(!graphtype) {
25902             graphtype = this.graphtype;
25903         }
25904         if(!opts){
25905             opts = this.opts;
25906         }
25907         var r = this.raphael,
25908             fin = function () {
25909                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25910             },
25911             fout = function () {
25912                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25913             },
25914             pfin = function() {
25915                 this.sector.stop();
25916                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25917
25918                 if (this.label) {
25919                     this.label[0].stop();
25920                     this.label[0].attr({ r: 7.5 });
25921                     this.label[1].attr({ "font-weight": 800 });
25922                 }
25923             },
25924             pfout = function() {
25925                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25926
25927                 if (this.label) {
25928                     this.label[0].animate({ r: 5 }, 500, "bounce");
25929                     this.label[1].attr({ "font-weight": 400 });
25930                 }
25931             };
25932
25933         switch(graphtype){
25934             case 'bar':
25935                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25936                 break;
25937             case 'hbar':
25938                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25939                 break;
25940             case 'pie':
25941 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25942 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25943 //            
25944                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25945                 
25946                 break;
25947
25948         }
25949         
25950         if(this.title){
25951             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25952         }
25953         
25954     },
25955     
25956     setTitle: function(o)
25957     {
25958         this.title = o;
25959     },
25960     
25961     initEvents: function() {
25962         
25963         if(!this.href){
25964             this.el.on('click', this.onClick, this);
25965         }
25966     },
25967     
25968     onClick : function(e)
25969     {
25970         Roo.log('img onclick');
25971         this.fireEvent('click', this, e);
25972     }
25973    
25974 });
25975
25976  
25977 /*
25978  * - LGPL
25979  *
25980  * numberBox
25981  * 
25982  */
25983 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25984
25985 /**
25986  * @class Roo.bootstrap.dash.NumberBox
25987  * @extends Roo.bootstrap.Component
25988  * Bootstrap NumberBox class
25989  * @cfg {String} headline Box headline
25990  * @cfg {String} content Box content
25991  * @cfg {String} icon Box icon
25992  * @cfg {String} footer Footer text
25993  * @cfg {String} fhref Footer href
25994  * 
25995  * @constructor
25996  * Create a new NumberBox
25997  * @param {Object} config The config object
25998  */
25999
26000
26001 Roo.bootstrap.dash.NumberBox = function(config){
26002     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26003     
26004 };
26005
26006 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26007     
26008     headline : '',
26009     content : '',
26010     icon : '',
26011     footer : '',
26012     fhref : '',
26013     ficon : '',
26014     
26015     getAutoCreate : function(){
26016         
26017         var cfg = {
26018             tag : 'div',
26019             cls : 'small-box ',
26020             cn : [
26021                 {
26022                     tag : 'div',
26023                     cls : 'inner',
26024                     cn :[
26025                         {
26026                             tag : 'h3',
26027                             cls : 'roo-headline',
26028                             html : this.headline
26029                         },
26030                         {
26031                             tag : 'p',
26032                             cls : 'roo-content',
26033                             html : this.content
26034                         }
26035                     ]
26036                 }
26037             ]
26038         };
26039         
26040         if(this.icon){
26041             cfg.cn.push({
26042                 tag : 'div',
26043                 cls : 'icon',
26044                 cn :[
26045                     {
26046                         tag : 'i',
26047                         cls : 'ion ' + this.icon
26048                     }
26049                 ]
26050             });
26051         }
26052         
26053         if(this.footer){
26054             var footer = {
26055                 tag : 'a',
26056                 cls : 'small-box-footer',
26057                 href : this.fhref || '#',
26058                 html : this.footer
26059             };
26060             
26061             cfg.cn.push(footer);
26062             
26063         }
26064         
26065         return  cfg;
26066     },
26067
26068     onRender : function(ct,position){
26069         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26070
26071
26072        
26073                 
26074     },
26075
26076     setHeadline: function (value)
26077     {
26078         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26079     },
26080     
26081     setFooter: function (value, href)
26082     {
26083         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26084         
26085         if(href){
26086             this.el.select('a.small-box-footer',true).first().attr('href', href);
26087         }
26088         
26089     },
26090
26091     setContent: function (value)
26092     {
26093         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26094     },
26095
26096     initEvents: function() 
26097     {   
26098         
26099     }
26100     
26101 });
26102
26103  
26104 /*
26105  * - LGPL
26106  *
26107  * TabBox
26108  * 
26109  */
26110 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26111
26112 /**
26113  * @class Roo.bootstrap.dash.TabBox
26114  * @extends Roo.bootstrap.Component
26115  * Bootstrap TabBox class
26116  * @cfg {String} title Title of the TabBox
26117  * @cfg {String} icon Icon of the TabBox
26118  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26119  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26120  * 
26121  * @constructor
26122  * Create a new TabBox
26123  * @param {Object} config The config object
26124  */
26125
26126
26127 Roo.bootstrap.dash.TabBox = function(config){
26128     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26129     this.addEvents({
26130         // raw events
26131         /**
26132          * @event addpane
26133          * When a pane is added
26134          * @param {Roo.bootstrap.dash.TabPane} pane
26135          */
26136         "addpane" : true,
26137         /**
26138          * @event activatepane
26139          * When a pane is activated
26140          * @param {Roo.bootstrap.dash.TabPane} pane
26141          */
26142         "activatepane" : true
26143         
26144          
26145     });
26146     
26147     this.panes = [];
26148 };
26149
26150 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26151
26152     title : '',
26153     icon : false,
26154     showtabs : true,
26155     tabScrollable : false,
26156     
26157     getChildContainer : function()
26158     {
26159         return this.el.select('.tab-content', true).first();
26160     },
26161     
26162     getAutoCreate : function(){
26163         
26164         var header = {
26165             tag: 'li',
26166             cls: 'pull-left header',
26167             html: this.title,
26168             cn : []
26169         };
26170         
26171         if(this.icon){
26172             header.cn.push({
26173                 tag: 'i',
26174                 cls: 'fa ' + this.icon
26175             });
26176         }
26177         
26178         var h = {
26179             tag: 'ul',
26180             cls: 'nav nav-tabs pull-right',
26181             cn: [
26182                 header
26183             ]
26184         };
26185         
26186         if(this.tabScrollable){
26187             h = {
26188                 tag: 'div',
26189                 cls: 'tab-header',
26190                 cn: [
26191                     {
26192                         tag: 'ul',
26193                         cls: 'nav nav-tabs pull-right',
26194                         cn: [
26195                             header
26196                         ]
26197                     }
26198                 ]
26199             };
26200         }
26201         
26202         var cfg = {
26203             tag: 'div',
26204             cls: 'nav-tabs-custom',
26205             cn: [
26206                 h,
26207                 {
26208                     tag: 'div',
26209                     cls: 'tab-content no-padding',
26210                     cn: []
26211                 }
26212             ]
26213         };
26214
26215         return  cfg;
26216     },
26217     initEvents : function()
26218     {
26219         //Roo.log('add add pane handler');
26220         this.on('addpane', this.onAddPane, this);
26221     },
26222      /**
26223      * Updates the box title
26224      * @param {String} html to set the title to.
26225      */
26226     setTitle : function(value)
26227     {
26228         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26229     },
26230     onAddPane : function(pane)
26231     {
26232         this.panes.push(pane);
26233         //Roo.log('addpane');
26234         //Roo.log(pane);
26235         // tabs are rendere left to right..
26236         if(!this.showtabs){
26237             return;
26238         }
26239         
26240         var ctr = this.el.select('.nav-tabs', true).first();
26241          
26242          
26243         var existing = ctr.select('.nav-tab',true);
26244         var qty = existing.getCount();;
26245         
26246         
26247         var tab = ctr.createChild({
26248             tag : 'li',
26249             cls : 'nav-tab' + (qty ? '' : ' active'),
26250             cn : [
26251                 {
26252                     tag : 'a',
26253                     href:'#',
26254                     html : pane.title
26255                 }
26256             ]
26257         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26258         pane.tab = tab;
26259         
26260         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26261         if (!qty) {
26262             pane.el.addClass('active');
26263         }
26264         
26265                 
26266     },
26267     onTabClick : function(ev,un,ob,pane)
26268     {
26269         //Roo.log('tab - prev default');
26270         ev.preventDefault();
26271         
26272         
26273         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26274         pane.tab.addClass('active');
26275         //Roo.log(pane.title);
26276         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26277         // technically we should have a deactivate event.. but maybe add later.
26278         // and it should not de-activate the selected tab...
26279         this.fireEvent('activatepane', pane);
26280         pane.el.addClass('active');
26281         pane.fireEvent('activate');
26282         
26283         
26284     },
26285     
26286     getActivePane : function()
26287     {
26288         var r = false;
26289         Roo.each(this.panes, function(p) {
26290             if(p.el.hasClass('active')){
26291                 r = p;
26292                 return false;
26293             }
26294             
26295             return;
26296         });
26297         
26298         return r;
26299     }
26300     
26301     
26302 });
26303
26304  
26305 /*
26306  * - LGPL
26307  *
26308  * Tab pane
26309  * 
26310  */
26311 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26312 /**
26313  * @class Roo.bootstrap.TabPane
26314  * @extends Roo.bootstrap.Component
26315  * Bootstrap TabPane class
26316  * @cfg {Boolean} active (false | true) Default false
26317  * @cfg {String} title title of panel
26318
26319  * 
26320  * @constructor
26321  * Create a new TabPane
26322  * @param {Object} config The config object
26323  */
26324
26325 Roo.bootstrap.dash.TabPane = function(config){
26326     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26327     
26328     this.addEvents({
26329         // raw events
26330         /**
26331          * @event activate
26332          * When a pane is activated
26333          * @param {Roo.bootstrap.dash.TabPane} pane
26334          */
26335         "activate" : true
26336          
26337     });
26338 };
26339
26340 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26341     
26342     active : false,
26343     title : '',
26344     
26345     // the tabBox that this is attached to.
26346     tab : false,
26347      
26348     getAutoCreate : function() 
26349     {
26350         var cfg = {
26351             tag: 'div',
26352             cls: 'tab-pane'
26353         };
26354         
26355         if(this.active){
26356             cfg.cls += ' active';
26357         }
26358         
26359         return cfg;
26360     },
26361     initEvents  : function()
26362     {
26363         //Roo.log('trigger add pane handler');
26364         this.parent().fireEvent('addpane', this)
26365     },
26366     
26367      /**
26368      * Updates the tab title 
26369      * @param {String} html to set the title to.
26370      */
26371     setTitle: function(str)
26372     {
26373         if (!this.tab) {
26374             return;
26375         }
26376         this.title = str;
26377         this.tab.select('a', true).first().dom.innerHTML = str;
26378         
26379     }
26380     
26381     
26382     
26383 });
26384
26385  
26386
26387
26388  /*
26389  * - LGPL
26390  *
26391  * menu
26392  * 
26393  */
26394 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26395
26396 /**
26397  * @class Roo.bootstrap.menu.Menu
26398  * @extends Roo.bootstrap.Component
26399  * Bootstrap Menu class - container for Menu
26400  * @cfg {String} html Text of the menu
26401  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26402  * @cfg {String} icon Font awesome icon
26403  * @cfg {String} pos Menu align to (top | bottom) default bottom
26404  * 
26405  * 
26406  * @constructor
26407  * Create a new Menu
26408  * @param {Object} config The config object
26409  */
26410
26411
26412 Roo.bootstrap.menu.Menu = function(config){
26413     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26414     
26415     this.addEvents({
26416         /**
26417          * @event beforeshow
26418          * Fires before this menu is displayed
26419          * @param {Roo.bootstrap.menu.Menu} this
26420          */
26421         beforeshow : true,
26422         /**
26423          * @event beforehide
26424          * Fires before this menu is hidden
26425          * @param {Roo.bootstrap.menu.Menu} this
26426          */
26427         beforehide : true,
26428         /**
26429          * @event show
26430          * Fires after this menu is displayed
26431          * @param {Roo.bootstrap.menu.Menu} this
26432          */
26433         show : true,
26434         /**
26435          * @event hide
26436          * Fires after this menu is hidden
26437          * @param {Roo.bootstrap.menu.Menu} this
26438          */
26439         hide : true,
26440         /**
26441          * @event click
26442          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26443          * @param {Roo.bootstrap.menu.Menu} this
26444          * @param {Roo.EventObject} e
26445          */
26446         click : true
26447     });
26448     
26449 };
26450
26451 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26452     
26453     submenu : false,
26454     html : '',
26455     weight : 'default',
26456     icon : false,
26457     pos : 'bottom',
26458     
26459     
26460     getChildContainer : function() {
26461         if(this.isSubMenu){
26462             return this.el;
26463         }
26464         
26465         return this.el.select('ul.dropdown-menu', true).first();  
26466     },
26467     
26468     getAutoCreate : function()
26469     {
26470         var text = [
26471             {
26472                 tag : 'span',
26473                 cls : 'roo-menu-text',
26474                 html : this.html
26475             }
26476         ];
26477         
26478         if(this.icon){
26479             text.unshift({
26480                 tag : 'i',
26481                 cls : 'fa ' + this.icon
26482             })
26483         }
26484         
26485         
26486         var cfg = {
26487             tag : 'div',
26488             cls : 'btn-group',
26489             cn : [
26490                 {
26491                     tag : 'button',
26492                     cls : 'dropdown-button btn btn-' + this.weight,
26493                     cn : text
26494                 },
26495                 {
26496                     tag : 'button',
26497                     cls : 'dropdown-toggle btn btn-' + this.weight,
26498                     cn : [
26499                         {
26500                             tag : 'span',
26501                             cls : 'caret'
26502                         }
26503                     ]
26504                 },
26505                 {
26506                     tag : 'ul',
26507                     cls : 'dropdown-menu'
26508                 }
26509             ]
26510             
26511         };
26512         
26513         if(this.pos == 'top'){
26514             cfg.cls += ' dropup';
26515         }
26516         
26517         if(this.isSubMenu){
26518             cfg = {
26519                 tag : 'ul',
26520                 cls : 'dropdown-menu'
26521             }
26522         }
26523         
26524         return cfg;
26525     },
26526     
26527     onRender : function(ct, position)
26528     {
26529         this.isSubMenu = ct.hasClass('dropdown-submenu');
26530         
26531         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26532     },
26533     
26534     initEvents : function() 
26535     {
26536         if(this.isSubMenu){
26537             return;
26538         }
26539         
26540         this.hidden = true;
26541         
26542         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26543         this.triggerEl.on('click', this.onTriggerPress, this);
26544         
26545         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26546         this.buttonEl.on('click', this.onClick, this);
26547         
26548     },
26549     
26550     list : function()
26551     {
26552         if(this.isSubMenu){
26553             return this.el;
26554         }
26555         
26556         return this.el.select('ul.dropdown-menu', true).first();
26557     },
26558     
26559     onClick : function(e)
26560     {
26561         this.fireEvent("click", this, e);
26562     },
26563     
26564     onTriggerPress  : function(e)
26565     {   
26566         if (this.isVisible()) {
26567             this.hide();
26568         } else {
26569             this.show();
26570         }
26571     },
26572     
26573     isVisible : function(){
26574         return !this.hidden;
26575     },
26576     
26577     show : function()
26578     {
26579         this.fireEvent("beforeshow", this);
26580         
26581         this.hidden = false;
26582         this.el.addClass('open');
26583         
26584         Roo.get(document).on("mouseup", this.onMouseUp, this);
26585         
26586         this.fireEvent("show", this);
26587         
26588         
26589     },
26590     
26591     hide : function()
26592     {
26593         this.fireEvent("beforehide", this);
26594         
26595         this.hidden = true;
26596         this.el.removeClass('open');
26597         
26598         Roo.get(document).un("mouseup", this.onMouseUp);
26599         
26600         this.fireEvent("hide", this);
26601     },
26602     
26603     onMouseUp : function()
26604     {
26605         this.hide();
26606     }
26607     
26608 });
26609
26610  
26611  /*
26612  * - LGPL
26613  *
26614  * menu item
26615  * 
26616  */
26617 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26618
26619 /**
26620  * @class Roo.bootstrap.menu.Item
26621  * @extends Roo.bootstrap.Component
26622  * Bootstrap MenuItem class
26623  * @cfg {Boolean} submenu (true | false) default false
26624  * @cfg {String} html text of the item
26625  * @cfg {String} href the link
26626  * @cfg {Boolean} disable (true | false) default false
26627  * @cfg {Boolean} preventDefault (true | false) default true
26628  * @cfg {String} icon Font awesome icon
26629  * @cfg {String} pos Submenu align to (left | right) default right 
26630  * 
26631  * 
26632  * @constructor
26633  * Create a new Item
26634  * @param {Object} config The config object
26635  */
26636
26637
26638 Roo.bootstrap.menu.Item = function(config){
26639     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26640     this.addEvents({
26641         /**
26642          * @event mouseover
26643          * Fires when the mouse is hovering over this menu
26644          * @param {Roo.bootstrap.menu.Item} this
26645          * @param {Roo.EventObject} e
26646          */
26647         mouseover : true,
26648         /**
26649          * @event mouseout
26650          * Fires when the mouse exits this menu
26651          * @param {Roo.bootstrap.menu.Item} this
26652          * @param {Roo.EventObject} e
26653          */
26654         mouseout : true,
26655         // raw events
26656         /**
26657          * @event click
26658          * The raw click event for the entire grid.
26659          * @param {Roo.EventObject} e
26660          */
26661         click : true
26662     });
26663 };
26664
26665 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26666     
26667     submenu : false,
26668     href : '',
26669     html : '',
26670     preventDefault: true,
26671     disable : false,
26672     icon : false,
26673     pos : 'right',
26674     
26675     getAutoCreate : function()
26676     {
26677         var text = [
26678             {
26679                 tag : 'span',
26680                 cls : 'roo-menu-item-text',
26681                 html : this.html
26682             }
26683         ];
26684         
26685         if(this.icon){
26686             text.unshift({
26687                 tag : 'i',
26688                 cls : 'fa ' + this.icon
26689             })
26690         }
26691         
26692         var cfg = {
26693             tag : 'li',
26694             cn : [
26695                 {
26696                     tag : 'a',
26697                     href : this.href || '#',
26698                     cn : text
26699                 }
26700             ]
26701         };
26702         
26703         if(this.disable){
26704             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26705         }
26706         
26707         if(this.submenu){
26708             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26709             
26710             if(this.pos == 'left'){
26711                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26712             }
26713         }
26714         
26715         return cfg;
26716     },
26717     
26718     initEvents : function() 
26719     {
26720         this.el.on('mouseover', this.onMouseOver, this);
26721         this.el.on('mouseout', this.onMouseOut, this);
26722         
26723         this.el.select('a', true).first().on('click', this.onClick, this);
26724         
26725     },
26726     
26727     onClick : function(e)
26728     {
26729         if(this.preventDefault){
26730             e.preventDefault();
26731         }
26732         
26733         this.fireEvent("click", this, e);
26734     },
26735     
26736     onMouseOver : function(e)
26737     {
26738         if(this.submenu && this.pos == 'left'){
26739             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26740         }
26741         
26742         this.fireEvent("mouseover", this, e);
26743     },
26744     
26745     onMouseOut : function(e)
26746     {
26747         this.fireEvent("mouseout", this, e);
26748     }
26749 });
26750
26751  
26752
26753  /*
26754  * - LGPL
26755  *
26756  * menu separator
26757  * 
26758  */
26759 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26760
26761 /**
26762  * @class Roo.bootstrap.menu.Separator
26763  * @extends Roo.bootstrap.Component
26764  * Bootstrap Separator class
26765  * 
26766  * @constructor
26767  * Create a new Separator
26768  * @param {Object} config The config object
26769  */
26770
26771
26772 Roo.bootstrap.menu.Separator = function(config){
26773     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26774 };
26775
26776 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26777     
26778     getAutoCreate : function(){
26779         var cfg = {
26780             tag : 'li',
26781             cls: 'divider'
26782         };
26783         
26784         return cfg;
26785     }
26786    
26787 });
26788
26789  
26790
26791  /*
26792  * - LGPL
26793  *
26794  * Tooltip
26795  * 
26796  */
26797
26798 /**
26799  * @class Roo.bootstrap.Tooltip
26800  * Bootstrap Tooltip class
26801  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26802  * to determine which dom element triggers the tooltip.
26803  * 
26804  * It needs to add support for additional attributes like tooltip-position
26805  * 
26806  * @constructor
26807  * Create a new Toolti
26808  * @param {Object} config The config object
26809  */
26810
26811 Roo.bootstrap.Tooltip = function(config){
26812     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26813     
26814     this.alignment = Roo.bootstrap.Tooltip.alignment;
26815     
26816     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26817         this.alignment = config.alignment;
26818     }
26819     
26820 };
26821
26822 Roo.apply(Roo.bootstrap.Tooltip, {
26823     /**
26824      * @function init initialize tooltip monitoring.
26825      * @static
26826      */
26827     currentEl : false,
26828     currentTip : false,
26829     currentRegion : false,
26830     
26831     //  init : delay?
26832     
26833     init : function()
26834     {
26835         Roo.get(document).on('mouseover', this.enter ,this);
26836         Roo.get(document).on('mouseout', this.leave, this);
26837          
26838         
26839         this.currentTip = new Roo.bootstrap.Tooltip();
26840     },
26841     
26842     enter : function(ev)
26843     {
26844         var dom = ev.getTarget();
26845         
26846         //Roo.log(['enter',dom]);
26847         var el = Roo.fly(dom);
26848         if (this.currentEl) {
26849             //Roo.log(dom);
26850             //Roo.log(this.currentEl);
26851             //Roo.log(this.currentEl.contains(dom));
26852             if (this.currentEl == el) {
26853                 return;
26854             }
26855             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26856                 return;
26857             }
26858
26859         }
26860         
26861         if (this.currentTip.el) {
26862             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26863         }    
26864         //Roo.log(ev);
26865         
26866         if(!el || el.dom == document){
26867             return;
26868         }
26869         
26870         var bindEl = el;
26871         
26872         // you can not look for children, as if el is the body.. then everythign is the child..
26873         if (!el.attr('tooltip')) { //
26874             if (!el.select("[tooltip]").elements.length) {
26875                 return;
26876             }
26877             // is the mouse over this child...?
26878             bindEl = el.select("[tooltip]").first();
26879             var xy = ev.getXY();
26880             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26881                 //Roo.log("not in region.");
26882                 return;
26883             }
26884             //Roo.log("child element over..");
26885             
26886         }
26887         this.currentEl = bindEl;
26888         this.currentTip.bind(bindEl);
26889         this.currentRegion = Roo.lib.Region.getRegion(dom);
26890         this.currentTip.enter();
26891         
26892     },
26893     leave : function(ev)
26894     {
26895         var dom = ev.getTarget();
26896         //Roo.log(['leave',dom]);
26897         if (!this.currentEl) {
26898             return;
26899         }
26900         
26901         
26902         if (dom != this.currentEl.dom) {
26903             return;
26904         }
26905         var xy = ev.getXY();
26906         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26907             return;
26908         }
26909         // only activate leave if mouse cursor is outside... bounding box..
26910         
26911         
26912         
26913         
26914         if (this.currentTip) {
26915             this.currentTip.leave();
26916         }
26917         //Roo.log('clear currentEl');
26918         this.currentEl = false;
26919         
26920         
26921     },
26922     alignment : {
26923         'left' : ['r-l', [-2,0], 'right'],
26924         'right' : ['l-r', [2,0], 'left'],
26925         'bottom' : ['t-b', [0,2], 'top'],
26926         'top' : [ 'b-t', [0,-2], 'bottom']
26927     }
26928     
26929 });
26930
26931
26932 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26933     
26934     
26935     bindEl : false,
26936     
26937     delay : null, // can be { show : 300 , hide: 500}
26938     
26939     timeout : null,
26940     
26941     hoverState : null, //???
26942     
26943     placement : 'bottom', 
26944     
26945     alignment : false,
26946     
26947     getAutoCreate : function(){
26948     
26949         var cfg = {
26950            cls : 'tooltip',
26951            role : 'tooltip',
26952            cn : [
26953                 {
26954                     cls : 'tooltip-arrow'
26955                 },
26956                 {
26957                     cls : 'tooltip-inner'
26958                 }
26959            ]
26960         };
26961         
26962         return cfg;
26963     },
26964     bind : function(el)
26965     {
26966         this.bindEl = el;
26967     },
26968       
26969     
26970     enter : function () {
26971        
26972         if (this.timeout != null) {
26973             clearTimeout(this.timeout);
26974         }
26975         
26976         this.hoverState = 'in';
26977          //Roo.log("enter - show");
26978         if (!this.delay || !this.delay.show) {
26979             this.show();
26980             return;
26981         }
26982         var _t = this;
26983         this.timeout = setTimeout(function () {
26984             if (_t.hoverState == 'in') {
26985                 _t.show();
26986             }
26987         }, this.delay.show);
26988     },
26989     leave : function()
26990     {
26991         clearTimeout(this.timeout);
26992     
26993         this.hoverState = 'out';
26994          if (!this.delay || !this.delay.hide) {
26995             this.hide();
26996             return;
26997         }
26998        
26999         var _t = this;
27000         this.timeout = setTimeout(function () {
27001             //Roo.log("leave - timeout");
27002             
27003             if (_t.hoverState == 'out') {
27004                 _t.hide();
27005                 Roo.bootstrap.Tooltip.currentEl = false;
27006             }
27007         }, delay);
27008     },
27009     
27010     show : function (msg)
27011     {
27012         if (!this.el) {
27013             this.render(document.body);
27014         }
27015         // set content.
27016         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27017         
27018         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27019         
27020         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27021         
27022         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27023         
27024         var placement = typeof this.placement == 'function' ?
27025             this.placement.call(this, this.el, on_el) :
27026             this.placement;
27027             
27028         var autoToken = /\s?auto?\s?/i;
27029         var autoPlace = autoToken.test(placement);
27030         if (autoPlace) {
27031             placement = placement.replace(autoToken, '') || 'top';
27032         }
27033         
27034         //this.el.detach()
27035         //this.el.setXY([0,0]);
27036         this.el.show();
27037         //this.el.dom.style.display='block';
27038         
27039         //this.el.appendTo(on_el);
27040         
27041         var p = this.getPosition();
27042         var box = this.el.getBox();
27043         
27044         if (autoPlace) {
27045             // fixme..
27046         }
27047         
27048         var align = this.alignment[placement];
27049         
27050         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27051         
27052         if(placement == 'top' || placement == 'bottom'){
27053             if(xy[0] < 0){
27054                 placement = 'right';
27055             }
27056             
27057             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27058                 placement = 'left';
27059             }
27060             
27061             var scroll = Roo.select('body', true).first().getScroll();
27062             
27063             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27064                 placement = 'top';
27065             }
27066             
27067             align = this.alignment[placement];
27068         }
27069         
27070         this.el.alignTo(this.bindEl, align[0],align[1]);
27071         //var arrow = this.el.select('.arrow',true).first();
27072         //arrow.set(align[2], 
27073         
27074         this.el.addClass(placement);
27075         
27076         this.el.addClass('in fade');
27077         
27078         this.hoverState = null;
27079         
27080         if (this.el.hasClass('fade')) {
27081             // fade it?
27082         }
27083         
27084     },
27085     hide : function()
27086     {
27087          
27088         if (!this.el) {
27089             return;
27090         }
27091         //this.el.setXY([0,0]);
27092         this.el.removeClass('in');
27093         //this.el.hide();
27094         
27095     }
27096     
27097 });
27098  
27099
27100  /*
27101  * - LGPL
27102  *
27103  * Location Picker
27104  * 
27105  */
27106
27107 /**
27108  * @class Roo.bootstrap.LocationPicker
27109  * @extends Roo.bootstrap.Component
27110  * Bootstrap LocationPicker class
27111  * @cfg {Number} latitude Position when init default 0
27112  * @cfg {Number} longitude Position when init default 0
27113  * @cfg {Number} zoom default 15
27114  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27115  * @cfg {Boolean} mapTypeControl default false
27116  * @cfg {Boolean} disableDoubleClickZoom default false
27117  * @cfg {Boolean} scrollwheel default true
27118  * @cfg {Boolean} streetViewControl default false
27119  * @cfg {Number} radius default 0
27120  * @cfg {String} locationName
27121  * @cfg {Boolean} draggable default true
27122  * @cfg {Boolean} enableAutocomplete default false
27123  * @cfg {Boolean} enableReverseGeocode default true
27124  * @cfg {String} markerTitle
27125  * 
27126  * @constructor
27127  * Create a new LocationPicker
27128  * @param {Object} config The config object
27129  */
27130
27131
27132 Roo.bootstrap.LocationPicker = function(config){
27133     
27134     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27135     
27136     this.addEvents({
27137         /**
27138          * @event initial
27139          * Fires when the picker initialized.
27140          * @param {Roo.bootstrap.LocationPicker} this
27141          * @param {Google Location} location
27142          */
27143         initial : true,
27144         /**
27145          * @event positionchanged
27146          * Fires when the picker position changed.
27147          * @param {Roo.bootstrap.LocationPicker} this
27148          * @param {Google Location} location
27149          */
27150         positionchanged : true,
27151         /**
27152          * @event resize
27153          * Fires when the map resize.
27154          * @param {Roo.bootstrap.LocationPicker} this
27155          */
27156         resize : true,
27157         /**
27158          * @event show
27159          * Fires when the map show.
27160          * @param {Roo.bootstrap.LocationPicker} this
27161          */
27162         show : true,
27163         /**
27164          * @event hide
27165          * Fires when the map hide.
27166          * @param {Roo.bootstrap.LocationPicker} this
27167          */
27168         hide : true,
27169         /**
27170          * @event mapClick
27171          * Fires when click the map.
27172          * @param {Roo.bootstrap.LocationPicker} this
27173          * @param {Map event} e
27174          */
27175         mapClick : true,
27176         /**
27177          * @event mapRightClick
27178          * Fires when right click the map.
27179          * @param {Roo.bootstrap.LocationPicker} this
27180          * @param {Map event} e
27181          */
27182         mapRightClick : true,
27183         /**
27184          * @event markerClick
27185          * Fires when click the marker.
27186          * @param {Roo.bootstrap.LocationPicker} this
27187          * @param {Map event} e
27188          */
27189         markerClick : true,
27190         /**
27191          * @event markerRightClick
27192          * Fires when right click the marker.
27193          * @param {Roo.bootstrap.LocationPicker} this
27194          * @param {Map event} e
27195          */
27196         markerRightClick : true,
27197         /**
27198          * @event OverlayViewDraw
27199          * Fires when OverlayView Draw
27200          * @param {Roo.bootstrap.LocationPicker} this
27201          */
27202         OverlayViewDraw : true,
27203         /**
27204          * @event OverlayViewOnAdd
27205          * Fires when OverlayView Draw
27206          * @param {Roo.bootstrap.LocationPicker} this
27207          */
27208         OverlayViewOnAdd : true,
27209         /**
27210          * @event OverlayViewOnRemove
27211          * Fires when OverlayView Draw
27212          * @param {Roo.bootstrap.LocationPicker} this
27213          */
27214         OverlayViewOnRemove : true,
27215         /**
27216          * @event OverlayViewShow
27217          * Fires when OverlayView Draw
27218          * @param {Roo.bootstrap.LocationPicker} this
27219          * @param {Pixel} cpx
27220          */
27221         OverlayViewShow : true,
27222         /**
27223          * @event OverlayViewHide
27224          * Fires when OverlayView Draw
27225          * @param {Roo.bootstrap.LocationPicker} this
27226          */
27227         OverlayViewHide : true,
27228         /**
27229          * @event loadexception
27230          * Fires when load google lib failed.
27231          * @param {Roo.bootstrap.LocationPicker} this
27232          */
27233         loadexception : true
27234     });
27235         
27236 };
27237
27238 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27239     
27240     gMapContext: false,
27241     
27242     latitude: 0,
27243     longitude: 0,
27244     zoom: 15,
27245     mapTypeId: false,
27246     mapTypeControl: false,
27247     disableDoubleClickZoom: false,
27248     scrollwheel: true,
27249     streetViewControl: false,
27250     radius: 0,
27251     locationName: '',
27252     draggable: true,
27253     enableAutocomplete: false,
27254     enableReverseGeocode: true,
27255     markerTitle: '',
27256     
27257     getAutoCreate: function()
27258     {
27259
27260         var cfg = {
27261             tag: 'div',
27262             cls: 'roo-location-picker'
27263         };
27264         
27265         return cfg
27266     },
27267     
27268     initEvents: function(ct, position)
27269     {       
27270         if(!this.el.getWidth() || this.isApplied()){
27271             return;
27272         }
27273         
27274         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27275         
27276         this.initial();
27277     },
27278     
27279     initial: function()
27280     {
27281         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27282             this.fireEvent('loadexception', this);
27283             return;
27284         }
27285         
27286         if(!this.mapTypeId){
27287             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27288         }
27289         
27290         this.gMapContext = this.GMapContext();
27291         
27292         this.initOverlayView();
27293         
27294         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27295         
27296         var _this = this;
27297                 
27298         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27299             _this.setPosition(_this.gMapContext.marker.position);
27300         });
27301         
27302         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27303             _this.fireEvent('mapClick', this, event);
27304             
27305         });
27306
27307         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27308             _this.fireEvent('mapRightClick', this, event);
27309             
27310         });
27311         
27312         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27313             _this.fireEvent('markerClick', this, event);
27314             
27315         });
27316
27317         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27318             _this.fireEvent('markerRightClick', this, event);
27319             
27320         });
27321         
27322         this.setPosition(this.gMapContext.location);
27323         
27324         this.fireEvent('initial', this, this.gMapContext.location);
27325     },
27326     
27327     initOverlayView: function()
27328     {
27329         var _this = this;
27330         
27331         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27332             
27333             draw: function()
27334             {
27335                 _this.fireEvent('OverlayViewDraw', _this);
27336             },
27337             
27338             onAdd: function()
27339             {
27340                 _this.fireEvent('OverlayViewOnAdd', _this);
27341             },
27342             
27343             onRemove: function()
27344             {
27345                 _this.fireEvent('OverlayViewOnRemove', _this);
27346             },
27347             
27348             show: function(cpx)
27349             {
27350                 _this.fireEvent('OverlayViewShow', _this, cpx);
27351             },
27352             
27353             hide: function()
27354             {
27355                 _this.fireEvent('OverlayViewHide', _this);
27356             }
27357             
27358         });
27359     },
27360     
27361     fromLatLngToContainerPixel: function(event)
27362     {
27363         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27364     },
27365     
27366     isApplied: function() 
27367     {
27368         return this.getGmapContext() == false ? false : true;
27369     },
27370     
27371     getGmapContext: function() 
27372     {
27373         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27374     },
27375     
27376     GMapContext: function() 
27377     {
27378         var position = new google.maps.LatLng(this.latitude, this.longitude);
27379         
27380         var _map = new google.maps.Map(this.el.dom, {
27381             center: position,
27382             zoom: this.zoom,
27383             mapTypeId: this.mapTypeId,
27384             mapTypeControl: this.mapTypeControl,
27385             disableDoubleClickZoom: this.disableDoubleClickZoom,
27386             scrollwheel: this.scrollwheel,
27387             streetViewControl: this.streetViewControl,
27388             locationName: this.locationName,
27389             draggable: this.draggable,
27390             enableAutocomplete: this.enableAutocomplete,
27391             enableReverseGeocode: this.enableReverseGeocode
27392         });
27393         
27394         var _marker = new google.maps.Marker({
27395             position: position,
27396             map: _map,
27397             title: this.markerTitle,
27398             draggable: this.draggable
27399         });
27400         
27401         return {
27402             map: _map,
27403             marker: _marker,
27404             circle: null,
27405             location: position,
27406             radius: this.radius,
27407             locationName: this.locationName,
27408             addressComponents: {
27409                 formatted_address: null,
27410                 addressLine1: null,
27411                 addressLine2: null,
27412                 streetName: null,
27413                 streetNumber: null,
27414                 city: null,
27415                 district: null,
27416                 state: null,
27417                 stateOrProvince: null
27418             },
27419             settings: this,
27420             domContainer: this.el.dom,
27421             geodecoder: new google.maps.Geocoder()
27422         };
27423     },
27424     
27425     drawCircle: function(center, radius, options) 
27426     {
27427         if (this.gMapContext.circle != null) {
27428             this.gMapContext.circle.setMap(null);
27429         }
27430         if (radius > 0) {
27431             radius *= 1;
27432             options = Roo.apply({}, options, {
27433                 strokeColor: "#0000FF",
27434                 strokeOpacity: .35,
27435                 strokeWeight: 2,
27436                 fillColor: "#0000FF",
27437                 fillOpacity: .2
27438             });
27439             
27440             options.map = this.gMapContext.map;
27441             options.radius = radius;
27442             options.center = center;
27443             this.gMapContext.circle = new google.maps.Circle(options);
27444             return this.gMapContext.circle;
27445         }
27446         
27447         return null;
27448     },
27449     
27450     setPosition: function(location) 
27451     {
27452         this.gMapContext.location = location;
27453         this.gMapContext.marker.setPosition(location);
27454         this.gMapContext.map.panTo(location);
27455         this.drawCircle(location, this.gMapContext.radius, {});
27456         
27457         var _this = this;
27458         
27459         if (this.gMapContext.settings.enableReverseGeocode) {
27460             this.gMapContext.geodecoder.geocode({
27461                 latLng: this.gMapContext.location
27462             }, function(results, status) {
27463                 
27464                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27465                     _this.gMapContext.locationName = results[0].formatted_address;
27466                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27467                     
27468                     _this.fireEvent('positionchanged', this, location);
27469                 }
27470             });
27471             
27472             return;
27473         }
27474         
27475         this.fireEvent('positionchanged', this, location);
27476     },
27477     
27478     resize: function()
27479     {
27480         google.maps.event.trigger(this.gMapContext.map, "resize");
27481         
27482         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27483         
27484         this.fireEvent('resize', this);
27485     },
27486     
27487     setPositionByLatLng: function(latitude, longitude)
27488     {
27489         this.setPosition(new google.maps.LatLng(latitude, longitude));
27490     },
27491     
27492     getCurrentPosition: function() 
27493     {
27494         return {
27495             latitude: this.gMapContext.location.lat(),
27496             longitude: this.gMapContext.location.lng()
27497         };
27498     },
27499     
27500     getAddressName: function() 
27501     {
27502         return this.gMapContext.locationName;
27503     },
27504     
27505     getAddressComponents: function() 
27506     {
27507         return this.gMapContext.addressComponents;
27508     },
27509     
27510     address_component_from_google_geocode: function(address_components) 
27511     {
27512         var result = {};
27513         
27514         for (var i = 0; i < address_components.length; i++) {
27515             var component = address_components[i];
27516             if (component.types.indexOf("postal_code") >= 0) {
27517                 result.postalCode = component.short_name;
27518             } else if (component.types.indexOf("street_number") >= 0) {
27519                 result.streetNumber = component.short_name;
27520             } else if (component.types.indexOf("route") >= 0) {
27521                 result.streetName = component.short_name;
27522             } else if (component.types.indexOf("neighborhood") >= 0) {
27523                 result.city = component.short_name;
27524             } else if (component.types.indexOf("locality") >= 0) {
27525                 result.city = component.short_name;
27526             } else if (component.types.indexOf("sublocality") >= 0) {
27527                 result.district = component.short_name;
27528             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27529                 result.stateOrProvince = component.short_name;
27530             } else if (component.types.indexOf("country") >= 0) {
27531                 result.country = component.short_name;
27532             }
27533         }
27534         
27535         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27536         result.addressLine2 = "";
27537         return result;
27538     },
27539     
27540     setZoomLevel: function(zoom)
27541     {
27542         this.gMapContext.map.setZoom(zoom);
27543     },
27544     
27545     show: function()
27546     {
27547         if(!this.el){
27548             return;
27549         }
27550         
27551         this.el.show();
27552         
27553         this.resize();
27554         
27555         this.fireEvent('show', this);
27556     },
27557     
27558     hide: function()
27559     {
27560         if(!this.el){
27561             return;
27562         }
27563         
27564         this.el.hide();
27565         
27566         this.fireEvent('hide', this);
27567     }
27568     
27569 });
27570
27571 Roo.apply(Roo.bootstrap.LocationPicker, {
27572     
27573     OverlayView : function(map, options)
27574     {
27575         options = options || {};
27576         
27577         this.setMap(map);
27578     }
27579     
27580     
27581 });/**
27582  * @class Roo.bootstrap.Alert
27583  * @extends Roo.bootstrap.Component
27584  * Bootstrap Alert class - shows an alert area box
27585  * eg
27586  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27587   Enter a valid email address
27588 </div>
27589  * @licence LGPL
27590  * @cfg {String} title The title of alert
27591  * @cfg {String} html The content of alert
27592  * @cfg {String} weight (  success | info | warning | danger )
27593  * @cfg {String} faicon font-awesomeicon
27594  * 
27595  * @constructor
27596  * Create a new alert
27597  * @param {Object} config The config object
27598  */
27599
27600
27601 Roo.bootstrap.Alert = function(config){
27602     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27603     
27604 };
27605
27606 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27607     
27608     title: '',
27609     html: '',
27610     weight: false,
27611     faicon: false,
27612     
27613     getAutoCreate : function()
27614     {
27615         
27616         var cfg = {
27617             tag : 'div',
27618             cls : 'alert',
27619             cn : [
27620                 {
27621                     tag : 'i',
27622                     cls : 'roo-alert-icon'
27623                     
27624                 },
27625                 {
27626                     tag : 'b',
27627                     cls : 'roo-alert-title',
27628                     html : this.title
27629                 },
27630                 {
27631                     tag : 'span',
27632                     cls : 'roo-alert-text',
27633                     html : this.html
27634                 }
27635             ]
27636         };
27637         
27638         if(this.faicon){
27639             cfg.cn[0].cls += ' fa ' + this.faicon;
27640         }
27641         
27642         if(this.weight){
27643             cfg.cls += ' alert-' + this.weight;
27644         }
27645         
27646         return cfg;
27647     },
27648     
27649     initEvents: function() 
27650     {
27651         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27652     },
27653     
27654     setTitle : function(str)
27655     {
27656         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27657     },
27658     
27659     setText : function(str)
27660     {
27661         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27662     },
27663     
27664     setWeight : function(weight)
27665     {
27666         if(this.weight){
27667             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27668         }
27669         
27670         this.weight = weight;
27671         
27672         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27673     },
27674     
27675     setIcon : function(icon)
27676     {
27677         if(this.faicon){
27678             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27679         }
27680         
27681         this.faicon = icon;
27682         
27683         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27684     },
27685     
27686     hide: function() 
27687     {
27688         this.el.hide();   
27689     },
27690     
27691     show: function() 
27692     {  
27693         this.el.show();   
27694     }
27695     
27696 });
27697
27698  
27699 /*
27700 * Licence: LGPL
27701 */
27702
27703 /**
27704  * @class Roo.bootstrap.UploadCropbox
27705  * @extends Roo.bootstrap.Component
27706  * Bootstrap UploadCropbox class
27707  * @cfg {String} emptyText show when image has been loaded
27708  * @cfg {String} rotateNotify show when image too small to rotate
27709  * @cfg {Number} errorTimeout default 3000
27710  * @cfg {Number} minWidth default 300
27711  * @cfg {Number} minHeight default 300
27712  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27713  * @cfg {Boolean} isDocument (true|false) default false
27714  * @cfg {String} url action url
27715  * @cfg {String} paramName default 'imageUpload'
27716  * @cfg {String} method default POST
27717  * @cfg {Boolean} loadMask (true|false) default true
27718  * @cfg {Boolean} loadingText default 'Loading...'
27719  * 
27720  * @constructor
27721  * Create a new UploadCropbox
27722  * @param {Object} config The config object
27723  */
27724
27725 Roo.bootstrap.UploadCropbox = function(config){
27726     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27727     
27728     this.addEvents({
27729         /**
27730          * @event beforeselectfile
27731          * Fire before select file
27732          * @param {Roo.bootstrap.UploadCropbox} this
27733          */
27734         "beforeselectfile" : true,
27735         /**
27736          * @event initial
27737          * Fire after initEvent
27738          * @param {Roo.bootstrap.UploadCropbox} this
27739          */
27740         "initial" : true,
27741         /**
27742          * @event crop
27743          * Fire after initEvent
27744          * @param {Roo.bootstrap.UploadCropbox} this
27745          * @param {String} data
27746          */
27747         "crop" : true,
27748         /**
27749          * @event prepare
27750          * Fire when preparing the file data
27751          * @param {Roo.bootstrap.UploadCropbox} this
27752          * @param {Object} file
27753          */
27754         "prepare" : true,
27755         /**
27756          * @event exception
27757          * Fire when get exception
27758          * @param {Roo.bootstrap.UploadCropbox} this
27759          * @param {XMLHttpRequest} xhr
27760          */
27761         "exception" : true,
27762         /**
27763          * @event beforeloadcanvas
27764          * Fire before load the canvas
27765          * @param {Roo.bootstrap.UploadCropbox} this
27766          * @param {String} src
27767          */
27768         "beforeloadcanvas" : true,
27769         /**
27770          * @event trash
27771          * Fire when trash image
27772          * @param {Roo.bootstrap.UploadCropbox} this
27773          */
27774         "trash" : true,
27775         /**
27776          * @event download
27777          * Fire when download the image
27778          * @param {Roo.bootstrap.UploadCropbox} this
27779          */
27780         "download" : true,
27781         /**
27782          * @event footerbuttonclick
27783          * Fire when footerbuttonclick
27784          * @param {Roo.bootstrap.UploadCropbox} this
27785          * @param {String} type
27786          */
27787         "footerbuttonclick" : true,
27788         /**
27789          * @event resize
27790          * Fire when resize
27791          * @param {Roo.bootstrap.UploadCropbox} this
27792          */
27793         "resize" : true,
27794         /**
27795          * @event rotate
27796          * Fire when rotate the image
27797          * @param {Roo.bootstrap.UploadCropbox} this
27798          * @param {String} pos
27799          */
27800         "rotate" : true,
27801         /**
27802          * @event inspect
27803          * Fire when inspect the file
27804          * @param {Roo.bootstrap.UploadCropbox} this
27805          * @param {Object} file
27806          */
27807         "inspect" : true,
27808         /**
27809          * @event upload
27810          * Fire when xhr upload the file
27811          * @param {Roo.bootstrap.UploadCropbox} this
27812          * @param {Object} data
27813          */
27814         "upload" : true,
27815         /**
27816          * @event arrange
27817          * Fire when arrange the file data
27818          * @param {Roo.bootstrap.UploadCropbox} this
27819          * @param {Object} formData
27820          */
27821         "arrange" : true
27822     });
27823     
27824     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27825 };
27826
27827 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27828     
27829     emptyText : 'Click to upload image',
27830     rotateNotify : 'Image is too small to rotate',
27831     errorTimeout : 3000,
27832     scale : 0,
27833     baseScale : 1,
27834     rotate : 0,
27835     dragable : false,
27836     pinching : false,
27837     mouseX : 0,
27838     mouseY : 0,
27839     cropData : false,
27840     minWidth : 300,
27841     minHeight : 300,
27842     file : false,
27843     exif : {},
27844     baseRotate : 1,
27845     cropType : 'image/jpeg',
27846     buttons : false,
27847     canvasLoaded : false,
27848     isDocument : false,
27849     method : 'POST',
27850     paramName : 'imageUpload',
27851     loadMask : true,
27852     loadingText : 'Loading...',
27853     maskEl : false,
27854     
27855     getAutoCreate : function()
27856     {
27857         var cfg = {
27858             tag : 'div',
27859             cls : 'roo-upload-cropbox',
27860             cn : [
27861                 {
27862                     tag : 'input',
27863                     cls : 'roo-upload-cropbox-selector',
27864                     type : 'file'
27865                 },
27866                 {
27867                     tag : 'div',
27868                     cls : 'roo-upload-cropbox-body',
27869                     style : 'cursor:pointer',
27870                     cn : [
27871                         {
27872                             tag : 'div',
27873                             cls : 'roo-upload-cropbox-preview'
27874                         },
27875                         {
27876                             tag : 'div',
27877                             cls : 'roo-upload-cropbox-thumb'
27878                         },
27879                         {
27880                             tag : 'div',
27881                             cls : 'roo-upload-cropbox-empty-notify',
27882                             html : this.emptyText
27883                         },
27884                         {
27885                             tag : 'div',
27886                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27887                             html : this.rotateNotify
27888                         }
27889                     ]
27890                 },
27891                 {
27892                     tag : 'div',
27893                     cls : 'roo-upload-cropbox-footer',
27894                     cn : {
27895                         tag : 'div',
27896                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27897                         cn : []
27898                     }
27899                 }
27900             ]
27901         };
27902         
27903         return cfg;
27904     },
27905     
27906     onRender : function(ct, position)
27907     {
27908         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27909         
27910         if (this.buttons.length) {
27911             
27912             Roo.each(this.buttons, function(bb) {
27913                 
27914                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27915                 
27916                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27917                 
27918             }, this);
27919         }
27920         
27921         if(this.loadMask){
27922             this.maskEl = this.el;
27923         }
27924     },
27925     
27926     initEvents : function()
27927     {
27928         this.urlAPI = (window.createObjectURL && window) || 
27929                                 (window.URL && URL.revokeObjectURL && URL) || 
27930                                 (window.webkitURL && webkitURL);
27931                         
27932         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27933         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27934         
27935         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27936         this.selectorEl.hide();
27937         
27938         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27939         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27940         
27941         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27942         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27943         this.thumbEl.hide();
27944         
27945         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27946         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27947         
27948         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27949         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27950         this.errorEl.hide();
27951         
27952         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27953         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27954         this.footerEl.hide();
27955         
27956         this.setThumbBoxSize();
27957         
27958         this.bind();
27959         
27960         this.resize();
27961         
27962         this.fireEvent('initial', this);
27963     },
27964
27965     bind : function()
27966     {
27967         var _this = this;
27968         
27969         window.addEventListener("resize", function() { _this.resize(); } );
27970         
27971         this.bodyEl.on('click', this.beforeSelectFile, this);
27972         
27973         if(Roo.isTouch){
27974             this.bodyEl.on('touchstart', this.onTouchStart, this);
27975             this.bodyEl.on('touchmove', this.onTouchMove, this);
27976             this.bodyEl.on('touchend', this.onTouchEnd, this);
27977         }
27978         
27979         if(!Roo.isTouch){
27980             this.bodyEl.on('mousedown', this.onMouseDown, this);
27981             this.bodyEl.on('mousemove', this.onMouseMove, this);
27982             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27983             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27984             Roo.get(document).on('mouseup', this.onMouseUp, this);
27985         }
27986         
27987         this.selectorEl.on('change', this.onFileSelected, this);
27988     },
27989     
27990     reset : function()
27991     {    
27992         this.scale = 0;
27993         this.baseScale = 1;
27994         this.rotate = 0;
27995         this.baseRotate = 1;
27996         this.dragable = false;
27997         this.pinching = false;
27998         this.mouseX = 0;
27999         this.mouseY = 0;
28000         this.cropData = false;
28001         this.notifyEl.dom.innerHTML = this.emptyText;
28002         
28003         this.selectorEl.dom.value = '';
28004         
28005     },
28006     
28007     resize : function()
28008     {
28009         if(this.fireEvent('resize', this) != false){
28010             this.setThumbBoxPosition();
28011             this.setCanvasPosition();
28012         }
28013     },
28014     
28015     onFooterButtonClick : function(e, el, o, type)
28016     {
28017         switch (type) {
28018             case 'rotate-left' :
28019                 this.onRotateLeft(e);
28020                 break;
28021             case 'rotate-right' :
28022                 this.onRotateRight(e);
28023                 break;
28024             case 'picture' :
28025                 this.beforeSelectFile(e);
28026                 break;
28027             case 'trash' :
28028                 this.trash(e);
28029                 break;
28030             case 'crop' :
28031                 this.crop(e);
28032                 break;
28033             case 'download' :
28034                 this.download(e);
28035                 break;
28036             default :
28037                 break;
28038         }
28039         
28040         this.fireEvent('footerbuttonclick', this, type);
28041     },
28042     
28043     beforeSelectFile : function(e)
28044     {
28045         e.preventDefault();
28046         
28047         if(this.fireEvent('beforeselectfile', this) != false){
28048             this.selectorEl.dom.click();
28049         }
28050     },
28051     
28052     onFileSelected : function(e)
28053     {
28054         e.preventDefault();
28055         
28056         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28057             return;
28058         }
28059         
28060         var file = this.selectorEl.dom.files[0];
28061         
28062         if(this.fireEvent('inspect', this, file) != false){
28063             this.prepare(file);
28064         }
28065         
28066     },
28067     
28068     trash : function(e)
28069     {
28070         this.fireEvent('trash', this);
28071     },
28072     
28073     download : function(e)
28074     {
28075         this.fireEvent('download', this);
28076     },
28077     
28078     loadCanvas : function(src)
28079     {   
28080         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28081             
28082             this.reset();
28083             
28084             this.imageEl = document.createElement('img');
28085             
28086             var _this = this;
28087             
28088             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28089             
28090             this.imageEl.src = src;
28091         }
28092     },
28093     
28094     onLoadCanvas : function()
28095     {   
28096         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28097         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28098         
28099         this.bodyEl.un('click', this.beforeSelectFile, this);
28100         
28101         this.notifyEl.hide();
28102         this.thumbEl.show();
28103         this.footerEl.show();
28104         
28105         this.baseRotateLevel();
28106         
28107         if(this.isDocument){
28108             this.setThumbBoxSize();
28109         }
28110         
28111         this.setThumbBoxPosition();
28112         
28113         this.baseScaleLevel();
28114         
28115         this.draw();
28116         
28117         this.resize();
28118         
28119         this.canvasLoaded = true;
28120         
28121         if(this.loadMask){
28122             this.maskEl.unmask();
28123         }
28124         
28125     },
28126     
28127     setCanvasPosition : function()
28128     {   
28129         if(!this.canvasEl){
28130             return;
28131         }
28132         
28133         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28134         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28135         
28136         this.previewEl.setLeft(pw);
28137         this.previewEl.setTop(ph);
28138         
28139     },
28140     
28141     onMouseDown : function(e)
28142     {   
28143         e.stopEvent();
28144         
28145         this.dragable = true;
28146         this.pinching = false;
28147         
28148         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28149             this.dragable = false;
28150             return;
28151         }
28152         
28153         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28154         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28155         
28156     },
28157     
28158     onMouseMove : function(e)
28159     {   
28160         e.stopEvent();
28161         
28162         if(!this.canvasLoaded){
28163             return;
28164         }
28165         
28166         if (!this.dragable){
28167             return;
28168         }
28169         
28170         var minX = Math.ceil(this.thumbEl.getLeft(true));
28171         var minY = Math.ceil(this.thumbEl.getTop(true));
28172         
28173         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28174         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28175         
28176         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28177         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28178         
28179         x = x - this.mouseX;
28180         y = y - this.mouseY;
28181         
28182         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28183         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28184         
28185         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28186         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28187         
28188         this.previewEl.setLeft(bgX);
28189         this.previewEl.setTop(bgY);
28190         
28191         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28192         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28193     },
28194     
28195     onMouseUp : function(e)
28196     {   
28197         e.stopEvent();
28198         
28199         this.dragable = false;
28200     },
28201     
28202     onMouseWheel : function(e)
28203     {   
28204         e.stopEvent();
28205         
28206         this.startScale = this.scale;
28207         
28208         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28209         
28210         if(!this.zoomable()){
28211             this.scale = this.startScale;
28212             return;
28213         }
28214         
28215         this.draw();
28216         
28217         return;
28218     },
28219     
28220     zoomable : function()
28221     {
28222         var minScale = this.thumbEl.getWidth() / this.minWidth;
28223         
28224         if(this.minWidth < this.minHeight){
28225             minScale = this.thumbEl.getHeight() / this.minHeight;
28226         }
28227         
28228         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28229         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28230         
28231         if(
28232                 this.isDocument &&
28233                 (this.rotate == 0 || this.rotate == 180) && 
28234                 (
28235                     width > this.imageEl.OriginWidth || 
28236                     height > this.imageEl.OriginHeight ||
28237                     (width < this.minWidth && height < this.minHeight)
28238                 )
28239         ){
28240             return false;
28241         }
28242         
28243         if(
28244                 this.isDocument &&
28245                 (this.rotate == 90 || this.rotate == 270) && 
28246                 (
28247                     width > this.imageEl.OriginWidth || 
28248                     height > this.imageEl.OriginHeight ||
28249                     (width < this.minHeight && height < this.minWidth)
28250                 )
28251         ){
28252             return false;
28253         }
28254         
28255         if(
28256                 !this.isDocument &&
28257                 (this.rotate == 0 || this.rotate == 180) && 
28258                 (
28259                     width < this.minWidth || 
28260                     width > this.imageEl.OriginWidth || 
28261                     height < this.minHeight || 
28262                     height > this.imageEl.OriginHeight
28263                 )
28264         ){
28265             return false;
28266         }
28267         
28268         if(
28269                 !this.isDocument &&
28270                 (this.rotate == 90 || this.rotate == 270) && 
28271                 (
28272                     width < this.minHeight || 
28273                     width > this.imageEl.OriginWidth || 
28274                     height < this.minWidth || 
28275                     height > this.imageEl.OriginHeight
28276                 )
28277         ){
28278             return false;
28279         }
28280         
28281         return true;
28282         
28283     },
28284     
28285     onRotateLeft : function(e)
28286     {   
28287         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28288             
28289             var minScale = this.thumbEl.getWidth() / this.minWidth;
28290             
28291             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28292             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28293             
28294             this.startScale = this.scale;
28295             
28296             while (this.getScaleLevel() < minScale){
28297             
28298                 this.scale = this.scale + 1;
28299                 
28300                 if(!this.zoomable()){
28301                     break;
28302                 }
28303                 
28304                 if(
28305                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28306                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28307                 ){
28308                     continue;
28309                 }
28310                 
28311                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28312
28313                 this.draw();
28314                 
28315                 return;
28316             }
28317             
28318             this.scale = this.startScale;
28319             
28320             this.onRotateFail();
28321             
28322             return false;
28323         }
28324         
28325         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28326
28327         if(this.isDocument){
28328             this.setThumbBoxSize();
28329             this.setThumbBoxPosition();
28330             this.setCanvasPosition();
28331         }
28332         
28333         this.draw();
28334         
28335         this.fireEvent('rotate', this, 'left');
28336         
28337     },
28338     
28339     onRotateRight : function(e)
28340     {
28341         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28342             
28343             var minScale = this.thumbEl.getWidth() / this.minWidth;
28344         
28345             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28346             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28347             
28348             this.startScale = this.scale;
28349             
28350             while (this.getScaleLevel() < minScale){
28351             
28352                 this.scale = this.scale + 1;
28353                 
28354                 if(!this.zoomable()){
28355                     break;
28356                 }
28357                 
28358                 if(
28359                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28360                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28361                 ){
28362                     continue;
28363                 }
28364                 
28365                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28366
28367                 this.draw();
28368                 
28369                 return;
28370             }
28371             
28372             this.scale = this.startScale;
28373             
28374             this.onRotateFail();
28375             
28376             return false;
28377         }
28378         
28379         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28380
28381         if(this.isDocument){
28382             this.setThumbBoxSize();
28383             this.setThumbBoxPosition();
28384             this.setCanvasPosition();
28385         }
28386         
28387         this.draw();
28388         
28389         this.fireEvent('rotate', this, 'right');
28390     },
28391     
28392     onRotateFail : function()
28393     {
28394         this.errorEl.show(true);
28395         
28396         var _this = this;
28397         
28398         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28399     },
28400     
28401     draw : function()
28402     {
28403         this.previewEl.dom.innerHTML = '';
28404         
28405         var canvasEl = document.createElement("canvas");
28406         
28407         var contextEl = canvasEl.getContext("2d");
28408         
28409         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28410         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28411         var center = this.imageEl.OriginWidth / 2;
28412         
28413         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28414             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28415             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28416             center = this.imageEl.OriginHeight / 2;
28417         }
28418         
28419         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28420         
28421         contextEl.translate(center, center);
28422         contextEl.rotate(this.rotate * Math.PI / 180);
28423
28424         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28425         
28426         this.canvasEl = document.createElement("canvas");
28427         
28428         this.contextEl = this.canvasEl.getContext("2d");
28429         
28430         switch (this.rotate) {
28431             case 0 :
28432                 
28433                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28434                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28435                 
28436                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28437                 
28438                 break;
28439             case 90 : 
28440                 
28441                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28442                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28443                 
28444                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28445                     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);
28446                     break;
28447                 }
28448                 
28449                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28450                 
28451                 break;
28452             case 180 :
28453                 
28454                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28455                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28456                 
28457                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28458                     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);
28459                     break;
28460                 }
28461                 
28462                 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);
28463                 
28464                 break;
28465             case 270 :
28466                 
28467                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28468                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28469         
28470                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28471                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28472                     break;
28473                 }
28474                 
28475                 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);
28476                 
28477                 break;
28478             default : 
28479                 break;
28480         }
28481         
28482         this.previewEl.appendChild(this.canvasEl);
28483         
28484         this.setCanvasPosition();
28485     },
28486     
28487     crop : function()
28488     {
28489         if(!this.canvasLoaded){
28490             return;
28491         }
28492         
28493         var imageCanvas = document.createElement("canvas");
28494         
28495         var imageContext = imageCanvas.getContext("2d");
28496         
28497         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28498         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28499         
28500         var center = imageCanvas.width / 2;
28501         
28502         imageContext.translate(center, center);
28503         
28504         imageContext.rotate(this.rotate * Math.PI / 180);
28505         
28506         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28507         
28508         var canvas = document.createElement("canvas");
28509         
28510         var context = canvas.getContext("2d");
28511                 
28512         canvas.width = this.minWidth;
28513         canvas.height = this.minHeight;
28514
28515         switch (this.rotate) {
28516             case 0 :
28517                 
28518                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28519                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28520                 
28521                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28522                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28523                 
28524                 var targetWidth = this.minWidth - 2 * x;
28525                 var targetHeight = this.minHeight - 2 * y;
28526                 
28527                 var scale = 1;
28528                 
28529                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28530                     scale = targetWidth / width;
28531                 }
28532                 
28533                 if(x > 0 && y == 0){
28534                     scale = targetHeight / height;
28535                 }
28536                 
28537                 if(x > 0 && y > 0){
28538                     scale = targetWidth / width;
28539                     
28540                     if(width < height){
28541                         scale = targetHeight / height;
28542                     }
28543                 }
28544                 
28545                 context.scale(scale, scale);
28546                 
28547                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28548                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28549
28550                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28551                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28552
28553                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28554                 
28555                 break;
28556             case 90 : 
28557                 
28558                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28559                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28560                 
28561                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28562                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28563                 
28564                 var targetWidth = this.minWidth - 2 * x;
28565                 var targetHeight = this.minHeight - 2 * y;
28566                 
28567                 var scale = 1;
28568                 
28569                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28570                     scale = targetWidth / width;
28571                 }
28572                 
28573                 if(x > 0 && y == 0){
28574                     scale = targetHeight / height;
28575                 }
28576                 
28577                 if(x > 0 && y > 0){
28578                     scale = targetWidth / width;
28579                     
28580                     if(width < height){
28581                         scale = targetHeight / height;
28582                     }
28583                 }
28584                 
28585                 context.scale(scale, scale);
28586                 
28587                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28588                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28589
28590                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28591                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28592                 
28593                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28594                 
28595                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28596                 
28597                 break;
28598             case 180 :
28599                 
28600                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28601                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28602                 
28603                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28604                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28605                 
28606                 var targetWidth = this.minWidth - 2 * x;
28607                 var targetHeight = this.minHeight - 2 * y;
28608                 
28609                 var scale = 1;
28610                 
28611                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28612                     scale = targetWidth / width;
28613                 }
28614                 
28615                 if(x > 0 && y == 0){
28616                     scale = targetHeight / height;
28617                 }
28618                 
28619                 if(x > 0 && y > 0){
28620                     scale = targetWidth / width;
28621                     
28622                     if(width < height){
28623                         scale = targetHeight / height;
28624                     }
28625                 }
28626                 
28627                 context.scale(scale, scale);
28628                 
28629                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28630                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28631
28632                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28633                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28634
28635                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28636                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28637                 
28638                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28639                 
28640                 break;
28641             case 270 :
28642                 
28643                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28644                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28645                 
28646                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28647                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28648                 
28649                 var targetWidth = this.minWidth - 2 * x;
28650                 var targetHeight = this.minHeight - 2 * y;
28651                 
28652                 var scale = 1;
28653                 
28654                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28655                     scale = targetWidth / width;
28656                 }
28657                 
28658                 if(x > 0 && y == 0){
28659                     scale = targetHeight / height;
28660                 }
28661                 
28662                 if(x > 0 && y > 0){
28663                     scale = targetWidth / width;
28664                     
28665                     if(width < height){
28666                         scale = targetHeight / height;
28667                     }
28668                 }
28669                 
28670                 context.scale(scale, scale);
28671                 
28672                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28673                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28674
28675                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28676                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28677                 
28678                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28679                 
28680                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28681                 
28682                 break;
28683             default : 
28684                 break;
28685         }
28686         
28687         this.cropData = canvas.toDataURL(this.cropType);
28688         
28689         if(this.fireEvent('crop', this, this.cropData) !== false){
28690             this.process(this.file, this.cropData);
28691         }
28692         
28693         return;
28694         
28695     },
28696     
28697     setThumbBoxSize : function()
28698     {
28699         var width, height;
28700         
28701         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28702             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28703             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28704             
28705             this.minWidth = width;
28706             this.minHeight = height;
28707             
28708             if(this.rotate == 90 || this.rotate == 270){
28709                 this.minWidth = height;
28710                 this.minHeight = width;
28711             }
28712         }
28713         
28714         height = 300;
28715         width = Math.ceil(this.minWidth * height / this.minHeight);
28716         
28717         if(this.minWidth > this.minHeight){
28718             width = 300;
28719             height = Math.ceil(this.minHeight * width / this.minWidth);
28720         }
28721         
28722         this.thumbEl.setStyle({
28723             width : width + 'px',
28724             height : height + 'px'
28725         });
28726
28727         return;
28728             
28729     },
28730     
28731     setThumbBoxPosition : function()
28732     {
28733         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28734         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28735         
28736         this.thumbEl.setLeft(x);
28737         this.thumbEl.setTop(y);
28738         
28739     },
28740     
28741     baseRotateLevel : function()
28742     {
28743         this.baseRotate = 1;
28744         
28745         if(
28746                 typeof(this.exif) != 'undefined' &&
28747                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28748                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28749         ){
28750             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28751         }
28752         
28753         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28754         
28755     },
28756     
28757     baseScaleLevel : function()
28758     {
28759         var width, height;
28760         
28761         if(this.isDocument){
28762             
28763             if(this.baseRotate == 6 || this.baseRotate == 8){
28764             
28765                 height = this.thumbEl.getHeight();
28766                 this.baseScale = height / this.imageEl.OriginWidth;
28767
28768                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28769                     width = this.thumbEl.getWidth();
28770                     this.baseScale = width / this.imageEl.OriginHeight;
28771                 }
28772
28773                 return;
28774             }
28775
28776             height = this.thumbEl.getHeight();
28777             this.baseScale = height / this.imageEl.OriginHeight;
28778
28779             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28780                 width = this.thumbEl.getWidth();
28781                 this.baseScale = width / this.imageEl.OriginWidth;
28782             }
28783
28784             return;
28785         }
28786         
28787         if(this.baseRotate == 6 || this.baseRotate == 8){
28788             
28789             width = this.thumbEl.getHeight();
28790             this.baseScale = width / this.imageEl.OriginHeight;
28791             
28792             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28793                 height = this.thumbEl.getWidth();
28794                 this.baseScale = height / this.imageEl.OriginHeight;
28795             }
28796             
28797             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28798                 height = this.thumbEl.getWidth();
28799                 this.baseScale = height / this.imageEl.OriginHeight;
28800                 
28801                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28802                     width = this.thumbEl.getHeight();
28803                     this.baseScale = width / this.imageEl.OriginWidth;
28804                 }
28805             }
28806             
28807             return;
28808         }
28809         
28810         width = this.thumbEl.getWidth();
28811         this.baseScale = width / this.imageEl.OriginWidth;
28812         
28813         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28814             height = this.thumbEl.getHeight();
28815             this.baseScale = height / this.imageEl.OriginHeight;
28816         }
28817         
28818         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28819             
28820             height = this.thumbEl.getHeight();
28821             this.baseScale = height / this.imageEl.OriginHeight;
28822             
28823             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28824                 width = this.thumbEl.getWidth();
28825                 this.baseScale = width / this.imageEl.OriginWidth;
28826             }
28827             
28828         }
28829         
28830         return;
28831     },
28832     
28833     getScaleLevel : function()
28834     {
28835         return this.baseScale * Math.pow(1.1, this.scale);
28836     },
28837     
28838     onTouchStart : function(e)
28839     {
28840         if(!this.canvasLoaded){
28841             this.beforeSelectFile(e);
28842             return;
28843         }
28844         
28845         var touches = e.browserEvent.touches;
28846         
28847         if(!touches){
28848             return;
28849         }
28850         
28851         if(touches.length == 1){
28852             this.onMouseDown(e);
28853             return;
28854         }
28855         
28856         if(touches.length != 2){
28857             return;
28858         }
28859         
28860         var coords = [];
28861         
28862         for(var i = 0, finger; finger = touches[i]; i++){
28863             coords.push(finger.pageX, finger.pageY);
28864         }
28865         
28866         var x = Math.pow(coords[0] - coords[2], 2);
28867         var y = Math.pow(coords[1] - coords[3], 2);
28868         
28869         this.startDistance = Math.sqrt(x + y);
28870         
28871         this.startScale = this.scale;
28872         
28873         this.pinching = true;
28874         this.dragable = false;
28875         
28876     },
28877     
28878     onTouchMove : function(e)
28879     {
28880         if(!this.pinching && !this.dragable){
28881             return;
28882         }
28883         
28884         var touches = e.browserEvent.touches;
28885         
28886         if(!touches){
28887             return;
28888         }
28889         
28890         if(this.dragable){
28891             this.onMouseMove(e);
28892             return;
28893         }
28894         
28895         var coords = [];
28896         
28897         for(var i = 0, finger; finger = touches[i]; i++){
28898             coords.push(finger.pageX, finger.pageY);
28899         }
28900         
28901         var x = Math.pow(coords[0] - coords[2], 2);
28902         var y = Math.pow(coords[1] - coords[3], 2);
28903         
28904         this.endDistance = Math.sqrt(x + y);
28905         
28906         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28907         
28908         if(!this.zoomable()){
28909             this.scale = this.startScale;
28910             return;
28911         }
28912         
28913         this.draw();
28914         
28915     },
28916     
28917     onTouchEnd : function(e)
28918     {
28919         this.pinching = false;
28920         this.dragable = false;
28921         
28922     },
28923     
28924     process : function(file, crop)
28925     {
28926         if(this.loadMask){
28927             this.maskEl.mask(this.loadingText);
28928         }
28929         
28930         this.xhr = new XMLHttpRequest();
28931         
28932         file.xhr = this.xhr;
28933
28934         this.xhr.open(this.method, this.url, true);
28935         
28936         var headers = {
28937             "Accept": "application/json",
28938             "Cache-Control": "no-cache",
28939             "X-Requested-With": "XMLHttpRequest"
28940         };
28941         
28942         for (var headerName in headers) {
28943             var headerValue = headers[headerName];
28944             if (headerValue) {
28945                 this.xhr.setRequestHeader(headerName, headerValue);
28946             }
28947         }
28948         
28949         var _this = this;
28950         
28951         this.xhr.onload = function()
28952         {
28953             _this.xhrOnLoad(_this.xhr);
28954         }
28955         
28956         this.xhr.onerror = function()
28957         {
28958             _this.xhrOnError(_this.xhr);
28959         }
28960         
28961         var formData = new FormData();
28962
28963         formData.append('returnHTML', 'NO');
28964         
28965         if(crop){
28966             formData.append('crop', crop);
28967         }
28968         
28969         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28970             formData.append(this.paramName, file, file.name);
28971         }
28972         
28973         if(typeof(file.filename) != 'undefined'){
28974             formData.append('filename', file.filename);
28975         }
28976         
28977         if(typeof(file.mimetype) != 'undefined'){
28978             formData.append('mimetype', file.mimetype);
28979         }
28980         
28981         if(this.fireEvent('arrange', this, formData) != false){
28982             this.xhr.send(formData);
28983         };
28984     },
28985     
28986     xhrOnLoad : function(xhr)
28987     {
28988         if(this.loadMask){
28989             this.maskEl.unmask();
28990         }
28991         
28992         if (xhr.readyState !== 4) {
28993             this.fireEvent('exception', this, xhr);
28994             return;
28995         }
28996
28997         var response = Roo.decode(xhr.responseText);
28998         
28999         if(!response.success){
29000             this.fireEvent('exception', this, xhr);
29001             return;
29002         }
29003         
29004         var response = Roo.decode(xhr.responseText);
29005         
29006         this.fireEvent('upload', this, response);
29007         
29008     },
29009     
29010     xhrOnError : function()
29011     {
29012         if(this.loadMask){
29013             this.maskEl.unmask();
29014         }
29015         
29016         Roo.log('xhr on error');
29017         
29018         var response = Roo.decode(xhr.responseText);
29019           
29020         Roo.log(response);
29021         
29022     },
29023     
29024     prepare : function(file)
29025     {   
29026         if(this.loadMask){
29027             this.maskEl.mask(this.loadingText);
29028         }
29029         
29030         this.file = false;
29031         this.exif = {};
29032         
29033         if(typeof(file) === 'string'){
29034             this.loadCanvas(file);
29035             return;
29036         }
29037         
29038         if(!file || !this.urlAPI){
29039             return;
29040         }
29041         
29042         this.file = file;
29043         this.cropType = file.type;
29044         
29045         var _this = this;
29046         
29047         if(this.fireEvent('prepare', this, this.file) != false){
29048             
29049             var reader = new FileReader();
29050             
29051             reader.onload = function (e) {
29052                 if (e.target.error) {
29053                     Roo.log(e.target.error);
29054                     return;
29055                 }
29056                 
29057                 var buffer = e.target.result,
29058                     dataView = new DataView(buffer),
29059                     offset = 2,
29060                     maxOffset = dataView.byteLength - 4,
29061                     markerBytes,
29062                     markerLength;
29063                 
29064                 if (dataView.getUint16(0) === 0xffd8) {
29065                     while (offset < maxOffset) {
29066                         markerBytes = dataView.getUint16(offset);
29067                         
29068                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29069                             markerLength = dataView.getUint16(offset + 2) + 2;
29070                             if (offset + markerLength > dataView.byteLength) {
29071                                 Roo.log('Invalid meta data: Invalid segment size.');
29072                                 break;
29073                             }
29074                             
29075                             if(markerBytes == 0xffe1){
29076                                 _this.parseExifData(
29077                                     dataView,
29078                                     offset,
29079                                     markerLength
29080                                 );
29081                             }
29082                             
29083                             offset += markerLength;
29084                             
29085                             continue;
29086                         }
29087                         
29088                         break;
29089                     }
29090                     
29091                 }
29092                 
29093                 var url = _this.urlAPI.createObjectURL(_this.file);
29094                 
29095                 _this.loadCanvas(url);
29096                 
29097                 return;
29098             }
29099             
29100             reader.readAsArrayBuffer(this.file);
29101             
29102         }
29103         
29104     },
29105     
29106     parseExifData : function(dataView, offset, length)
29107     {
29108         var tiffOffset = offset + 10,
29109             littleEndian,
29110             dirOffset;
29111     
29112         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29113             // No Exif data, might be XMP data instead
29114             return;
29115         }
29116         
29117         // Check for the ASCII code for "Exif" (0x45786966):
29118         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29119             // No Exif data, might be XMP data instead
29120             return;
29121         }
29122         if (tiffOffset + 8 > dataView.byteLength) {
29123             Roo.log('Invalid Exif data: Invalid segment size.');
29124             return;
29125         }
29126         // Check for the two null bytes:
29127         if (dataView.getUint16(offset + 8) !== 0x0000) {
29128             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29129             return;
29130         }
29131         // Check the byte alignment:
29132         switch (dataView.getUint16(tiffOffset)) {
29133         case 0x4949:
29134             littleEndian = true;
29135             break;
29136         case 0x4D4D:
29137             littleEndian = false;
29138             break;
29139         default:
29140             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29141             return;
29142         }
29143         // Check for the TIFF tag marker (0x002A):
29144         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29145             Roo.log('Invalid Exif data: Missing TIFF marker.');
29146             return;
29147         }
29148         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29149         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29150         
29151         this.parseExifTags(
29152             dataView,
29153             tiffOffset,
29154             tiffOffset + dirOffset,
29155             littleEndian
29156         );
29157     },
29158     
29159     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29160     {
29161         var tagsNumber,
29162             dirEndOffset,
29163             i;
29164         if (dirOffset + 6 > dataView.byteLength) {
29165             Roo.log('Invalid Exif data: Invalid directory offset.');
29166             return;
29167         }
29168         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29169         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29170         if (dirEndOffset + 4 > dataView.byteLength) {
29171             Roo.log('Invalid Exif data: Invalid directory size.');
29172             return;
29173         }
29174         for (i = 0; i < tagsNumber; i += 1) {
29175             this.parseExifTag(
29176                 dataView,
29177                 tiffOffset,
29178                 dirOffset + 2 + 12 * i, // tag offset
29179                 littleEndian
29180             );
29181         }
29182         // Return the offset to the next directory:
29183         return dataView.getUint32(dirEndOffset, littleEndian);
29184     },
29185     
29186     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29187     {
29188         var tag = dataView.getUint16(offset, littleEndian);
29189         
29190         this.exif[tag] = this.getExifValue(
29191             dataView,
29192             tiffOffset,
29193             offset,
29194             dataView.getUint16(offset + 2, littleEndian), // tag type
29195             dataView.getUint32(offset + 4, littleEndian), // tag length
29196             littleEndian
29197         );
29198     },
29199     
29200     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29201     {
29202         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29203             tagSize,
29204             dataOffset,
29205             values,
29206             i,
29207             str,
29208             c;
29209     
29210         if (!tagType) {
29211             Roo.log('Invalid Exif data: Invalid tag type.');
29212             return;
29213         }
29214         
29215         tagSize = tagType.size * length;
29216         // Determine if the value is contained in the dataOffset bytes,
29217         // or if the value at the dataOffset is a pointer to the actual data:
29218         dataOffset = tagSize > 4 ?
29219                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29220         if (dataOffset + tagSize > dataView.byteLength) {
29221             Roo.log('Invalid Exif data: Invalid data offset.');
29222             return;
29223         }
29224         if (length === 1) {
29225             return tagType.getValue(dataView, dataOffset, littleEndian);
29226         }
29227         values = [];
29228         for (i = 0; i < length; i += 1) {
29229             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29230         }
29231         
29232         if (tagType.ascii) {
29233             str = '';
29234             // Concatenate the chars:
29235             for (i = 0; i < values.length; i += 1) {
29236                 c = values[i];
29237                 // Ignore the terminating NULL byte(s):
29238                 if (c === '\u0000') {
29239                     break;
29240                 }
29241                 str += c;
29242             }
29243             return str;
29244         }
29245         return values;
29246     }
29247     
29248 });
29249
29250 Roo.apply(Roo.bootstrap.UploadCropbox, {
29251     tags : {
29252         'Orientation': 0x0112
29253     },
29254     
29255     Orientation: {
29256             1: 0, //'top-left',
29257 //            2: 'top-right',
29258             3: 180, //'bottom-right',
29259 //            4: 'bottom-left',
29260 //            5: 'left-top',
29261             6: 90, //'right-top',
29262 //            7: 'right-bottom',
29263             8: 270 //'left-bottom'
29264     },
29265     
29266     exifTagTypes : {
29267         // byte, 8-bit unsigned int:
29268         1: {
29269             getValue: function (dataView, dataOffset) {
29270                 return dataView.getUint8(dataOffset);
29271             },
29272             size: 1
29273         },
29274         // ascii, 8-bit byte:
29275         2: {
29276             getValue: function (dataView, dataOffset) {
29277                 return String.fromCharCode(dataView.getUint8(dataOffset));
29278             },
29279             size: 1,
29280             ascii: true
29281         },
29282         // short, 16 bit int:
29283         3: {
29284             getValue: function (dataView, dataOffset, littleEndian) {
29285                 return dataView.getUint16(dataOffset, littleEndian);
29286             },
29287             size: 2
29288         },
29289         // long, 32 bit int:
29290         4: {
29291             getValue: function (dataView, dataOffset, littleEndian) {
29292                 return dataView.getUint32(dataOffset, littleEndian);
29293             },
29294             size: 4
29295         },
29296         // rational = two long values, first is numerator, second is denominator:
29297         5: {
29298             getValue: function (dataView, dataOffset, littleEndian) {
29299                 return dataView.getUint32(dataOffset, littleEndian) /
29300                     dataView.getUint32(dataOffset + 4, littleEndian);
29301             },
29302             size: 8
29303         },
29304         // slong, 32 bit signed int:
29305         9: {
29306             getValue: function (dataView, dataOffset, littleEndian) {
29307                 return dataView.getInt32(dataOffset, littleEndian);
29308             },
29309             size: 4
29310         },
29311         // srational, two slongs, first is numerator, second is denominator:
29312         10: {
29313             getValue: function (dataView, dataOffset, littleEndian) {
29314                 return dataView.getInt32(dataOffset, littleEndian) /
29315                     dataView.getInt32(dataOffset + 4, littleEndian);
29316             },
29317             size: 8
29318         }
29319     },
29320     
29321     footer : {
29322         STANDARD : [
29323             {
29324                 tag : 'div',
29325                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29326                 action : 'rotate-left',
29327                 cn : [
29328                     {
29329                         tag : 'button',
29330                         cls : 'btn btn-default',
29331                         html : '<i class="fa fa-undo"></i>'
29332                     }
29333                 ]
29334             },
29335             {
29336                 tag : 'div',
29337                 cls : 'btn-group roo-upload-cropbox-picture',
29338                 action : 'picture',
29339                 cn : [
29340                     {
29341                         tag : 'button',
29342                         cls : 'btn btn-default',
29343                         html : '<i class="fa fa-picture-o"></i>'
29344                     }
29345                 ]
29346             },
29347             {
29348                 tag : 'div',
29349                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29350                 action : 'rotate-right',
29351                 cn : [
29352                     {
29353                         tag : 'button',
29354                         cls : 'btn btn-default',
29355                         html : '<i class="fa fa-repeat"></i>'
29356                     }
29357                 ]
29358             }
29359         ],
29360         DOCUMENT : [
29361             {
29362                 tag : 'div',
29363                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29364                 action : 'rotate-left',
29365                 cn : [
29366                     {
29367                         tag : 'button',
29368                         cls : 'btn btn-default',
29369                         html : '<i class="fa fa-undo"></i>'
29370                     }
29371                 ]
29372             },
29373             {
29374                 tag : 'div',
29375                 cls : 'btn-group roo-upload-cropbox-download',
29376                 action : 'download',
29377                 cn : [
29378                     {
29379                         tag : 'button',
29380                         cls : 'btn btn-default',
29381                         html : '<i class="fa fa-download"></i>'
29382                     }
29383                 ]
29384             },
29385             {
29386                 tag : 'div',
29387                 cls : 'btn-group roo-upload-cropbox-crop',
29388                 action : 'crop',
29389                 cn : [
29390                     {
29391                         tag : 'button',
29392                         cls : 'btn btn-default',
29393                         html : '<i class="fa fa-crop"></i>'
29394                     }
29395                 ]
29396             },
29397             {
29398                 tag : 'div',
29399                 cls : 'btn-group roo-upload-cropbox-trash',
29400                 action : 'trash',
29401                 cn : [
29402                     {
29403                         tag : 'button',
29404                         cls : 'btn btn-default',
29405                         html : '<i class="fa fa-trash"></i>'
29406                     }
29407                 ]
29408             },
29409             {
29410                 tag : 'div',
29411                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29412                 action : 'rotate-right',
29413                 cn : [
29414                     {
29415                         tag : 'button',
29416                         cls : 'btn btn-default',
29417                         html : '<i class="fa fa-repeat"></i>'
29418                     }
29419                 ]
29420             }
29421         ],
29422         ROTATOR : [
29423             {
29424                 tag : 'div',
29425                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29426                 action : 'rotate-left',
29427                 cn : [
29428                     {
29429                         tag : 'button',
29430                         cls : 'btn btn-default',
29431                         html : '<i class="fa fa-undo"></i>'
29432                     }
29433                 ]
29434             },
29435             {
29436                 tag : 'div',
29437                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29438                 action : 'rotate-right',
29439                 cn : [
29440                     {
29441                         tag : 'button',
29442                         cls : 'btn btn-default',
29443                         html : '<i class="fa fa-repeat"></i>'
29444                     }
29445                 ]
29446             }
29447         ]
29448     }
29449 });
29450
29451 /*
29452 * Licence: LGPL
29453 */
29454
29455 /**
29456  * @class Roo.bootstrap.DocumentManager
29457  * @extends Roo.bootstrap.Component
29458  * Bootstrap DocumentManager class
29459  * @cfg {String} paramName default 'imageUpload'
29460  * @cfg {String} toolTipName default 'filename'
29461  * @cfg {String} method default POST
29462  * @cfg {String} url action url
29463  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29464  * @cfg {Boolean} multiple multiple upload default true
29465  * @cfg {Number} thumbSize default 300
29466  * @cfg {String} fieldLabel
29467  * @cfg {Number} labelWidth default 4
29468  * @cfg {String} labelAlign (left|top) default left
29469  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29470 * @cfg {Number} labellg set the width of label (1-12)
29471  * @cfg {Number} labelmd set the width of label (1-12)
29472  * @cfg {Number} labelsm set the width of label (1-12)
29473  * @cfg {Number} labelxs set the width of label (1-12)
29474  * 
29475  * @constructor
29476  * Create a new DocumentManager
29477  * @param {Object} config The config object
29478  */
29479
29480 Roo.bootstrap.DocumentManager = function(config){
29481     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29482     
29483     this.files = [];
29484     this.delegates = [];
29485     
29486     this.addEvents({
29487         /**
29488          * @event initial
29489          * Fire when initial the DocumentManager
29490          * @param {Roo.bootstrap.DocumentManager} this
29491          */
29492         "initial" : true,
29493         /**
29494          * @event inspect
29495          * inspect selected file
29496          * @param {Roo.bootstrap.DocumentManager} this
29497          * @param {File} file
29498          */
29499         "inspect" : true,
29500         /**
29501          * @event exception
29502          * Fire when xhr load exception
29503          * @param {Roo.bootstrap.DocumentManager} this
29504          * @param {XMLHttpRequest} xhr
29505          */
29506         "exception" : true,
29507         /**
29508          * @event afterupload
29509          * Fire when xhr load exception
29510          * @param {Roo.bootstrap.DocumentManager} this
29511          * @param {XMLHttpRequest} xhr
29512          */
29513         "afterupload" : true,
29514         /**
29515          * @event prepare
29516          * prepare the form data
29517          * @param {Roo.bootstrap.DocumentManager} this
29518          * @param {Object} formData
29519          */
29520         "prepare" : true,
29521         /**
29522          * @event remove
29523          * Fire when remove the file
29524          * @param {Roo.bootstrap.DocumentManager} this
29525          * @param {Object} file
29526          */
29527         "remove" : true,
29528         /**
29529          * @event refresh
29530          * Fire after refresh the file
29531          * @param {Roo.bootstrap.DocumentManager} this
29532          */
29533         "refresh" : true,
29534         /**
29535          * @event click
29536          * Fire after click the image
29537          * @param {Roo.bootstrap.DocumentManager} this
29538          * @param {Object} file
29539          */
29540         "click" : true,
29541         /**
29542          * @event edit
29543          * Fire when upload a image and editable set to true
29544          * @param {Roo.bootstrap.DocumentManager} this
29545          * @param {Object} file
29546          */
29547         "edit" : true,
29548         /**
29549          * @event beforeselectfile
29550          * Fire before select file
29551          * @param {Roo.bootstrap.DocumentManager} this
29552          */
29553         "beforeselectfile" : true,
29554         /**
29555          * @event process
29556          * Fire before process file
29557          * @param {Roo.bootstrap.DocumentManager} this
29558          * @param {Object} file
29559          */
29560         "process" : true,
29561         /**
29562          * @event previewrendered
29563          * Fire when preview rendered
29564          * @param {Roo.bootstrap.DocumentManager} this
29565          * @param {Object} file
29566          */
29567         "previewrendered" : true,
29568         /**
29569          */
29570         "previewResize" : true
29571         
29572     });
29573 };
29574
29575 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29576     
29577     boxes : 0,
29578     inputName : '',
29579     thumbSize : 300,
29580     multiple : true,
29581     files : false,
29582     method : 'POST',
29583     url : '',
29584     paramName : 'imageUpload',
29585     toolTipName : 'filename',
29586     fieldLabel : '',
29587     labelWidth : 4,
29588     labelAlign : 'left',
29589     editable : true,
29590     delegates : false,
29591     xhr : false, 
29592     
29593     labellg : 0,
29594     labelmd : 0,
29595     labelsm : 0,
29596     labelxs : 0,
29597     
29598     getAutoCreate : function()
29599     {   
29600         var managerWidget = {
29601             tag : 'div',
29602             cls : 'roo-document-manager',
29603             cn : [
29604                 {
29605                     tag : 'input',
29606                     cls : 'roo-document-manager-selector',
29607                     type : 'file'
29608                 },
29609                 {
29610                     tag : 'div',
29611                     cls : 'roo-document-manager-uploader',
29612                     cn : [
29613                         {
29614                             tag : 'div',
29615                             cls : 'roo-document-manager-upload-btn',
29616                             html : '<i class="fa fa-plus"></i>'
29617                         }
29618                     ]
29619                     
29620                 }
29621             ]
29622         };
29623         
29624         var content = [
29625             {
29626                 tag : 'div',
29627                 cls : 'column col-md-12',
29628                 cn : managerWidget
29629             }
29630         ];
29631         
29632         if(this.fieldLabel.length){
29633             
29634             content = [
29635                 {
29636                     tag : 'div',
29637                     cls : 'column col-md-12',
29638                     html : this.fieldLabel
29639                 },
29640                 {
29641                     tag : 'div',
29642                     cls : 'column col-md-12',
29643                     cn : managerWidget
29644                 }
29645             ];
29646
29647             if(this.labelAlign == 'left'){
29648                 content = [
29649                     {
29650                         tag : 'div',
29651                         cls : 'column',
29652                         html : this.fieldLabel
29653                     },
29654                     {
29655                         tag : 'div',
29656                         cls : 'column',
29657                         cn : managerWidget
29658                     }
29659                 ];
29660                 
29661                 if(this.labelWidth > 12){
29662                     content[0].style = "width: " + this.labelWidth + 'px';
29663                 }
29664
29665                 if(this.labelWidth < 13 && this.labelmd == 0){
29666                     this.labelmd = this.labelWidth;
29667                 }
29668
29669                 if(this.labellg > 0){
29670                     content[0].cls += ' col-lg-' + this.labellg;
29671                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29672                 }
29673
29674                 if(this.labelmd > 0){
29675                     content[0].cls += ' col-md-' + this.labelmd;
29676                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29677                 }
29678
29679                 if(this.labelsm > 0){
29680                     content[0].cls += ' col-sm-' + this.labelsm;
29681                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29682                 }
29683
29684                 if(this.labelxs > 0){
29685                     content[0].cls += ' col-xs-' + this.labelxs;
29686                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29687                 }
29688                 
29689             }
29690         }
29691         
29692         var cfg = {
29693             tag : 'div',
29694             cls : 'row clearfix',
29695             cn : content
29696         };
29697         
29698         return cfg;
29699         
29700     },
29701     
29702     initEvents : function()
29703     {
29704         this.managerEl = this.el.select('.roo-document-manager', true).first();
29705         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29706         
29707         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29708         this.selectorEl.hide();
29709         
29710         if(this.multiple){
29711             this.selectorEl.attr('multiple', 'multiple');
29712         }
29713         
29714         this.selectorEl.on('change', this.onFileSelected, this);
29715         
29716         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29717         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29718         
29719         this.uploader.on('click', this.onUploaderClick, this);
29720         
29721         this.renderProgressDialog();
29722         
29723         var _this = this;
29724         
29725         window.addEventListener("resize", function() { _this.refresh(); } );
29726         
29727         this.fireEvent('initial', this);
29728     },
29729     
29730     renderProgressDialog : function()
29731     {
29732         var _this = this;
29733         
29734         this.progressDialog = new Roo.bootstrap.Modal({
29735             cls : 'roo-document-manager-progress-dialog',
29736             allow_close : false,
29737             animate : false,
29738             title : '',
29739             buttons : [
29740                 {
29741                     name  :'cancel',
29742                     weight : 'danger',
29743                     html : 'Cancel'
29744                 }
29745             ], 
29746             listeners : { 
29747                 btnclick : function() {
29748                     _this.uploadCancel();
29749                     this.hide();
29750                 }
29751             }
29752         });
29753          
29754         this.progressDialog.render(Roo.get(document.body));
29755          
29756         this.progress = new Roo.bootstrap.Progress({
29757             cls : 'roo-document-manager-progress',
29758             active : true,
29759             striped : true
29760         });
29761         
29762         this.progress.render(this.progressDialog.getChildContainer());
29763         
29764         this.progressBar = new Roo.bootstrap.ProgressBar({
29765             cls : 'roo-document-manager-progress-bar',
29766             aria_valuenow : 0,
29767             aria_valuemin : 0,
29768             aria_valuemax : 12,
29769             panel : 'success'
29770         });
29771         
29772         this.progressBar.render(this.progress.getChildContainer());
29773     },
29774     
29775     onUploaderClick : function(e)
29776     {
29777         e.preventDefault();
29778      
29779         if(this.fireEvent('beforeselectfile', this) != false){
29780             this.selectorEl.dom.click();
29781         }
29782         
29783     },
29784     
29785     onFileSelected : function(e)
29786     {
29787         e.preventDefault();
29788         
29789         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29790             return;
29791         }
29792         
29793         Roo.each(this.selectorEl.dom.files, function(file){
29794             if(this.fireEvent('inspect', this, file) != false){
29795                 this.files.push(file);
29796             }
29797         }, this);
29798         
29799         this.queue();
29800         
29801     },
29802     
29803     queue : function()
29804     {
29805         this.selectorEl.dom.value = '';
29806         
29807         if(!this.files || !this.files.length){
29808             return;
29809         }
29810         
29811         if(this.boxes > 0 && this.files.length > this.boxes){
29812             this.files = this.files.slice(0, this.boxes);
29813         }
29814         
29815         this.uploader.show();
29816         
29817         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29818             this.uploader.hide();
29819         }
29820         
29821         var _this = this;
29822         
29823         var files = [];
29824         
29825         var docs = [];
29826         
29827         Roo.each(this.files, function(file){
29828             
29829             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29830                 var f = this.renderPreview(file);
29831                 files.push(f);
29832                 return;
29833             }
29834             
29835             if(file.type.indexOf('image') != -1){
29836                 this.delegates.push(
29837                     (function(){
29838                         _this.process(file);
29839                     }).createDelegate(this)
29840                 );
29841         
29842                 return;
29843             }
29844             
29845             docs.push(
29846                 (function(){
29847                     _this.process(file);
29848                 }).createDelegate(this)
29849             );
29850             
29851         }, this);
29852         
29853         this.files = files;
29854         
29855         this.delegates = this.delegates.concat(docs);
29856         
29857         if(!this.delegates.length){
29858             this.refresh();
29859             return;
29860         }
29861         
29862         this.progressBar.aria_valuemax = this.delegates.length;
29863         
29864         this.arrange();
29865         
29866         return;
29867     },
29868     
29869     arrange : function()
29870     {
29871         if(!this.delegates.length){
29872             this.progressDialog.hide();
29873             this.refresh();
29874             return;
29875         }
29876         
29877         var delegate = this.delegates.shift();
29878         
29879         this.progressDialog.show();
29880         
29881         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29882         
29883         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29884         
29885         delegate();
29886     },
29887     
29888     refresh : function()
29889     {
29890         this.uploader.show();
29891         
29892         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29893             this.uploader.hide();
29894         }
29895         
29896         Roo.isTouch ? this.closable(false) : this.closable(true);
29897         
29898         this.fireEvent('refresh', this);
29899     },
29900     
29901     onRemove : function(e, el, o)
29902     {
29903         e.preventDefault();
29904         
29905         this.fireEvent('remove', this, o);
29906         
29907     },
29908     
29909     remove : function(o)
29910     {
29911         var files = [];
29912         
29913         Roo.each(this.files, function(file){
29914             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29915                 files.push(file);
29916                 return;
29917             }
29918
29919             o.target.remove();
29920
29921         }, this);
29922         
29923         this.files = files;
29924         
29925         this.refresh();
29926     },
29927     
29928     clear : function()
29929     {
29930         Roo.each(this.files, function(file){
29931             if(!file.target){
29932                 return;
29933             }
29934             
29935             file.target.remove();
29936
29937         }, this);
29938         
29939         this.files = [];
29940         
29941         this.refresh();
29942     },
29943     
29944     onClick : function(e, el, o)
29945     {
29946         e.preventDefault();
29947         
29948         this.fireEvent('click', this, o);
29949         
29950     },
29951     
29952     closable : function(closable)
29953     {
29954         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29955             
29956             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29957             
29958             if(closable){
29959                 el.show();
29960                 return;
29961             }
29962             
29963             el.hide();
29964             
29965         }, this);
29966     },
29967     
29968     xhrOnLoad : function(xhr)
29969     {
29970         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29971             el.remove();
29972         }, this);
29973         
29974         if (xhr.readyState !== 4) {
29975             this.arrange();
29976             this.fireEvent('exception', this, xhr);
29977             return;
29978         }
29979
29980         var response = Roo.decode(xhr.responseText);
29981         
29982         if(!response.success){
29983             this.arrange();
29984             this.fireEvent('exception', this, xhr);
29985             return;
29986         }
29987         
29988         var file = this.renderPreview(response.data);
29989         
29990         this.files.push(file);
29991         
29992         this.arrange();
29993         
29994         this.fireEvent('afterupload', this, xhr);
29995         
29996     },
29997     
29998     xhrOnError : function(xhr)
29999     {
30000         Roo.log('xhr on error');
30001         
30002         var response = Roo.decode(xhr.responseText);
30003           
30004         Roo.log(response);
30005         
30006         this.arrange();
30007     },
30008     
30009     process : function(file)
30010     {
30011         if(this.fireEvent('process', this, file) !== false){
30012             if(this.editable && file.type.indexOf('image') != -1){
30013                 this.fireEvent('edit', this, file);
30014                 return;
30015             }
30016
30017             this.uploadStart(file, false);
30018
30019             return;
30020         }
30021         
30022     },
30023     
30024     uploadStart : function(file, crop)
30025     {
30026         this.xhr = new XMLHttpRequest();
30027         
30028         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30029             this.arrange();
30030             return;
30031         }
30032         
30033         file.xhr = this.xhr;
30034             
30035         this.managerEl.createChild({
30036             tag : 'div',
30037             cls : 'roo-document-manager-loading',
30038             cn : [
30039                 {
30040                     tag : 'div',
30041                     tooltip : file.name,
30042                     cls : 'roo-document-manager-thumb',
30043                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30044                 }
30045             ]
30046
30047         });
30048
30049         this.xhr.open(this.method, this.url, true);
30050         
30051         var headers = {
30052             "Accept": "application/json",
30053             "Cache-Control": "no-cache",
30054             "X-Requested-With": "XMLHttpRequest"
30055         };
30056         
30057         for (var headerName in headers) {
30058             var headerValue = headers[headerName];
30059             if (headerValue) {
30060                 this.xhr.setRequestHeader(headerName, headerValue);
30061             }
30062         }
30063         
30064         var _this = this;
30065         
30066         this.xhr.onload = function()
30067         {
30068             _this.xhrOnLoad(_this.xhr);
30069         }
30070         
30071         this.xhr.onerror = function()
30072         {
30073             _this.xhrOnError(_this.xhr);
30074         }
30075         
30076         var formData = new FormData();
30077
30078         formData.append('returnHTML', 'NO');
30079         
30080         if(crop){
30081             formData.append('crop', crop);
30082         }
30083         
30084         formData.append(this.paramName, file, file.name);
30085         
30086         var options = {
30087             file : file, 
30088             manually : false
30089         };
30090         
30091         if(this.fireEvent('prepare', this, formData, options) != false){
30092             
30093             if(options.manually){
30094                 return;
30095             }
30096             
30097             this.xhr.send(formData);
30098             return;
30099         };
30100         
30101         this.uploadCancel();
30102     },
30103     
30104     uploadCancel : function()
30105     {
30106         if (this.xhr) {
30107             this.xhr.abort();
30108         }
30109         
30110         this.delegates = [];
30111         
30112         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30113             el.remove();
30114         }, this);
30115         
30116         this.arrange();
30117     },
30118     
30119     renderPreview : function(file)
30120     {
30121         if(typeof(file.target) != 'undefined' && file.target){
30122             return file;
30123         }
30124         
30125         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30126         
30127         var previewEl = this.managerEl.createChild({
30128             tag : 'div',
30129             cls : 'roo-document-manager-preview',
30130             cn : [
30131                 {
30132                     tag : 'div',
30133                     tooltip : file[this.toolTipName],
30134                     cls : 'roo-document-manager-thumb',
30135                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30136                 },
30137                 {
30138                     tag : 'button',
30139                     cls : 'close',
30140                     html : '<i class="fa fa-times-circle"></i>'
30141                 }
30142             ]
30143         });
30144
30145         var close = previewEl.select('button.close', true).first();
30146
30147         close.on('click', this.onRemove, this, file);
30148
30149         file.target = previewEl;
30150
30151         var image = previewEl.select('img', true).first();
30152         
30153         var _this = this;
30154         
30155         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30156         
30157         image.on('click', this.onClick, this, file);
30158         
30159         this.fireEvent('previewrendered', this, file);
30160         
30161         return file;
30162         
30163     },
30164     
30165     onPreviewLoad : function(file, image)
30166     {
30167         if(typeof(file.target) == 'undefined' || !file.target){
30168             return;
30169         }
30170         
30171         var width = image.dom.naturalWidth || image.dom.width;
30172         var height = image.dom.naturalHeight || image.dom.height;
30173         
30174         if(!this.previewResize) {
30175             return;
30176         }
30177         
30178         if(width > height){
30179             file.target.addClass('wide');
30180             return;
30181         }
30182         
30183         file.target.addClass('tall');
30184         return;
30185         
30186     },
30187     
30188     uploadFromSource : function(file, crop)
30189     {
30190         this.xhr = new XMLHttpRequest();
30191         
30192         this.managerEl.createChild({
30193             tag : 'div',
30194             cls : 'roo-document-manager-loading',
30195             cn : [
30196                 {
30197                     tag : 'div',
30198                     tooltip : file.name,
30199                     cls : 'roo-document-manager-thumb',
30200                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30201                 }
30202             ]
30203
30204         });
30205
30206         this.xhr.open(this.method, this.url, true);
30207         
30208         var headers = {
30209             "Accept": "application/json",
30210             "Cache-Control": "no-cache",
30211             "X-Requested-With": "XMLHttpRequest"
30212         };
30213         
30214         for (var headerName in headers) {
30215             var headerValue = headers[headerName];
30216             if (headerValue) {
30217                 this.xhr.setRequestHeader(headerName, headerValue);
30218             }
30219         }
30220         
30221         var _this = this;
30222         
30223         this.xhr.onload = function()
30224         {
30225             _this.xhrOnLoad(_this.xhr);
30226         }
30227         
30228         this.xhr.onerror = function()
30229         {
30230             _this.xhrOnError(_this.xhr);
30231         }
30232         
30233         var formData = new FormData();
30234
30235         formData.append('returnHTML', 'NO');
30236         
30237         formData.append('crop', crop);
30238         
30239         if(typeof(file.filename) != 'undefined'){
30240             formData.append('filename', file.filename);
30241         }
30242         
30243         if(typeof(file.mimetype) != 'undefined'){
30244             formData.append('mimetype', file.mimetype);
30245         }
30246         
30247         Roo.log(formData);
30248         
30249         if(this.fireEvent('prepare', this, formData) != false){
30250             this.xhr.send(formData);
30251         };
30252     }
30253 });
30254
30255 /*
30256 * Licence: LGPL
30257 */
30258
30259 /**
30260  * @class Roo.bootstrap.DocumentViewer
30261  * @extends Roo.bootstrap.Component
30262  * Bootstrap DocumentViewer class
30263  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30264  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30265  * 
30266  * @constructor
30267  * Create a new DocumentViewer
30268  * @param {Object} config The config object
30269  */
30270
30271 Roo.bootstrap.DocumentViewer = function(config){
30272     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30273     
30274     this.addEvents({
30275         /**
30276          * @event initial
30277          * Fire after initEvent
30278          * @param {Roo.bootstrap.DocumentViewer} this
30279          */
30280         "initial" : true,
30281         /**
30282          * @event click
30283          * Fire after click
30284          * @param {Roo.bootstrap.DocumentViewer} this
30285          */
30286         "click" : true,
30287         /**
30288          * @event download
30289          * Fire after download button
30290          * @param {Roo.bootstrap.DocumentViewer} this
30291          */
30292         "download" : true,
30293         /**
30294          * @event trash
30295          * Fire after trash button
30296          * @param {Roo.bootstrap.DocumentViewer} this
30297          */
30298         "trash" : true
30299         
30300     });
30301 };
30302
30303 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30304     
30305     showDownload : true,
30306     
30307     showTrash : true,
30308     
30309     getAutoCreate : function()
30310     {
30311         var cfg = {
30312             tag : 'div',
30313             cls : 'roo-document-viewer',
30314             cn : [
30315                 {
30316                     tag : 'div',
30317                     cls : 'roo-document-viewer-body',
30318                     cn : [
30319                         {
30320                             tag : 'div',
30321                             cls : 'roo-document-viewer-thumb',
30322                             cn : [
30323                                 {
30324                                     tag : 'img',
30325                                     cls : 'roo-document-viewer-image'
30326                                 }
30327                             ]
30328                         }
30329                     ]
30330                 },
30331                 {
30332                     tag : 'div',
30333                     cls : 'roo-document-viewer-footer',
30334                     cn : {
30335                         tag : 'div',
30336                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30337                         cn : [
30338                             {
30339                                 tag : 'div',
30340                                 cls : 'btn-group roo-document-viewer-download',
30341                                 cn : [
30342                                     {
30343                                         tag : 'button',
30344                                         cls : 'btn btn-default',
30345                                         html : '<i class="fa fa-download"></i>'
30346                                     }
30347                                 ]
30348                             },
30349                             {
30350                                 tag : 'div',
30351                                 cls : 'btn-group roo-document-viewer-trash',
30352                                 cn : [
30353                                     {
30354                                         tag : 'button',
30355                                         cls : 'btn btn-default',
30356                                         html : '<i class="fa fa-trash"></i>'
30357                                     }
30358                                 ]
30359                             }
30360                         ]
30361                     }
30362                 }
30363             ]
30364         };
30365         
30366         return cfg;
30367     },
30368     
30369     initEvents : function()
30370     {
30371         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30372         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30373         
30374         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30375         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30376         
30377         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30378         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30379         
30380         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30381         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30382         
30383         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30384         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30385         
30386         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30387         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30388         
30389         this.bodyEl.on('click', this.onClick, this);
30390         this.downloadBtn.on('click', this.onDownload, this);
30391         this.trashBtn.on('click', this.onTrash, this);
30392         
30393         this.downloadBtn.hide();
30394         this.trashBtn.hide();
30395         
30396         if(this.showDownload){
30397             this.downloadBtn.show();
30398         }
30399         
30400         if(this.showTrash){
30401             this.trashBtn.show();
30402         }
30403         
30404         if(!this.showDownload && !this.showTrash) {
30405             this.footerEl.hide();
30406         }
30407         
30408     },
30409     
30410     initial : function()
30411     {
30412         this.fireEvent('initial', this);
30413         
30414     },
30415     
30416     onClick : function(e)
30417     {
30418         e.preventDefault();
30419         
30420         this.fireEvent('click', this);
30421     },
30422     
30423     onDownload : function(e)
30424     {
30425         e.preventDefault();
30426         
30427         this.fireEvent('download', this);
30428     },
30429     
30430     onTrash : function(e)
30431     {
30432         e.preventDefault();
30433         
30434         this.fireEvent('trash', this);
30435     }
30436     
30437 });
30438 /*
30439  * - LGPL
30440  *
30441  * nav progress bar
30442  * 
30443  */
30444
30445 /**
30446  * @class Roo.bootstrap.NavProgressBar
30447  * @extends Roo.bootstrap.Component
30448  * Bootstrap NavProgressBar class
30449  * 
30450  * @constructor
30451  * Create a new nav progress bar
30452  * @param {Object} config The config object
30453  */
30454
30455 Roo.bootstrap.NavProgressBar = function(config){
30456     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30457
30458     this.bullets = this.bullets || [];
30459    
30460 //    Roo.bootstrap.NavProgressBar.register(this);
30461      this.addEvents({
30462         /**
30463              * @event changed
30464              * Fires when the active item changes
30465              * @param {Roo.bootstrap.NavProgressBar} this
30466              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30467              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30468          */
30469         'changed': true
30470      });
30471     
30472 };
30473
30474 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30475     
30476     bullets : [],
30477     barItems : [],
30478     
30479     getAutoCreate : function()
30480     {
30481         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30482         
30483         cfg = {
30484             tag : 'div',
30485             cls : 'roo-navigation-bar-group',
30486             cn : [
30487                 {
30488                     tag : 'div',
30489                     cls : 'roo-navigation-top-bar'
30490                 },
30491                 {
30492                     tag : 'div',
30493                     cls : 'roo-navigation-bullets-bar',
30494                     cn : [
30495                         {
30496                             tag : 'ul',
30497                             cls : 'roo-navigation-bar'
30498                         }
30499                     ]
30500                 },
30501                 
30502                 {
30503                     tag : 'div',
30504                     cls : 'roo-navigation-bottom-bar'
30505                 }
30506             ]
30507             
30508         };
30509         
30510         return cfg;
30511         
30512     },
30513     
30514     initEvents: function() 
30515     {
30516         
30517     },
30518     
30519     onRender : function(ct, position) 
30520     {
30521         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30522         
30523         if(this.bullets.length){
30524             Roo.each(this.bullets, function(b){
30525                this.addItem(b);
30526             }, this);
30527         }
30528         
30529         this.format();
30530         
30531     },
30532     
30533     addItem : function(cfg)
30534     {
30535         var item = new Roo.bootstrap.NavProgressItem(cfg);
30536         
30537         item.parentId = this.id;
30538         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30539         
30540         if(cfg.html){
30541             var top = new Roo.bootstrap.Element({
30542                 tag : 'div',
30543                 cls : 'roo-navigation-bar-text'
30544             });
30545             
30546             var bottom = new Roo.bootstrap.Element({
30547                 tag : 'div',
30548                 cls : 'roo-navigation-bar-text'
30549             });
30550             
30551             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30552             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30553             
30554             var topText = new Roo.bootstrap.Element({
30555                 tag : 'span',
30556                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30557             });
30558             
30559             var bottomText = new Roo.bootstrap.Element({
30560                 tag : 'span',
30561                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30562             });
30563             
30564             topText.onRender(top.el, null);
30565             bottomText.onRender(bottom.el, null);
30566             
30567             item.topEl = top;
30568             item.bottomEl = bottom;
30569         }
30570         
30571         this.barItems.push(item);
30572         
30573         return item;
30574     },
30575     
30576     getActive : function()
30577     {
30578         var active = false;
30579         
30580         Roo.each(this.barItems, function(v){
30581             
30582             if (!v.isActive()) {
30583                 return;
30584             }
30585             
30586             active = v;
30587             return false;
30588             
30589         });
30590         
30591         return active;
30592     },
30593     
30594     setActiveItem : function(item)
30595     {
30596         var prev = false;
30597         
30598         Roo.each(this.barItems, function(v){
30599             if (v.rid == item.rid) {
30600                 return ;
30601             }
30602             
30603             if (v.isActive()) {
30604                 v.setActive(false);
30605                 prev = v;
30606             }
30607         });
30608
30609         item.setActive(true);
30610         
30611         this.fireEvent('changed', this, item, prev);
30612     },
30613     
30614     getBarItem: function(rid)
30615     {
30616         var ret = false;
30617         
30618         Roo.each(this.barItems, function(e) {
30619             if (e.rid != rid) {
30620                 return;
30621             }
30622             
30623             ret =  e;
30624             return false;
30625         });
30626         
30627         return ret;
30628     },
30629     
30630     indexOfItem : function(item)
30631     {
30632         var index = false;
30633         
30634         Roo.each(this.barItems, function(v, i){
30635             
30636             if (v.rid != item.rid) {
30637                 return;
30638             }
30639             
30640             index = i;
30641             return false
30642         });
30643         
30644         return index;
30645     },
30646     
30647     setActiveNext : function()
30648     {
30649         var i = this.indexOfItem(this.getActive());
30650         
30651         if (i > this.barItems.length) {
30652             return;
30653         }
30654         
30655         this.setActiveItem(this.barItems[i+1]);
30656     },
30657     
30658     setActivePrev : function()
30659     {
30660         var i = this.indexOfItem(this.getActive());
30661         
30662         if (i  < 1) {
30663             return;
30664         }
30665         
30666         this.setActiveItem(this.barItems[i-1]);
30667     },
30668     
30669     format : function()
30670     {
30671         if(!this.barItems.length){
30672             return;
30673         }
30674      
30675         var width = 100 / this.barItems.length;
30676         
30677         Roo.each(this.barItems, function(i){
30678             i.el.setStyle('width', width + '%');
30679             i.topEl.el.setStyle('width', width + '%');
30680             i.bottomEl.el.setStyle('width', width + '%');
30681         }, this);
30682         
30683     }
30684     
30685 });
30686 /*
30687  * - LGPL
30688  *
30689  * Nav Progress Item
30690  * 
30691  */
30692
30693 /**
30694  * @class Roo.bootstrap.NavProgressItem
30695  * @extends Roo.bootstrap.Component
30696  * Bootstrap NavProgressItem class
30697  * @cfg {String} rid the reference id
30698  * @cfg {Boolean} active (true|false) Is item active default false
30699  * @cfg {Boolean} disabled (true|false) Is item active default false
30700  * @cfg {String} html
30701  * @cfg {String} position (top|bottom) text position default bottom
30702  * @cfg {String} icon show icon instead of number
30703  * 
30704  * @constructor
30705  * Create a new NavProgressItem
30706  * @param {Object} config The config object
30707  */
30708 Roo.bootstrap.NavProgressItem = function(config){
30709     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30710     this.addEvents({
30711         // raw events
30712         /**
30713          * @event click
30714          * The raw click event for the entire grid.
30715          * @param {Roo.bootstrap.NavProgressItem} this
30716          * @param {Roo.EventObject} e
30717          */
30718         "click" : true
30719     });
30720    
30721 };
30722
30723 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30724     
30725     rid : '',
30726     active : false,
30727     disabled : false,
30728     html : '',
30729     position : 'bottom',
30730     icon : false,
30731     
30732     getAutoCreate : function()
30733     {
30734         var iconCls = 'roo-navigation-bar-item-icon';
30735         
30736         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30737         
30738         var cfg = {
30739             tag: 'li',
30740             cls: 'roo-navigation-bar-item',
30741             cn : [
30742                 {
30743                     tag : 'i',
30744                     cls : iconCls
30745                 }
30746             ]
30747         };
30748         
30749         if(this.active){
30750             cfg.cls += ' active';
30751         }
30752         if(this.disabled){
30753             cfg.cls += ' disabled';
30754         }
30755         
30756         return cfg;
30757     },
30758     
30759     disable : function()
30760     {
30761         this.setDisabled(true);
30762     },
30763     
30764     enable : function()
30765     {
30766         this.setDisabled(false);
30767     },
30768     
30769     initEvents: function() 
30770     {
30771         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30772         
30773         this.iconEl.on('click', this.onClick, this);
30774     },
30775     
30776     onClick : function(e)
30777     {
30778         e.preventDefault();
30779         
30780         if(this.disabled){
30781             return;
30782         }
30783         
30784         if(this.fireEvent('click', this, e) === false){
30785             return;
30786         };
30787         
30788         this.parent().setActiveItem(this);
30789     },
30790     
30791     isActive: function () 
30792     {
30793         return this.active;
30794     },
30795     
30796     setActive : function(state)
30797     {
30798         if(this.active == state){
30799             return;
30800         }
30801         
30802         this.active = state;
30803         
30804         if (state) {
30805             this.el.addClass('active');
30806             return;
30807         }
30808         
30809         this.el.removeClass('active');
30810         
30811         return;
30812     },
30813     
30814     setDisabled : function(state)
30815     {
30816         if(this.disabled == state){
30817             return;
30818         }
30819         
30820         this.disabled = state;
30821         
30822         if (state) {
30823             this.el.addClass('disabled');
30824             return;
30825         }
30826         
30827         this.el.removeClass('disabled');
30828     },
30829     
30830     tooltipEl : function()
30831     {
30832         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30833     }
30834 });
30835  
30836
30837  /*
30838  * - LGPL
30839  *
30840  * FieldLabel
30841  * 
30842  */
30843
30844 /**
30845  * @class Roo.bootstrap.FieldLabel
30846  * @extends Roo.bootstrap.Component
30847  * Bootstrap FieldLabel class
30848  * @cfg {String} html contents of the element
30849  * @cfg {String} tag tag of the element default label
30850  * @cfg {String} cls class of the element
30851  * @cfg {String} target label target 
30852  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30853  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30854  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30855  * @cfg {String} iconTooltip default "This field is required"
30856  * @cfg {String} indicatorpos (left|right) default left
30857  * 
30858  * @constructor
30859  * Create a new FieldLabel
30860  * @param {Object} config The config object
30861  */
30862
30863 Roo.bootstrap.FieldLabel = function(config){
30864     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30865     
30866     this.addEvents({
30867             /**
30868              * @event invalid
30869              * Fires after the field has been marked as invalid.
30870              * @param {Roo.form.FieldLabel} this
30871              * @param {String} msg The validation message
30872              */
30873             invalid : true,
30874             /**
30875              * @event valid
30876              * Fires after the field has been validated with no errors.
30877              * @param {Roo.form.FieldLabel} this
30878              */
30879             valid : true
30880         });
30881 };
30882
30883 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30884     
30885     tag: 'label',
30886     cls: '',
30887     html: '',
30888     target: '',
30889     allowBlank : true,
30890     invalidClass : 'has-warning',
30891     validClass : 'has-success',
30892     iconTooltip : 'This field is required',
30893     indicatorpos : 'left',
30894     
30895     getAutoCreate : function(){
30896         
30897         var cls = "";
30898         if (!this.allowBlank) {
30899             cls  = "visible";
30900         }
30901         
30902         var cfg = {
30903             tag : this.tag,
30904             cls : 'roo-bootstrap-field-label ' + this.cls,
30905             for : this.target,
30906             cn : [
30907                 {
30908                     tag : 'i',
30909                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30910                     tooltip : this.iconTooltip
30911                 },
30912                 {
30913                     tag : 'span',
30914                     html : this.html
30915                 }
30916             ] 
30917         };
30918         
30919         if(this.indicatorpos == 'right'){
30920             var cfg = {
30921                 tag : this.tag,
30922                 cls : 'roo-bootstrap-field-label ' + this.cls,
30923                 for : this.target,
30924                 cn : [
30925                     {
30926                         tag : 'span',
30927                         html : this.html
30928                     },
30929                     {
30930                         tag : 'i',
30931                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30932                         tooltip : this.iconTooltip
30933                     }
30934                 ] 
30935             };
30936         }
30937         
30938         return cfg;
30939     },
30940     
30941     initEvents: function() 
30942     {
30943         Roo.bootstrap.Element.superclass.initEvents.call(this);
30944         
30945         this.indicator = this.indicatorEl();
30946         
30947         if(this.indicator){
30948             this.indicator.removeClass('visible');
30949             this.indicator.addClass('invisible');
30950         }
30951         
30952         Roo.bootstrap.FieldLabel.register(this);
30953     },
30954     
30955     indicatorEl : function()
30956     {
30957         var indicator = this.el.select('i.roo-required-indicator',true).first();
30958         
30959         if(!indicator){
30960             return false;
30961         }
30962         
30963         return indicator;
30964         
30965     },
30966     
30967     /**
30968      * Mark this field as valid
30969      */
30970     markValid : function()
30971     {
30972         if(this.indicator){
30973             this.indicator.removeClass('visible');
30974             this.indicator.addClass('invisible');
30975         }
30976         if (Roo.bootstrap.version == 3) {
30977             this.el.removeClass(this.invalidClass);
30978             this.el.addClass(this.validClass);
30979         } else {
30980             this.el.removeClass('is-invalid');
30981             this.el.addClass('is-valid');
30982         }
30983         
30984         
30985         this.fireEvent('valid', this);
30986     },
30987     
30988     /**
30989      * Mark this field as invalid
30990      * @param {String} msg The validation message
30991      */
30992     markInvalid : function(msg)
30993     {
30994         if(this.indicator){
30995             this.indicator.removeClass('invisible');
30996             this.indicator.addClass('visible');
30997         }
30998           if (Roo.bootstrap.version == 3) {
30999             this.el.removeClass(this.validClass);
31000             this.el.addClass(this.invalidClass);
31001         } else {
31002             this.el.removeClass('is-valid');
31003             this.el.addClass('is-invalid');
31004         }
31005         
31006         
31007         this.fireEvent('invalid', this, msg);
31008     }
31009     
31010    
31011 });
31012
31013 Roo.apply(Roo.bootstrap.FieldLabel, {
31014     
31015     groups: {},
31016     
31017      /**
31018     * register a FieldLabel Group
31019     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31020     */
31021     register : function(label)
31022     {
31023         if(this.groups.hasOwnProperty(label.target)){
31024             return;
31025         }
31026      
31027         this.groups[label.target] = label;
31028         
31029     },
31030     /**
31031     * fetch a FieldLabel Group based on the target
31032     * @param {string} target
31033     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31034     */
31035     get: function(target) {
31036         if (typeof(this.groups[target]) == 'undefined') {
31037             return false;
31038         }
31039         
31040         return this.groups[target] ;
31041     }
31042 });
31043
31044  
31045
31046  /*
31047  * - LGPL
31048  *
31049  * page DateSplitField.
31050  * 
31051  */
31052
31053
31054 /**
31055  * @class Roo.bootstrap.DateSplitField
31056  * @extends Roo.bootstrap.Component
31057  * Bootstrap DateSplitField class
31058  * @cfg {string} fieldLabel - the label associated
31059  * @cfg {Number} labelWidth set the width of label (0-12)
31060  * @cfg {String} labelAlign (top|left)
31061  * @cfg {Boolean} dayAllowBlank (true|false) default false
31062  * @cfg {Boolean} monthAllowBlank (true|false) default false
31063  * @cfg {Boolean} yearAllowBlank (true|false) default false
31064  * @cfg {string} dayPlaceholder 
31065  * @cfg {string} monthPlaceholder
31066  * @cfg {string} yearPlaceholder
31067  * @cfg {string} dayFormat default 'd'
31068  * @cfg {string} monthFormat default 'm'
31069  * @cfg {string} yearFormat default 'Y'
31070  * @cfg {Number} labellg set the width of label (1-12)
31071  * @cfg {Number} labelmd set the width of label (1-12)
31072  * @cfg {Number} labelsm set the width of label (1-12)
31073  * @cfg {Number} labelxs set the width of label (1-12)
31074
31075  *     
31076  * @constructor
31077  * Create a new DateSplitField
31078  * @param {Object} config The config object
31079  */
31080
31081 Roo.bootstrap.DateSplitField = function(config){
31082     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31083     
31084     this.addEvents({
31085         // raw events
31086          /**
31087          * @event years
31088          * getting the data of years
31089          * @param {Roo.bootstrap.DateSplitField} this
31090          * @param {Object} years
31091          */
31092         "years" : true,
31093         /**
31094          * @event days
31095          * getting the data of days
31096          * @param {Roo.bootstrap.DateSplitField} this
31097          * @param {Object} days
31098          */
31099         "days" : true,
31100         /**
31101          * @event invalid
31102          * Fires after the field has been marked as invalid.
31103          * @param {Roo.form.Field} this
31104          * @param {String} msg The validation message
31105          */
31106         invalid : true,
31107        /**
31108          * @event valid
31109          * Fires after the field has been validated with no errors.
31110          * @param {Roo.form.Field} this
31111          */
31112         valid : true
31113     });
31114 };
31115
31116 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31117     
31118     fieldLabel : '',
31119     labelAlign : 'top',
31120     labelWidth : 3,
31121     dayAllowBlank : false,
31122     monthAllowBlank : false,
31123     yearAllowBlank : false,
31124     dayPlaceholder : '',
31125     monthPlaceholder : '',
31126     yearPlaceholder : '',
31127     dayFormat : 'd',
31128     monthFormat : 'm',
31129     yearFormat : 'Y',
31130     isFormField : true,
31131     labellg : 0,
31132     labelmd : 0,
31133     labelsm : 0,
31134     labelxs : 0,
31135     
31136     getAutoCreate : function()
31137     {
31138         var cfg = {
31139             tag : 'div',
31140             cls : 'row roo-date-split-field-group',
31141             cn : [
31142                 {
31143                     tag : 'input',
31144                     type : 'hidden',
31145                     cls : 'form-hidden-field roo-date-split-field-group-value',
31146                     name : this.name
31147                 }
31148             ]
31149         };
31150         
31151         var labelCls = 'col-md-12';
31152         var contentCls = 'col-md-4';
31153         
31154         if(this.fieldLabel){
31155             
31156             var label = {
31157                 tag : 'div',
31158                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31159                 cn : [
31160                     {
31161                         tag : 'label',
31162                         html : this.fieldLabel
31163                     }
31164                 ]
31165             };
31166             
31167             if(this.labelAlign == 'left'){
31168             
31169                 if(this.labelWidth > 12){
31170                     label.style = "width: " + this.labelWidth + 'px';
31171                 }
31172
31173                 if(this.labelWidth < 13 && this.labelmd == 0){
31174                     this.labelmd = this.labelWidth;
31175                 }
31176
31177                 if(this.labellg > 0){
31178                     labelCls = ' col-lg-' + this.labellg;
31179                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31180                 }
31181
31182                 if(this.labelmd > 0){
31183                     labelCls = ' col-md-' + this.labelmd;
31184                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31185                 }
31186
31187                 if(this.labelsm > 0){
31188                     labelCls = ' col-sm-' + this.labelsm;
31189                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31190                 }
31191
31192                 if(this.labelxs > 0){
31193                     labelCls = ' col-xs-' + this.labelxs;
31194                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31195                 }
31196             }
31197             
31198             label.cls += ' ' + labelCls;
31199             
31200             cfg.cn.push(label);
31201         }
31202         
31203         Roo.each(['day', 'month', 'year'], function(t){
31204             cfg.cn.push({
31205                 tag : 'div',
31206                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31207             });
31208         }, this);
31209         
31210         return cfg;
31211     },
31212     
31213     inputEl: function ()
31214     {
31215         return this.el.select('.roo-date-split-field-group-value', true).first();
31216     },
31217     
31218     onRender : function(ct, position) 
31219     {
31220         var _this = this;
31221         
31222         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31223         
31224         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31225         
31226         this.dayField = new Roo.bootstrap.ComboBox({
31227             allowBlank : this.dayAllowBlank,
31228             alwaysQuery : true,
31229             displayField : 'value',
31230             editable : false,
31231             fieldLabel : '',
31232             forceSelection : true,
31233             mode : 'local',
31234             placeholder : this.dayPlaceholder,
31235             selectOnFocus : true,
31236             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31237             triggerAction : 'all',
31238             typeAhead : true,
31239             valueField : 'value',
31240             store : new Roo.data.SimpleStore({
31241                 data : (function() {    
31242                     var days = [];
31243                     _this.fireEvent('days', _this, days);
31244                     return days;
31245                 })(),
31246                 fields : [ 'value' ]
31247             }),
31248             listeners : {
31249                 select : function (_self, record, index)
31250                 {
31251                     _this.setValue(_this.getValue());
31252                 }
31253             }
31254         });
31255
31256         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31257         
31258         this.monthField = new Roo.bootstrap.MonthField({
31259             after : '<i class=\"fa fa-calendar\"></i>',
31260             allowBlank : this.monthAllowBlank,
31261             placeholder : this.monthPlaceholder,
31262             readOnly : true,
31263             listeners : {
31264                 render : function (_self)
31265                 {
31266                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31267                         e.preventDefault();
31268                         _self.focus();
31269                     });
31270                 },
31271                 select : function (_self, oldvalue, newvalue)
31272                 {
31273                     _this.setValue(_this.getValue());
31274                 }
31275             }
31276         });
31277         
31278         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31279         
31280         this.yearField = new Roo.bootstrap.ComboBox({
31281             allowBlank : this.yearAllowBlank,
31282             alwaysQuery : true,
31283             displayField : 'value',
31284             editable : false,
31285             fieldLabel : '',
31286             forceSelection : true,
31287             mode : 'local',
31288             placeholder : this.yearPlaceholder,
31289             selectOnFocus : true,
31290             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31291             triggerAction : 'all',
31292             typeAhead : true,
31293             valueField : 'value',
31294             store : new Roo.data.SimpleStore({
31295                 data : (function() {
31296                     var years = [];
31297                     _this.fireEvent('years', _this, years);
31298                     return years;
31299                 })(),
31300                 fields : [ 'value' ]
31301             }),
31302             listeners : {
31303                 select : function (_self, record, index)
31304                 {
31305                     _this.setValue(_this.getValue());
31306                 }
31307             }
31308         });
31309
31310         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31311     },
31312     
31313     setValue : function(v, format)
31314     {
31315         this.inputEl.dom.value = v;
31316         
31317         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31318         
31319         var d = Date.parseDate(v, f);
31320         
31321         if(!d){
31322             this.validate();
31323             return;
31324         }
31325         
31326         this.setDay(d.format(this.dayFormat));
31327         this.setMonth(d.format(this.monthFormat));
31328         this.setYear(d.format(this.yearFormat));
31329         
31330         this.validate();
31331         
31332         return;
31333     },
31334     
31335     setDay : function(v)
31336     {
31337         this.dayField.setValue(v);
31338         this.inputEl.dom.value = this.getValue();
31339         this.validate();
31340         return;
31341     },
31342     
31343     setMonth : function(v)
31344     {
31345         this.monthField.setValue(v, true);
31346         this.inputEl.dom.value = this.getValue();
31347         this.validate();
31348         return;
31349     },
31350     
31351     setYear : function(v)
31352     {
31353         this.yearField.setValue(v);
31354         this.inputEl.dom.value = this.getValue();
31355         this.validate();
31356         return;
31357     },
31358     
31359     getDay : function()
31360     {
31361         return this.dayField.getValue();
31362     },
31363     
31364     getMonth : function()
31365     {
31366         return this.monthField.getValue();
31367     },
31368     
31369     getYear : function()
31370     {
31371         return this.yearField.getValue();
31372     },
31373     
31374     getValue : function()
31375     {
31376         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31377         
31378         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31379         
31380         return date;
31381     },
31382     
31383     reset : function()
31384     {
31385         this.setDay('');
31386         this.setMonth('');
31387         this.setYear('');
31388         this.inputEl.dom.value = '';
31389         this.validate();
31390         return;
31391     },
31392     
31393     validate : function()
31394     {
31395         var d = this.dayField.validate();
31396         var m = this.monthField.validate();
31397         var y = this.yearField.validate();
31398         
31399         var valid = true;
31400         
31401         if(
31402                 (!this.dayAllowBlank && !d) ||
31403                 (!this.monthAllowBlank && !m) ||
31404                 (!this.yearAllowBlank && !y)
31405         ){
31406             valid = false;
31407         }
31408         
31409         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31410             return valid;
31411         }
31412         
31413         if(valid){
31414             this.markValid();
31415             return valid;
31416         }
31417         
31418         this.markInvalid();
31419         
31420         return valid;
31421     },
31422     
31423     markValid : function()
31424     {
31425         
31426         var label = this.el.select('label', true).first();
31427         var icon = this.el.select('i.fa-star', true).first();
31428
31429         if(label && icon){
31430             icon.remove();
31431         }
31432         
31433         this.fireEvent('valid', this);
31434     },
31435     
31436      /**
31437      * Mark this field as invalid
31438      * @param {String} msg The validation message
31439      */
31440     markInvalid : function(msg)
31441     {
31442         
31443         var label = this.el.select('label', true).first();
31444         var icon = this.el.select('i.fa-star', true).first();
31445
31446         if(label && !icon){
31447             this.el.select('.roo-date-split-field-label', true).createChild({
31448                 tag : 'i',
31449                 cls : 'text-danger fa fa-lg fa-star',
31450                 tooltip : 'This field is required',
31451                 style : 'margin-right:5px;'
31452             }, label, true);
31453         }
31454         
31455         this.fireEvent('invalid', this, msg);
31456     },
31457     
31458     clearInvalid : function()
31459     {
31460         var label = this.el.select('label', true).first();
31461         var icon = this.el.select('i.fa-star', true).first();
31462
31463         if(label && icon){
31464             icon.remove();
31465         }
31466         
31467         this.fireEvent('valid', this);
31468     },
31469     
31470     getName: function()
31471     {
31472         return this.name;
31473     }
31474     
31475 });
31476
31477  /**
31478  *
31479  * This is based on 
31480  * http://masonry.desandro.com
31481  *
31482  * The idea is to render all the bricks based on vertical width...
31483  *
31484  * The original code extends 'outlayer' - we might need to use that....
31485  * 
31486  */
31487
31488
31489 /**
31490  * @class Roo.bootstrap.LayoutMasonry
31491  * @extends Roo.bootstrap.Component
31492  * Bootstrap Layout Masonry class
31493  * 
31494  * @constructor
31495  * Create a new Element
31496  * @param {Object} config The config object
31497  */
31498
31499 Roo.bootstrap.LayoutMasonry = function(config){
31500     
31501     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31502     
31503     this.bricks = [];
31504     
31505     Roo.bootstrap.LayoutMasonry.register(this);
31506     
31507     this.addEvents({
31508         // raw events
31509         /**
31510          * @event layout
31511          * Fire after layout the items
31512          * @param {Roo.bootstrap.LayoutMasonry} this
31513          * @param {Roo.EventObject} e
31514          */
31515         "layout" : true
31516     });
31517     
31518 };
31519
31520 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31521     
31522     /**
31523      * @cfg {Boolean} isLayoutInstant = no animation?
31524      */   
31525     isLayoutInstant : false, // needed?
31526    
31527     /**
31528      * @cfg {Number} boxWidth  width of the columns
31529      */   
31530     boxWidth : 450,
31531     
31532       /**
31533      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31534      */   
31535     boxHeight : 0,
31536     
31537     /**
31538      * @cfg {Number} padWidth padding below box..
31539      */   
31540     padWidth : 10, 
31541     
31542     /**
31543      * @cfg {Number} gutter gutter width..
31544      */   
31545     gutter : 10,
31546     
31547      /**
31548      * @cfg {Number} maxCols maximum number of columns
31549      */   
31550     
31551     maxCols: 0,
31552     
31553     /**
31554      * @cfg {Boolean} isAutoInitial defalut true
31555      */   
31556     isAutoInitial : true, 
31557     
31558     containerWidth: 0,
31559     
31560     /**
31561      * @cfg {Boolean} isHorizontal defalut false
31562      */   
31563     isHorizontal : false, 
31564
31565     currentSize : null,
31566     
31567     tag: 'div',
31568     
31569     cls: '',
31570     
31571     bricks: null, //CompositeElement
31572     
31573     cols : 1,
31574     
31575     _isLayoutInited : false,
31576     
31577 //    isAlternative : false, // only use for vertical layout...
31578     
31579     /**
31580      * @cfg {Number} alternativePadWidth padding below box..
31581      */   
31582     alternativePadWidth : 50,
31583     
31584     selectedBrick : [],
31585     
31586     getAutoCreate : function(){
31587         
31588         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31589         
31590         var cfg = {
31591             tag: this.tag,
31592             cls: 'blog-masonary-wrapper ' + this.cls,
31593             cn : {
31594                 cls : 'mas-boxes masonary'
31595             }
31596         };
31597         
31598         return cfg;
31599     },
31600     
31601     getChildContainer: function( )
31602     {
31603         if (this.boxesEl) {
31604             return this.boxesEl;
31605         }
31606         
31607         this.boxesEl = this.el.select('.mas-boxes').first();
31608         
31609         return this.boxesEl;
31610     },
31611     
31612     
31613     initEvents : function()
31614     {
31615         var _this = this;
31616         
31617         if(this.isAutoInitial){
31618             Roo.log('hook children rendered');
31619             this.on('childrenrendered', function() {
31620                 Roo.log('children rendered');
31621                 _this.initial();
31622             } ,this);
31623         }
31624     },
31625     
31626     initial : function()
31627     {
31628         this.selectedBrick = [];
31629         
31630         this.currentSize = this.el.getBox(true);
31631         
31632         Roo.EventManager.onWindowResize(this.resize, this); 
31633
31634         if(!this.isAutoInitial){
31635             this.layout();
31636             return;
31637         }
31638         
31639         this.layout();
31640         
31641         return;
31642         //this.layout.defer(500,this);
31643         
31644     },
31645     
31646     resize : function()
31647     {
31648         var cs = this.el.getBox(true);
31649         
31650         if (
31651                 this.currentSize.width == cs.width && 
31652                 this.currentSize.x == cs.x && 
31653                 this.currentSize.height == cs.height && 
31654                 this.currentSize.y == cs.y 
31655         ) {
31656             Roo.log("no change in with or X or Y");
31657             return;
31658         }
31659         
31660         this.currentSize = cs;
31661         
31662         this.layout();
31663         
31664     },
31665     
31666     layout : function()
31667     {   
31668         this._resetLayout();
31669         
31670         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31671         
31672         this.layoutItems( isInstant );
31673       
31674         this._isLayoutInited = true;
31675         
31676         this.fireEvent('layout', this);
31677         
31678     },
31679     
31680     _resetLayout : function()
31681     {
31682         if(this.isHorizontal){
31683             this.horizontalMeasureColumns();
31684             return;
31685         }
31686         
31687         this.verticalMeasureColumns();
31688         
31689     },
31690     
31691     verticalMeasureColumns : function()
31692     {
31693         this.getContainerWidth();
31694         
31695 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31696 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31697 //            return;
31698 //        }
31699         
31700         var boxWidth = this.boxWidth + this.padWidth;
31701         
31702         if(this.containerWidth < this.boxWidth){
31703             boxWidth = this.containerWidth
31704         }
31705         
31706         var containerWidth = this.containerWidth;
31707         
31708         var cols = Math.floor(containerWidth / boxWidth);
31709         
31710         this.cols = Math.max( cols, 1 );
31711         
31712         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31713         
31714         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31715         
31716         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31717         
31718         this.colWidth = boxWidth + avail - this.padWidth;
31719         
31720         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31721         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31722     },
31723     
31724     horizontalMeasureColumns : function()
31725     {
31726         this.getContainerWidth();
31727         
31728         var boxWidth = this.boxWidth;
31729         
31730         if(this.containerWidth < boxWidth){
31731             boxWidth = this.containerWidth;
31732         }
31733         
31734         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31735         
31736         this.el.setHeight(boxWidth);
31737         
31738     },
31739     
31740     getContainerWidth : function()
31741     {
31742         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31743     },
31744     
31745     layoutItems : function( isInstant )
31746     {
31747         Roo.log(this.bricks);
31748         
31749         var items = Roo.apply([], this.bricks);
31750         
31751         if(this.isHorizontal){
31752             this._horizontalLayoutItems( items , isInstant );
31753             return;
31754         }
31755         
31756 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31757 //            this._verticalAlternativeLayoutItems( items , isInstant );
31758 //            return;
31759 //        }
31760         
31761         this._verticalLayoutItems( items , isInstant );
31762         
31763     },
31764     
31765     _verticalLayoutItems : function ( items , isInstant)
31766     {
31767         if ( !items || !items.length ) {
31768             return;
31769         }
31770         
31771         var standard = [
31772             ['xs', 'xs', 'xs', 'tall'],
31773             ['xs', 'xs', 'tall'],
31774             ['xs', 'xs', 'sm'],
31775             ['xs', 'xs', 'xs'],
31776             ['xs', 'tall'],
31777             ['xs', 'sm'],
31778             ['xs', 'xs'],
31779             ['xs'],
31780             
31781             ['sm', 'xs', 'xs'],
31782             ['sm', 'xs'],
31783             ['sm'],
31784             
31785             ['tall', 'xs', 'xs', 'xs'],
31786             ['tall', 'xs', 'xs'],
31787             ['tall', 'xs'],
31788             ['tall']
31789             
31790         ];
31791         
31792         var queue = [];
31793         
31794         var boxes = [];
31795         
31796         var box = [];
31797         
31798         Roo.each(items, function(item, k){
31799             
31800             switch (item.size) {
31801                 // these layouts take up a full box,
31802                 case 'md' :
31803                 case 'md-left' :
31804                 case 'md-right' :
31805                 case 'wide' :
31806                     
31807                     if(box.length){
31808                         boxes.push(box);
31809                         box = [];
31810                     }
31811                     
31812                     boxes.push([item]);
31813                     
31814                     break;
31815                     
31816                 case 'xs' :
31817                 case 'sm' :
31818                 case 'tall' :
31819                     
31820                     box.push(item);
31821                     
31822                     break;
31823                 default :
31824                     break;
31825                     
31826             }
31827             
31828         }, this);
31829         
31830         if(box.length){
31831             boxes.push(box);
31832             box = [];
31833         }
31834         
31835         var filterPattern = function(box, length)
31836         {
31837             if(!box.length){
31838                 return;
31839             }
31840             
31841             var match = false;
31842             
31843             var pattern = box.slice(0, length);
31844             
31845             var format = [];
31846             
31847             Roo.each(pattern, function(i){
31848                 format.push(i.size);
31849             }, this);
31850             
31851             Roo.each(standard, function(s){
31852                 
31853                 if(String(s) != String(format)){
31854                     return;
31855                 }
31856                 
31857                 match = true;
31858                 return false;
31859                 
31860             }, this);
31861             
31862             if(!match && length == 1){
31863                 return;
31864             }
31865             
31866             if(!match){
31867                 filterPattern(box, length - 1);
31868                 return;
31869             }
31870                 
31871             queue.push(pattern);
31872
31873             box = box.slice(length, box.length);
31874
31875             filterPattern(box, 4);
31876
31877             return;
31878             
31879         }
31880         
31881         Roo.each(boxes, function(box, k){
31882             
31883             if(!box.length){
31884                 return;
31885             }
31886             
31887             if(box.length == 1){
31888                 queue.push(box);
31889                 return;
31890             }
31891             
31892             filterPattern(box, 4);
31893             
31894         }, this);
31895         
31896         this._processVerticalLayoutQueue( queue, isInstant );
31897         
31898     },
31899     
31900 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31901 //    {
31902 //        if ( !items || !items.length ) {
31903 //            return;
31904 //        }
31905 //
31906 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31907 //        
31908 //    },
31909     
31910     _horizontalLayoutItems : function ( items , isInstant)
31911     {
31912         if ( !items || !items.length || items.length < 3) {
31913             return;
31914         }
31915         
31916         items.reverse();
31917         
31918         var eItems = items.slice(0, 3);
31919         
31920         items = items.slice(3, items.length);
31921         
31922         var standard = [
31923             ['xs', 'xs', 'xs', 'wide'],
31924             ['xs', 'xs', 'wide'],
31925             ['xs', 'xs', 'sm'],
31926             ['xs', 'xs', 'xs'],
31927             ['xs', 'wide'],
31928             ['xs', 'sm'],
31929             ['xs', 'xs'],
31930             ['xs'],
31931             
31932             ['sm', 'xs', 'xs'],
31933             ['sm', 'xs'],
31934             ['sm'],
31935             
31936             ['wide', 'xs', 'xs', 'xs'],
31937             ['wide', 'xs', 'xs'],
31938             ['wide', 'xs'],
31939             ['wide'],
31940             
31941             ['wide-thin']
31942         ];
31943         
31944         var queue = [];
31945         
31946         var boxes = [];
31947         
31948         var box = [];
31949         
31950         Roo.each(items, function(item, k){
31951             
31952             switch (item.size) {
31953                 case 'md' :
31954                 case 'md-left' :
31955                 case 'md-right' :
31956                 case 'tall' :
31957                     
31958                     if(box.length){
31959                         boxes.push(box);
31960                         box = [];
31961                     }
31962                     
31963                     boxes.push([item]);
31964                     
31965                     break;
31966                     
31967                 case 'xs' :
31968                 case 'sm' :
31969                 case 'wide' :
31970                 case 'wide-thin' :
31971                     
31972                     box.push(item);
31973                     
31974                     break;
31975                 default :
31976                     break;
31977                     
31978             }
31979             
31980         }, this);
31981         
31982         if(box.length){
31983             boxes.push(box);
31984             box = [];
31985         }
31986         
31987         var filterPattern = function(box, length)
31988         {
31989             if(!box.length){
31990                 return;
31991             }
31992             
31993             var match = false;
31994             
31995             var pattern = box.slice(0, length);
31996             
31997             var format = [];
31998             
31999             Roo.each(pattern, function(i){
32000                 format.push(i.size);
32001             }, this);
32002             
32003             Roo.each(standard, function(s){
32004                 
32005                 if(String(s) != String(format)){
32006                     return;
32007                 }
32008                 
32009                 match = true;
32010                 return false;
32011                 
32012             }, this);
32013             
32014             if(!match && length == 1){
32015                 return;
32016             }
32017             
32018             if(!match){
32019                 filterPattern(box, length - 1);
32020                 return;
32021             }
32022                 
32023             queue.push(pattern);
32024
32025             box = box.slice(length, box.length);
32026
32027             filterPattern(box, 4);
32028
32029             return;
32030             
32031         }
32032         
32033         Roo.each(boxes, function(box, k){
32034             
32035             if(!box.length){
32036                 return;
32037             }
32038             
32039             if(box.length == 1){
32040                 queue.push(box);
32041                 return;
32042             }
32043             
32044             filterPattern(box, 4);
32045             
32046         }, this);
32047         
32048         
32049         var prune = [];
32050         
32051         var pos = this.el.getBox(true);
32052         
32053         var minX = pos.x;
32054         
32055         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32056         
32057         var hit_end = false;
32058         
32059         Roo.each(queue, function(box){
32060             
32061             if(hit_end){
32062                 
32063                 Roo.each(box, function(b){
32064                 
32065                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32066                     b.el.hide();
32067
32068                 }, this);
32069
32070                 return;
32071             }
32072             
32073             var mx = 0;
32074             
32075             Roo.each(box, function(b){
32076                 
32077                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32078                 b.el.show();
32079
32080                 mx = Math.max(mx, b.x);
32081                 
32082             }, this);
32083             
32084             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32085             
32086             if(maxX < minX){
32087                 
32088                 Roo.each(box, function(b){
32089                 
32090                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32091                     b.el.hide();
32092                     
32093                 }, this);
32094                 
32095                 hit_end = true;
32096                 
32097                 return;
32098             }
32099             
32100             prune.push(box);
32101             
32102         }, this);
32103         
32104         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32105     },
32106     
32107     /** Sets position of item in DOM
32108     * @param {Element} item
32109     * @param {Number} x - horizontal position
32110     * @param {Number} y - vertical position
32111     * @param {Boolean} isInstant - disables transitions
32112     */
32113     _processVerticalLayoutQueue : function( queue, isInstant )
32114     {
32115         var pos = this.el.getBox(true);
32116         var x = pos.x;
32117         var y = pos.y;
32118         var maxY = [];
32119         
32120         for (var i = 0; i < this.cols; i++){
32121             maxY[i] = pos.y;
32122         }
32123         
32124         Roo.each(queue, function(box, k){
32125             
32126             var col = k % this.cols;
32127             
32128             Roo.each(box, function(b,kk){
32129                 
32130                 b.el.position('absolute');
32131                 
32132                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32133                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32134                 
32135                 if(b.size == 'md-left' || b.size == 'md-right'){
32136                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32137                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32138                 }
32139                 
32140                 b.el.setWidth(width);
32141                 b.el.setHeight(height);
32142                 // iframe?
32143                 b.el.select('iframe',true).setSize(width,height);
32144                 
32145             }, this);
32146             
32147             for (var i = 0; i < this.cols; i++){
32148                 
32149                 if(maxY[i] < maxY[col]){
32150                     col = i;
32151                     continue;
32152                 }
32153                 
32154                 col = Math.min(col, i);
32155                 
32156             }
32157             
32158             x = pos.x + col * (this.colWidth + this.padWidth);
32159             
32160             y = maxY[col];
32161             
32162             var positions = [];
32163             
32164             switch (box.length){
32165                 case 1 :
32166                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32167                     break;
32168                 case 2 :
32169                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32170                     break;
32171                 case 3 :
32172                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32173                     break;
32174                 case 4 :
32175                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32176                     break;
32177                 default :
32178                     break;
32179             }
32180             
32181             Roo.each(box, function(b,kk){
32182                 
32183                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32184                 
32185                 var sz = b.el.getSize();
32186                 
32187                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32188                 
32189             }, this);
32190             
32191         }, this);
32192         
32193         var mY = 0;
32194         
32195         for (var i = 0; i < this.cols; i++){
32196             mY = Math.max(mY, maxY[i]);
32197         }
32198         
32199         this.el.setHeight(mY - pos.y);
32200         
32201     },
32202     
32203 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32204 //    {
32205 //        var pos = this.el.getBox(true);
32206 //        var x = pos.x;
32207 //        var y = pos.y;
32208 //        var maxX = pos.right;
32209 //        
32210 //        var maxHeight = 0;
32211 //        
32212 //        Roo.each(items, function(item, k){
32213 //            
32214 //            var c = k % 2;
32215 //            
32216 //            item.el.position('absolute');
32217 //                
32218 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32219 //
32220 //            item.el.setWidth(width);
32221 //
32222 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32223 //
32224 //            item.el.setHeight(height);
32225 //            
32226 //            if(c == 0){
32227 //                item.el.setXY([x, y], isInstant ? false : true);
32228 //            } else {
32229 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32230 //            }
32231 //            
32232 //            y = y + height + this.alternativePadWidth;
32233 //            
32234 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32235 //            
32236 //        }, this);
32237 //        
32238 //        this.el.setHeight(maxHeight);
32239 //        
32240 //    },
32241     
32242     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32243     {
32244         var pos = this.el.getBox(true);
32245         
32246         var minX = pos.x;
32247         var minY = pos.y;
32248         
32249         var maxX = pos.right;
32250         
32251         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32252         
32253         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32254         
32255         Roo.each(queue, function(box, k){
32256             
32257             Roo.each(box, function(b, kk){
32258                 
32259                 b.el.position('absolute');
32260                 
32261                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32262                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32263                 
32264                 if(b.size == 'md-left' || b.size == 'md-right'){
32265                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32266                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32267                 }
32268                 
32269                 b.el.setWidth(width);
32270                 b.el.setHeight(height);
32271                 
32272             }, this);
32273             
32274             if(!box.length){
32275                 return;
32276             }
32277             
32278             var positions = [];
32279             
32280             switch (box.length){
32281                 case 1 :
32282                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32283                     break;
32284                 case 2 :
32285                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32286                     break;
32287                 case 3 :
32288                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32289                     break;
32290                 case 4 :
32291                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32292                     break;
32293                 default :
32294                     break;
32295             }
32296             
32297             Roo.each(box, function(b,kk){
32298                 
32299                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32300                 
32301                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32302                 
32303             }, this);
32304             
32305         }, this);
32306         
32307     },
32308     
32309     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32310     {
32311         Roo.each(eItems, function(b,k){
32312             
32313             b.size = (k == 0) ? 'sm' : 'xs';
32314             b.x = (k == 0) ? 2 : 1;
32315             b.y = (k == 0) ? 2 : 1;
32316             
32317             b.el.position('absolute');
32318             
32319             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32320                 
32321             b.el.setWidth(width);
32322             
32323             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32324             
32325             b.el.setHeight(height);
32326             
32327         }, this);
32328
32329         var positions = [];
32330         
32331         positions.push({
32332             x : maxX - this.unitWidth * 2 - this.gutter,
32333             y : minY
32334         });
32335         
32336         positions.push({
32337             x : maxX - this.unitWidth,
32338             y : minY + (this.unitWidth + this.gutter) * 2
32339         });
32340         
32341         positions.push({
32342             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32343             y : minY
32344         });
32345         
32346         Roo.each(eItems, function(b,k){
32347             
32348             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32349
32350         }, this);
32351         
32352     },
32353     
32354     getVerticalOneBoxColPositions : function(x, y, box)
32355     {
32356         var pos = [];
32357         
32358         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32359         
32360         if(box[0].size == 'md-left'){
32361             rand = 0;
32362         }
32363         
32364         if(box[0].size == 'md-right'){
32365             rand = 1;
32366         }
32367         
32368         pos.push({
32369             x : x + (this.unitWidth + this.gutter) * rand,
32370             y : y
32371         });
32372         
32373         return pos;
32374     },
32375     
32376     getVerticalTwoBoxColPositions : function(x, y, box)
32377     {
32378         var pos = [];
32379         
32380         if(box[0].size == 'xs'){
32381             
32382             pos.push({
32383                 x : x,
32384                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32385             });
32386
32387             pos.push({
32388                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32389                 y : y
32390             });
32391             
32392             return pos;
32393             
32394         }
32395         
32396         pos.push({
32397             x : x,
32398             y : y
32399         });
32400
32401         pos.push({
32402             x : x + (this.unitWidth + this.gutter) * 2,
32403             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32404         });
32405         
32406         return pos;
32407         
32408     },
32409     
32410     getVerticalThreeBoxColPositions : function(x, y, box)
32411     {
32412         var pos = [];
32413         
32414         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32415             
32416             pos.push({
32417                 x : x,
32418                 y : y
32419             });
32420
32421             pos.push({
32422                 x : x + (this.unitWidth + this.gutter) * 1,
32423                 y : y
32424             });
32425             
32426             pos.push({
32427                 x : x + (this.unitWidth + this.gutter) * 2,
32428                 y : y
32429             });
32430             
32431             return pos;
32432             
32433         }
32434         
32435         if(box[0].size == 'xs' && box[1].size == 'xs'){
32436             
32437             pos.push({
32438                 x : x,
32439                 y : y
32440             });
32441
32442             pos.push({
32443                 x : x,
32444                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32445             });
32446             
32447             pos.push({
32448                 x : x + (this.unitWidth + this.gutter) * 1,
32449                 y : y
32450             });
32451             
32452             return pos;
32453             
32454         }
32455         
32456         pos.push({
32457             x : x,
32458             y : y
32459         });
32460
32461         pos.push({
32462             x : x + (this.unitWidth + this.gutter) * 2,
32463             y : y
32464         });
32465
32466         pos.push({
32467             x : x + (this.unitWidth + this.gutter) * 2,
32468             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32469         });
32470             
32471         return pos;
32472         
32473     },
32474     
32475     getVerticalFourBoxColPositions : function(x, y, box)
32476     {
32477         var pos = [];
32478         
32479         if(box[0].size == 'xs'){
32480             
32481             pos.push({
32482                 x : x,
32483                 y : y
32484             });
32485
32486             pos.push({
32487                 x : x,
32488                 y : y + (this.unitHeight + this.gutter) * 1
32489             });
32490             
32491             pos.push({
32492                 x : x,
32493                 y : y + (this.unitHeight + this.gutter) * 2
32494             });
32495             
32496             pos.push({
32497                 x : x + (this.unitWidth + this.gutter) * 1,
32498                 y : y
32499             });
32500             
32501             return pos;
32502             
32503         }
32504         
32505         pos.push({
32506             x : x,
32507             y : y
32508         });
32509
32510         pos.push({
32511             x : x + (this.unitWidth + this.gutter) * 2,
32512             y : y
32513         });
32514
32515         pos.push({
32516             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32517             y : y + (this.unitHeight + this.gutter) * 1
32518         });
32519
32520         pos.push({
32521             x : x + (this.unitWidth + this.gutter) * 2,
32522             y : y + (this.unitWidth + this.gutter) * 2
32523         });
32524
32525         return pos;
32526         
32527     },
32528     
32529     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32530     {
32531         var pos = [];
32532         
32533         if(box[0].size == 'md-left'){
32534             pos.push({
32535                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32536                 y : minY
32537             });
32538             
32539             return pos;
32540         }
32541         
32542         if(box[0].size == 'md-right'){
32543             pos.push({
32544                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32545                 y : minY + (this.unitWidth + this.gutter) * 1
32546             });
32547             
32548             return pos;
32549         }
32550         
32551         var rand = Math.floor(Math.random() * (4 - box[0].y));
32552         
32553         pos.push({
32554             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32555             y : minY + (this.unitWidth + this.gutter) * rand
32556         });
32557         
32558         return pos;
32559         
32560     },
32561     
32562     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32563     {
32564         var pos = [];
32565         
32566         if(box[0].size == 'xs'){
32567             
32568             pos.push({
32569                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32570                 y : minY
32571             });
32572
32573             pos.push({
32574                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32575                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32576             });
32577             
32578             return pos;
32579             
32580         }
32581         
32582         pos.push({
32583             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32584             y : minY
32585         });
32586
32587         pos.push({
32588             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32589             y : minY + (this.unitWidth + this.gutter) * 2
32590         });
32591         
32592         return pos;
32593         
32594     },
32595     
32596     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32597     {
32598         var pos = [];
32599         
32600         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32601             
32602             pos.push({
32603                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32604                 y : minY
32605             });
32606
32607             pos.push({
32608                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32609                 y : minY + (this.unitWidth + this.gutter) * 1
32610             });
32611             
32612             pos.push({
32613                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32614                 y : minY + (this.unitWidth + this.gutter) * 2
32615             });
32616             
32617             return pos;
32618             
32619         }
32620         
32621         if(box[0].size == 'xs' && box[1].size == 'xs'){
32622             
32623             pos.push({
32624                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32625                 y : minY
32626             });
32627
32628             pos.push({
32629                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32630                 y : minY
32631             });
32632             
32633             pos.push({
32634                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32635                 y : minY + (this.unitWidth + this.gutter) * 1
32636             });
32637             
32638             return pos;
32639             
32640         }
32641         
32642         pos.push({
32643             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32644             y : minY
32645         });
32646
32647         pos.push({
32648             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32649             y : minY + (this.unitWidth + this.gutter) * 2
32650         });
32651
32652         pos.push({
32653             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32654             y : minY + (this.unitWidth + this.gutter) * 2
32655         });
32656             
32657         return pos;
32658         
32659     },
32660     
32661     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32662     {
32663         var pos = [];
32664         
32665         if(box[0].size == 'xs'){
32666             
32667             pos.push({
32668                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32669                 y : minY
32670             });
32671
32672             pos.push({
32673                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32674                 y : minY
32675             });
32676             
32677             pos.push({
32678                 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),
32679                 y : minY
32680             });
32681             
32682             pos.push({
32683                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32684                 y : minY + (this.unitWidth + this.gutter) * 1
32685             });
32686             
32687             return pos;
32688             
32689         }
32690         
32691         pos.push({
32692             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32693             y : minY
32694         });
32695         
32696         pos.push({
32697             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32698             y : minY + (this.unitWidth + this.gutter) * 2
32699         });
32700         
32701         pos.push({
32702             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32703             y : minY + (this.unitWidth + this.gutter) * 2
32704         });
32705         
32706         pos.push({
32707             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),
32708             y : minY + (this.unitWidth + this.gutter) * 2
32709         });
32710
32711         return pos;
32712         
32713     },
32714     
32715     /**
32716     * remove a Masonry Brick
32717     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32718     */
32719     removeBrick : function(brick_id)
32720     {
32721         if (!brick_id) {
32722             return;
32723         }
32724         
32725         for (var i = 0; i<this.bricks.length; i++) {
32726             if (this.bricks[i].id == brick_id) {
32727                 this.bricks.splice(i,1);
32728                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32729                 this.initial();
32730             }
32731         }
32732     },
32733     
32734     /**
32735     * adds a Masonry Brick
32736     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32737     */
32738     addBrick : function(cfg)
32739     {
32740         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32741         //this.register(cn);
32742         cn.parentId = this.id;
32743         cn.render(this.el);
32744         return cn;
32745     },
32746     
32747     /**
32748     * register a Masonry Brick
32749     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32750     */
32751     
32752     register : function(brick)
32753     {
32754         this.bricks.push(brick);
32755         brick.masonryId = this.id;
32756     },
32757     
32758     /**
32759     * clear all the Masonry Brick
32760     */
32761     clearAll : function()
32762     {
32763         this.bricks = [];
32764         //this.getChildContainer().dom.innerHTML = "";
32765         this.el.dom.innerHTML = '';
32766     },
32767     
32768     getSelected : function()
32769     {
32770         if (!this.selectedBrick) {
32771             return false;
32772         }
32773         
32774         return this.selectedBrick;
32775     }
32776 });
32777
32778 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32779     
32780     groups: {},
32781      /**
32782     * register a Masonry Layout
32783     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32784     */
32785     
32786     register : function(layout)
32787     {
32788         this.groups[layout.id] = layout;
32789     },
32790     /**
32791     * fetch a  Masonry Layout based on the masonry layout ID
32792     * @param {string} the masonry layout to add
32793     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32794     */
32795     
32796     get: function(layout_id) {
32797         if (typeof(this.groups[layout_id]) == 'undefined') {
32798             return false;
32799         }
32800         return this.groups[layout_id] ;
32801     }
32802     
32803     
32804     
32805 });
32806
32807  
32808
32809  /**
32810  *
32811  * This is based on 
32812  * http://masonry.desandro.com
32813  *
32814  * The idea is to render all the bricks based on vertical width...
32815  *
32816  * The original code extends 'outlayer' - we might need to use that....
32817  * 
32818  */
32819
32820
32821 /**
32822  * @class Roo.bootstrap.LayoutMasonryAuto
32823  * @extends Roo.bootstrap.Component
32824  * Bootstrap Layout Masonry class
32825  * 
32826  * @constructor
32827  * Create a new Element
32828  * @param {Object} config The config object
32829  */
32830
32831 Roo.bootstrap.LayoutMasonryAuto = function(config){
32832     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32833 };
32834
32835 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32836     
32837       /**
32838      * @cfg {Boolean} isFitWidth  - resize the width..
32839      */   
32840     isFitWidth : false,  // options..
32841     /**
32842      * @cfg {Boolean} isOriginLeft = left align?
32843      */   
32844     isOriginLeft : true,
32845     /**
32846      * @cfg {Boolean} isOriginTop = top align?
32847      */   
32848     isOriginTop : false,
32849     /**
32850      * @cfg {Boolean} isLayoutInstant = no animation?
32851      */   
32852     isLayoutInstant : false, // needed?
32853     /**
32854      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32855      */   
32856     isResizingContainer : true,
32857     /**
32858      * @cfg {Number} columnWidth  width of the columns 
32859      */   
32860     
32861     columnWidth : 0,
32862     
32863     /**
32864      * @cfg {Number} maxCols maximum number of columns
32865      */   
32866     
32867     maxCols: 0,
32868     /**
32869      * @cfg {Number} padHeight padding below box..
32870      */   
32871     
32872     padHeight : 10, 
32873     
32874     /**
32875      * @cfg {Boolean} isAutoInitial defalut true
32876      */   
32877     
32878     isAutoInitial : true, 
32879     
32880     // private?
32881     gutter : 0,
32882     
32883     containerWidth: 0,
32884     initialColumnWidth : 0,
32885     currentSize : null,
32886     
32887     colYs : null, // array.
32888     maxY : 0,
32889     padWidth: 10,
32890     
32891     
32892     tag: 'div',
32893     cls: '',
32894     bricks: null, //CompositeElement
32895     cols : 0, // array?
32896     // element : null, // wrapped now this.el
32897     _isLayoutInited : null, 
32898     
32899     
32900     getAutoCreate : function(){
32901         
32902         var cfg = {
32903             tag: this.tag,
32904             cls: 'blog-masonary-wrapper ' + this.cls,
32905             cn : {
32906                 cls : 'mas-boxes masonary'
32907             }
32908         };
32909         
32910         return cfg;
32911     },
32912     
32913     getChildContainer: function( )
32914     {
32915         if (this.boxesEl) {
32916             return this.boxesEl;
32917         }
32918         
32919         this.boxesEl = this.el.select('.mas-boxes').first();
32920         
32921         return this.boxesEl;
32922     },
32923     
32924     
32925     initEvents : function()
32926     {
32927         var _this = this;
32928         
32929         if(this.isAutoInitial){
32930             Roo.log('hook children rendered');
32931             this.on('childrenrendered', function() {
32932                 Roo.log('children rendered');
32933                 _this.initial();
32934             } ,this);
32935         }
32936         
32937     },
32938     
32939     initial : function()
32940     {
32941         this.reloadItems();
32942
32943         this.currentSize = this.el.getBox(true);
32944
32945         /// was window resize... - let's see if this works..
32946         Roo.EventManager.onWindowResize(this.resize, this); 
32947
32948         if(!this.isAutoInitial){
32949             this.layout();
32950             return;
32951         }
32952         
32953         this.layout.defer(500,this);
32954     },
32955     
32956     reloadItems: function()
32957     {
32958         this.bricks = this.el.select('.masonry-brick', true);
32959         
32960         this.bricks.each(function(b) {
32961             //Roo.log(b.getSize());
32962             if (!b.attr('originalwidth')) {
32963                 b.attr('originalwidth',  b.getSize().width);
32964             }
32965             
32966         });
32967         
32968         Roo.log(this.bricks.elements.length);
32969     },
32970     
32971     resize : function()
32972     {
32973         Roo.log('resize');
32974         var cs = this.el.getBox(true);
32975         
32976         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32977             Roo.log("no change in with or X");
32978             return;
32979         }
32980         this.currentSize = cs;
32981         this.layout();
32982     },
32983     
32984     layout : function()
32985     {
32986          Roo.log('layout');
32987         this._resetLayout();
32988         //this._manageStamps();
32989       
32990         // don't animate first layout
32991         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32992         this.layoutItems( isInstant );
32993       
32994         // flag for initalized
32995         this._isLayoutInited = true;
32996     },
32997     
32998     layoutItems : function( isInstant )
32999     {
33000         //var items = this._getItemsForLayout( this.items );
33001         // original code supports filtering layout items.. we just ignore it..
33002         
33003         this._layoutItems( this.bricks , isInstant );
33004       
33005         this._postLayout();
33006     },
33007     _layoutItems : function ( items , isInstant)
33008     {
33009        //this.fireEvent( 'layout', this, items );
33010     
33011
33012         if ( !items || !items.elements.length ) {
33013           // no items, emit event with empty array
33014             return;
33015         }
33016
33017         var queue = [];
33018         items.each(function(item) {
33019             Roo.log("layout item");
33020             Roo.log(item);
33021             // get x/y object from method
33022             var position = this._getItemLayoutPosition( item );
33023             // enqueue
33024             position.item = item;
33025             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33026             queue.push( position );
33027         }, this);
33028       
33029         this._processLayoutQueue( queue );
33030     },
33031     /** Sets position of item in DOM
33032     * @param {Element} item
33033     * @param {Number} x - horizontal position
33034     * @param {Number} y - vertical position
33035     * @param {Boolean} isInstant - disables transitions
33036     */
33037     _processLayoutQueue : function( queue )
33038     {
33039         for ( var i=0, len = queue.length; i < len; i++ ) {
33040             var obj = queue[i];
33041             obj.item.position('absolute');
33042             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33043         }
33044     },
33045       
33046     
33047     /**
33048     * Any logic you want to do after each layout,
33049     * i.e. size the container
33050     */
33051     _postLayout : function()
33052     {
33053         this.resizeContainer();
33054     },
33055     
33056     resizeContainer : function()
33057     {
33058         if ( !this.isResizingContainer ) {
33059             return;
33060         }
33061         var size = this._getContainerSize();
33062         if ( size ) {
33063             this.el.setSize(size.width,size.height);
33064             this.boxesEl.setSize(size.width,size.height);
33065         }
33066     },
33067     
33068     
33069     
33070     _resetLayout : function()
33071     {
33072         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33073         this.colWidth = this.el.getWidth();
33074         //this.gutter = this.el.getWidth(); 
33075         
33076         this.measureColumns();
33077
33078         // reset column Y
33079         var i = this.cols;
33080         this.colYs = [];
33081         while (i--) {
33082             this.colYs.push( 0 );
33083         }
33084     
33085         this.maxY = 0;
33086     },
33087
33088     measureColumns : function()
33089     {
33090         this.getContainerWidth();
33091       // if columnWidth is 0, default to outerWidth of first item
33092         if ( !this.columnWidth ) {
33093             var firstItem = this.bricks.first();
33094             Roo.log(firstItem);
33095             this.columnWidth  = this.containerWidth;
33096             if (firstItem && firstItem.attr('originalwidth') ) {
33097                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33098             }
33099             // columnWidth fall back to item of first element
33100             Roo.log("set column width?");
33101                         this.initialColumnWidth = this.columnWidth  ;
33102
33103             // if first elem has no width, default to size of container
33104             
33105         }
33106         
33107         
33108         if (this.initialColumnWidth) {
33109             this.columnWidth = this.initialColumnWidth;
33110         }
33111         
33112         
33113             
33114         // column width is fixed at the top - however if container width get's smaller we should
33115         // reduce it...
33116         
33117         // this bit calcs how man columns..
33118             
33119         var columnWidth = this.columnWidth += this.gutter;
33120       
33121         // calculate columns
33122         var containerWidth = this.containerWidth + this.gutter;
33123         
33124         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33125         // fix rounding errors, typically with gutters
33126         var excess = columnWidth - containerWidth % columnWidth;
33127         
33128         
33129         // if overshoot is less than a pixel, round up, otherwise floor it
33130         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33131         cols = Math[ mathMethod ]( cols );
33132         this.cols = Math.max( cols, 1 );
33133         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33134         
33135          // padding positioning..
33136         var totalColWidth = this.cols * this.columnWidth;
33137         var padavail = this.containerWidth - totalColWidth;
33138         // so for 2 columns - we need 3 'pads'
33139         
33140         var padNeeded = (1+this.cols) * this.padWidth;
33141         
33142         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33143         
33144         this.columnWidth += padExtra
33145         //this.padWidth = Math.floor(padavail /  ( this.cols));
33146         
33147         // adjust colum width so that padding is fixed??
33148         
33149         // we have 3 columns ... total = width * 3
33150         // we have X left over... that should be used by 
33151         
33152         //if (this.expandC) {
33153             
33154         //}
33155         
33156         
33157         
33158     },
33159     
33160     getContainerWidth : function()
33161     {
33162        /* // container is parent if fit width
33163         var container = this.isFitWidth ? this.element.parentNode : this.element;
33164         // check that this.size and size are there
33165         // IE8 triggers resize on body size change, so they might not be
33166         
33167         var size = getSize( container );  //FIXME
33168         this.containerWidth = size && size.innerWidth; //FIXME
33169         */
33170          
33171         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33172         
33173     },
33174     
33175     _getItemLayoutPosition : function( item )  // what is item?
33176     {
33177         // we resize the item to our columnWidth..
33178       
33179         item.setWidth(this.columnWidth);
33180         item.autoBoxAdjust  = false;
33181         
33182         var sz = item.getSize();
33183  
33184         // how many columns does this brick span
33185         var remainder = this.containerWidth % this.columnWidth;
33186         
33187         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33188         // round if off by 1 pixel, otherwise use ceil
33189         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33190         colSpan = Math.min( colSpan, this.cols );
33191         
33192         // normally this should be '1' as we dont' currently allow multi width columns..
33193         
33194         var colGroup = this._getColGroup( colSpan );
33195         // get the minimum Y value from the columns
33196         var minimumY = Math.min.apply( Math, colGroup );
33197         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33198         
33199         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33200          
33201         // position the brick
33202         var position = {
33203             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33204             y: this.currentSize.y + minimumY + this.padHeight
33205         };
33206         
33207         Roo.log(position);
33208         // apply setHeight to necessary columns
33209         var setHeight = minimumY + sz.height + this.padHeight;
33210         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33211         
33212         var setSpan = this.cols + 1 - colGroup.length;
33213         for ( var i = 0; i < setSpan; i++ ) {
33214           this.colYs[ shortColIndex + i ] = setHeight ;
33215         }
33216       
33217         return position;
33218     },
33219     
33220     /**
33221      * @param {Number} colSpan - number of columns the element spans
33222      * @returns {Array} colGroup
33223      */
33224     _getColGroup : function( colSpan )
33225     {
33226         if ( colSpan < 2 ) {
33227           // if brick spans only one column, use all the column Ys
33228           return this.colYs;
33229         }
33230       
33231         var colGroup = [];
33232         // how many different places could this brick fit horizontally
33233         var groupCount = this.cols + 1 - colSpan;
33234         // for each group potential horizontal position
33235         for ( var i = 0; i < groupCount; i++ ) {
33236           // make an array of colY values for that one group
33237           var groupColYs = this.colYs.slice( i, i + colSpan );
33238           // and get the max value of the array
33239           colGroup[i] = Math.max.apply( Math, groupColYs );
33240         }
33241         return colGroup;
33242     },
33243     /*
33244     _manageStamp : function( stamp )
33245     {
33246         var stampSize =  stamp.getSize();
33247         var offset = stamp.getBox();
33248         // get the columns that this stamp affects
33249         var firstX = this.isOriginLeft ? offset.x : offset.right;
33250         var lastX = firstX + stampSize.width;
33251         var firstCol = Math.floor( firstX / this.columnWidth );
33252         firstCol = Math.max( 0, firstCol );
33253         
33254         var lastCol = Math.floor( lastX / this.columnWidth );
33255         // lastCol should not go over if multiple of columnWidth #425
33256         lastCol -= lastX % this.columnWidth ? 0 : 1;
33257         lastCol = Math.min( this.cols - 1, lastCol );
33258         
33259         // set colYs to bottom of the stamp
33260         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33261             stampSize.height;
33262             
33263         for ( var i = firstCol; i <= lastCol; i++ ) {
33264           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33265         }
33266     },
33267     */
33268     
33269     _getContainerSize : function()
33270     {
33271         this.maxY = Math.max.apply( Math, this.colYs );
33272         var size = {
33273             height: this.maxY
33274         };
33275       
33276         if ( this.isFitWidth ) {
33277             size.width = this._getContainerFitWidth();
33278         }
33279       
33280         return size;
33281     },
33282     
33283     _getContainerFitWidth : function()
33284     {
33285         var unusedCols = 0;
33286         // count unused columns
33287         var i = this.cols;
33288         while ( --i ) {
33289           if ( this.colYs[i] !== 0 ) {
33290             break;
33291           }
33292           unusedCols++;
33293         }
33294         // fit container to columns that have been used
33295         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33296     },
33297     
33298     needsResizeLayout : function()
33299     {
33300         var previousWidth = this.containerWidth;
33301         this.getContainerWidth();
33302         return previousWidth !== this.containerWidth;
33303     }
33304  
33305 });
33306
33307  
33308
33309  /*
33310  * - LGPL
33311  *
33312  * element
33313  * 
33314  */
33315
33316 /**
33317  * @class Roo.bootstrap.MasonryBrick
33318  * @extends Roo.bootstrap.Component
33319  * Bootstrap MasonryBrick class
33320  * 
33321  * @constructor
33322  * Create a new MasonryBrick
33323  * @param {Object} config The config object
33324  */
33325
33326 Roo.bootstrap.MasonryBrick = function(config){
33327     
33328     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33329     
33330     Roo.bootstrap.MasonryBrick.register(this);
33331     
33332     this.addEvents({
33333         // raw events
33334         /**
33335          * @event click
33336          * When a MasonryBrick is clcik
33337          * @param {Roo.bootstrap.MasonryBrick} this
33338          * @param {Roo.EventObject} e
33339          */
33340         "click" : true
33341     });
33342 };
33343
33344 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33345     
33346     /**
33347      * @cfg {String} title
33348      */   
33349     title : '',
33350     /**
33351      * @cfg {String} html
33352      */   
33353     html : '',
33354     /**
33355      * @cfg {String} bgimage
33356      */   
33357     bgimage : '',
33358     /**
33359      * @cfg {String} videourl
33360      */   
33361     videourl : '',
33362     /**
33363      * @cfg {String} cls
33364      */   
33365     cls : '',
33366     /**
33367      * @cfg {String} href
33368      */   
33369     href : '',
33370     /**
33371      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33372      */   
33373     size : 'xs',
33374     
33375     /**
33376      * @cfg {String} placetitle (center|bottom)
33377      */   
33378     placetitle : '',
33379     
33380     /**
33381      * @cfg {Boolean} isFitContainer defalut true
33382      */   
33383     isFitContainer : true, 
33384     
33385     /**
33386      * @cfg {Boolean} preventDefault defalut false
33387      */   
33388     preventDefault : false, 
33389     
33390     /**
33391      * @cfg {Boolean} inverse defalut false
33392      */   
33393     maskInverse : false, 
33394     
33395     getAutoCreate : function()
33396     {
33397         if(!this.isFitContainer){
33398             return this.getSplitAutoCreate();
33399         }
33400         
33401         var cls = 'masonry-brick masonry-brick-full';
33402         
33403         if(this.href.length){
33404             cls += ' masonry-brick-link';
33405         }
33406         
33407         if(this.bgimage.length){
33408             cls += ' masonry-brick-image';
33409         }
33410         
33411         if(this.maskInverse){
33412             cls += ' mask-inverse';
33413         }
33414         
33415         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33416             cls += ' enable-mask';
33417         }
33418         
33419         if(this.size){
33420             cls += ' masonry-' + this.size + '-brick';
33421         }
33422         
33423         if(this.placetitle.length){
33424             
33425             switch (this.placetitle) {
33426                 case 'center' :
33427                     cls += ' masonry-center-title';
33428                     break;
33429                 case 'bottom' :
33430                     cls += ' masonry-bottom-title';
33431                     break;
33432                 default:
33433                     break;
33434             }
33435             
33436         } else {
33437             if(!this.html.length && !this.bgimage.length){
33438                 cls += ' masonry-center-title';
33439             }
33440
33441             if(!this.html.length && this.bgimage.length){
33442                 cls += ' masonry-bottom-title';
33443             }
33444         }
33445         
33446         if(this.cls){
33447             cls += ' ' + this.cls;
33448         }
33449         
33450         var cfg = {
33451             tag: (this.href.length) ? 'a' : 'div',
33452             cls: cls,
33453             cn: [
33454                 {
33455                     tag: 'div',
33456                     cls: 'masonry-brick-mask'
33457                 },
33458                 {
33459                     tag: 'div',
33460                     cls: 'masonry-brick-paragraph',
33461                     cn: []
33462                 }
33463             ]
33464         };
33465         
33466         if(this.href.length){
33467             cfg.href = this.href;
33468         }
33469         
33470         var cn = cfg.cn[1].cn;
33471         
33472         if(this.title.length){
33473             cn.push({
33474                 tag: 'h4',
33475                 cls: 'masonry-brick-title',
33476                 html: this.title
33477             });
33478         }
33479         
33480         if(this.html.length){
33481             cn.push({
33482                 tag: 'p',
33483                 cls: 'masonry-brick-text',
33484                 html: this.html
33485             });
33486         }
33487         
33488         if (!this.title.length && !this.html.length) {
33489             cfg.cn[1].cls += ' hide';
33490         }
33491         
33492         if(this.bgimage.length){
33493             cfg.cn.push({
33494                 tag: 'img',
33495                 cls: 'masonry-brick-image-view',
33496                 src: this.bgimage
33497             });
33498         }
33499         
33500         if(this.videourl.length){
33501             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33502             // youtube support only?
33503             cfg.cn.push({
33504                 tag: 'iframe',
33505                 cls: 'masonry-brick-image-view',
33506                 src: vurl,
33507                 frameborder : 0,
33508                 allowfullscreen : true
33509             });
33510         }
33511         
33512         return cfg;
33513         
33514     },
33515     
33516     getSplitAutoCreate : function()
33517     {
33518         var cls = 'masonry-brick masonry-brick-split';
33519         
33520         if(this.href.length){
33521             cls += ' masonry-brick-link';
33522         }
33523         
33524         if(this.bgimage.length){
33525             cls += ' masonry-brick-image';
33526         }
33527         
33528         if(this.size){
33529             cls += ' masonry-' + this.size + '-brick';
33530         }
33531         
33532         switch (this.placetitle) {
33533             case 'center' :
33534                 cls += ' masonry-center-title';
33535                 break;
33536             case 'bottom' :
33537                 cls += ' masonry-bottom-title';
33538                 break;
33539             default:
33540                 if(!this.bgimage.length){
33541                     cls += ' masonry-center-title';
33542                 }
33543
33544                 if(this.bgimage.length){
33545                     cls += ' masonry-bottom-title';
33546                 }
33547                 break;
33548         }
33549         
33550         if(this.cls){
33551             cls += ' ' + this.cls;
33552         }
33553         
33554         var cfg = {
33555             tag: (this.href.length) ? 'a' : 'div',
33556             cls: cls,
33557             cn: [
33558                 {
33559                     tag: 'div',
33560                     cls: 'masonry-brick-split-head',
33561                     cn: [
33562                         {
33563                             tag: 'div',
33564                             cls: 'masonry-brick-paragraph',
33565                             cn: []
33566                         }
33567                     ]
33568                 },
33569                 {
33570                     tag: 'div',
33571                     cls: 'masonry-brick-split-body',
33572                     cn: []
33573                 }
33574             ]
33575         };
33576         
33577         if(this.href.length){
33578             cfg.href = this.href;
33579         }
33580         
33581         if(this.title.length){
33582             cfg.cn[0].cn[0].cn.push({
33583                 tag: 'h4',
33584                 cls: 'masonry-brick-title',
33585                 html: this.title
33586             });
33587         }
33588         
33589         if(this.html.length){
33590             cfg.cn[1].cn.push({
33591                 tag: 'p',
33592                 cls: 'masonry-brick-text',
33593                 html: this.html
33594             });
33595         }
33596
33597         if(this.bgimage.length){
33598             cfg.cn[0].cn.push({
33599                 tag: 'img',
33600                 cls: 'masonry-brick-image-view',
33601                 src: this.bgimage
33602             });
33603         }
33604         
33605         if(this.videourl.length){
33606             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33607             // youtube support only?
33608             cfg.cn[0].cn.cn.push({
33609                 tag: 'iframe',
33610                 cls: 'masonry-brick-image-view',
33611                 src: vurl,
33612                 frameborder : 0,
33613                 allowfullscreen : true
33614             });
33615         }
33616         
33617         return cfg;
33618     },
33619     
33620     initEvents: function() 
33621     {
33622         switch (this.size) {
33623             case 'xs' :
33624                 this.x = 1;
33625                 this.y = 1;
33626                 break;
33627             case 'sm' :
33628                 this.x = 2;
33629                 this.y = 2;
33630                 break;
33631             case 'md' :
33632             case 'md-left' :
33633             case 'md-right' :
33634                 this.x = 3;
33635                 this.y = 3;
33636                 break;
33637             case 'tall' :
33638                 this.x = 2;
33639                 this.y = 3;
33640                 break;
33641             case 'wide' :
33642                 this.x = 3;
33643                 this.y = 2;
33644                 break;
33645             case 'wide-thin' :
33646                 this.x = 3;
33647                 this.y = 1;
33648                 break;
33649                         
33650             default :
33651                 break;
33652         }
33653         
33654         if(Roo.isTouch){
33655             this.el.on('touchstart', this.onTouchStart, this);
33656             this.el.on('touchmove', this.onTouchMove, this);
33657             this.el.on('touchend', this.onTouchEnd, this);
33658             this.el.on('contextmenu', this.onContextMenu, this);
33659         } else {
33660             this.el.on('mouseenter'  ,this.enter, this);
33661             this.el.on('mouseleave', this.leave, this);
33662             this.el.on('click', this.onClick, this);
33663         }
33664         
33665         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33666             this.parent().bricks.push(this);   
33667         }
33668         
33669     },
33670     
33671     onClick: function(e, el)
33672     {
33673         var time = this.endTimer - this.startTimer;
33674         // Roo.log(e.preventDefault());
33675         if(Roo.isTouch){
33676             if(time > 1000){
33677                 e.preventDefault();
33678                 return;
33679             }
33680         }
33681         
33682         if(!this.preventDefault){
33683             return;
33684         }
33685         
33686         e.preventDefault();
33687         
33688         if (this.activeClass != '') {
33689             this.selectBrick();
33690         }
33691         
33692         this.fireEvent('click', this, e);
33693     },
33694     
33695     enter: function(e, el)
33696     {
33697         e.preventDefault();
33698         
33699         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33700             return;
33701         }
33702         
33703         if(this.bgimage.length && this.html.length){
33704             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33705         }
33706     },
33707     
33708     leave: function(e, el)
33709     {
33710         e.preventDefault();
33711         
33712         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33713             return;
33714         }
33715         
33716         if(this.bgimage.length && this.html.length){
33717             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33718         }
33719     },
33720     
33721     onTouchStart: function(e, el)
33722     {
33723 //        e.preventDefault();
33724         
33725         this.touchmoved = false;
33726         
33727         if(!this.isFitContainer){
33728             return;
33729         }
33730         
33731         if(!this.bgimage.length || !this.html.length){
33732             return;
33733         }
33734         
33735         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33736         
33737         this.timer = new Date().getTime();
33738         
33739     },
33740     
33741     onTouchMove: function(e, el)
33742     {
33743         this.touchmoved = true;
33744     },
33745     
33746     onContextMenu : function(e,el)
33747     {
33748         e.preventDefault();
33749         e.stopPropagation();
33750         return false;
33751     },
33752     
33753     onTouchEnd: function(e, el)
33754     {
33755 //        e.preventDefault();
33756         
33757         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33758         
33759             this.leave(e,el);
33760             
33761             return;
33762         }
33763         
33764         if(!this.bgimage.length || !this.html.length){
33765             
33766             if(this.href.length){
33767                 window.location.href = this.href;
33768             }
33769             
33770             return;
33771         }
33772         
33773         if(!this.isFitContainer){
33774             return;
33775         }
33776         
33777         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33778         
33779         window.location.href = this.href;
33780     },
33781     
33782     //selection on single brick only
33783     selectBrick : function() {
33784         
33785         if (!this.parentId) {
33786             return;
33787         }
33788         
33789         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33790         var index = m.selectedBrick.indexOf(this.id);
33791         
33792         if ( index > -1) {
33793             m.selectedBrick.splice(index,1);
33794             this.el.removeClass(this.activeClass);
33795             return;
33796         }
33797         
33798         for(var i = 0; i < m.selectedBrick.length; i++) {
33799             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33800             b.el.removeClass(b.activeClass);
33801         }
33802         
33803         m.selectedBrick = [];
33804         
33805         m.selectedBrick.push(this.id);
33806         this.el.addClass(this.activeClass);
33807         return;
33808     },
33809     
33810     isSelected : function(){
33811         return this.el.hasClass(this.activeClass);
33812         
33813     }
33814 });
33815
33816 Roo.apply(Roo.bootstrap.MasonryBrick, {
33817     
33818     //groups: {},
33819     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33820      /**
33821     * register a Masonry Brick
33822     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33823     */
33824     
33825     register : function(brick)
33826     {
33827         //this.groups[brick.id] = brick;
33828         this.groups.add(brick.id, brick);
33829     },
33830     /**
33831     * fetch a  masonry brick based on the masonry brick ID
33832     * @param {string} the masonry brick to add
33833     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33834     */
33835     
33836     get: function(brick_id) 
33837     {
33838         // if (typeof(this.groups[brick_id]) == 'undefined') {
33839         //     return false;
33840         // }
33841         // return this.groups[brick_id] ;
33842         
33843         if(this.groups.key(brick_id)) {
33844             return this.groups.key(brick_id);
33845         }
33846         
33847         return false;
33848     }
33849     
33850     
33851     
33852 });
33853
33854  /*
33855  * - LGPL
33856  *
33857  * element
33858  * 
33859  */
33860
33861 /**
33862  * @class Roo.bootstrap.Brick
33863  * @extends Roo.bootstrap.Component
33864  * Bootstrap Brick class
33865  * 
33866  * @constructor
33867  * Create a new Brick
33868  * @param {Object} config The config object
33869  */
33870
33871 Roo.bootstrap.Brick = function(config){
33872     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33873     
33874     this.addEvents({
33875         // raw events
33876         /**
33877          * @event click
33878          * When a Brick is click
33879          * @param {Roo.bootstrap.Brick} this
33880          * @param {Roo.EventObject} e
33881          */
33882         "click" : true
33883     });
33884 };
33885
33886 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33887     
33888     /**
33889      * @cfg {String} title
33890      */   
33891     title : '',
33892     /**
33893      * @cfg {String} html
33894      */   
33895     html : '',
33896     /**
33897      * @cfg {String} bgimage
33898      */   
33899     bgimage : '',
33900     /**
33901      * @cfg {String} cls
33902      */   
33903     cls : '',
33904     /**
33905      * @cfg {String} href
33906      */   
33907     href : '',
33908     /**
33909      * @cfg {String} video
33910      */   
33911     video : '',
33912     /**
33913      * @cfg {Boolean} square
33914      */   
33915     square : true,
33916     
33917     getAutoCreate : function()
33918     {
33919         var cls = 'roo-brick';
33920         
33921         if(this.href.length){
33922             cls += ' roo-brick-link';
33923         }
33924         
33925         if(this.bgimage.length){
33926             cls += ' roo-brick-image';
33927         }
33928         
33929         if(!this.html.length && !this.bgimage.length){
33930             cls += ' roo-brick-center-title';
33931         }
33932         
33933         if(!this.html.length && this.bgimage.length){
33934             cls += ' roo-brick-bottom-title';
33935         }
33936         
33937         if(this.cls){
33938             cls += ' ' + this.cls;
33939         }
33940         
33941         var cfg = {
33942             tag: (this.href.length) ? 'a' : 'div',
33943             cls: cls,
33944             cn: [
33945                 {
33946                     tag: 'div',
33947                     cls: 'roo-brick-paragraph',
33948                     cn: []
33949                 }
33950             ]
33951         };
33952         
33953         if(this.href.length){
33954             cfg.href = this.href;
33955         }
33956         
33957         var cn = cfg.cn[0].cn;
33958         
33959         if(this.title.length){
33960             cn.push({
33961                 tag: 'h4',
33962                 cls: 'roo-brick-title',
33963                 html: this.title
33964             });
33965         }
33966         
33967         if(this.html.length){
33968             cn.push({
33969                 tag: 'p',
33970                 cls: 'roo-brick-text',
33971                 html: this.html
33972             });
33973         } else {
33974             cn.cls += ' hide';
33975         }
33976         
33977         if(this.bgimage.length){
33978             cfg.cn.push({
33979                 tag: 'img',
33980                 cls: 'roo-brick-image-view',
33981                 src: this.bgimage
33982             });
33983         }
33984         
33985         return cfg;
33986     },
33987     
33988     initEvents: function() 
33989     {
33990         if(this.title.length || this.html.length){
33991             this.el.on('mouseenter'  ,this.enter, this);
33992             this.el.on('mouseleave', this.leave, this);
33993         }
33994         
33995         Roo.EventManager.onWindowResize(this.resize, this); 
33996         
33997         if(this.bgimage.length){
33998             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33999             this.imageEl.on('load', this.onImageLoad, this);
34000             return;
34001         }
34002         
34003         this.resize();
34004     },
34005     
34006     onImageLoad : function()
34007     {
34008         this.resize();
34009     },
34010     
34011     resize : function()
34012     {
34013         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34014         
34015         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34016         
34017         if(this.bgimage.length){
34018             var image = this.el.select('.roo-brick-image-view', true).first();
34019             
34020             image.setWidth(paragraph.getWidth());
34021             
34022             if(this.square){
34023                 image.setHeight(paragraph.getWidth());
34024             }
34025             
34026             this.el.setHeight(image.getHeight());
34027             paragraph.setHeight(image.getHeight());
34028             
34029         }
34030         
34031     },
34032     
34033     enter: function(e, el)
34034     {
34035         e.preventDefault();
34036         
34037         if(this.bgimage.length){
34038             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34039             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34040         }
34041     },
34042     
34043     leave: function(e, el)
34044     {
34045         e.preventDefault();
34046         
34047         if(this.bgimage.length){
34048             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34049             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34050         }
34051     }
34052     
34053 });
34054
34055  
34056
34057  /*
34058  * - LGPL
34059  *
34060  * Number field 
34061  */
34062
34063 /**
34064  * @class Roo.bootstrap.NumberField
34065  * @extends Roo.bootstrap.Input
34066  * Bootstrap NumberField class
34067  * 
34068  * 
34069  * 
34070  * 
34071  * @constructor
34072  * Create a new NumberField
34073  * @param {Object} config The config object
34074  */
34075
34076 Roo.bootstrap.NumberField = function(config){
34077     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34078 };
34079
34080 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34081     
34082     /**
34083      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34084      */
34085     allowDecimals : true,
34086     /**
34087      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34088      */
34089     decimalSeparator : ".",
34090     /**
34091      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34092      */
34093     decimalPrecision : 2,
34094     /**
34095      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34096      */
34097     allowNegative : true,
34098     
34099     /**
34100      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34101      */
34102     allowZero: true,
34103     /**
34104      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34105      */
34106     minValue : Number.NEGATIVE_INFINITY,
34107     /**
34108      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34109      */
34110     maxValue : Number.MAX_VALUE,
34111     /**
34112      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34113      */
34114     minText : "The minimum value for this field is {0}",
34115     /**
34116      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34117      */
34118     maxText : "The maximum value for this field is {0}",
34119     /**
34120      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34121      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34122      */
34123     nanText : "{0} is not a valid number",
34124     /**
34125      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34126      */
34127     thousandsDelimiter : false,
34128     /**
34129      * @cfg {String} valueAlign alignment of value
34130      */
34131     valueAlign : "left",
34132
34133     getAutoCreate : function()
34134     {
34135         var hiddenInput = {
34136             tag: 'input',
34137             type: 'hidden',
34138             id: Roo.id(),
34139             cls: 'hidden-number-input'
34140         };
34141         
34142         if (this.name) {
34143             hiddenInput.name = this.name;
34144         }
34145         
34146         this.name = '';
34147         
34148         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34149         
34150         this.name = hiddenInput.name;
34151         
34152         if(cfg.cn.length > 0) {
34153             cfg.cn.push(hiddenInput);
34154         }
34155         
34156         return cfg;
34157     },
34158
34159     // private
34160     initEvents : function()
34161     {   
34162         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34163         
34164         var allowed = "0123456789";
34165         
34166         if(this.allowDecimals){
34167             allowed += this.decimalSeparator;
34168         }
34169         
34170         if(this.allowNegative){
34171             allowed += "-";
34172         }
34173         
34174         if(this.thousandsDelimiter) {
34175             allowed += ",";
34176         }
34177         
34178         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34179         
34180         var keyPress = function(e){
34181             
34182             var k = e.getKey();
34183             
34184             var c = e.getCharCode();
34185             
34186             if(
34187                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34188                     allowed.indexOf(String.fromCharCode(c)) === -1
34189             ){
34190                 e.stopEvent();
34191                 return;
34192             }
34193             
34194             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34195                 return;
34196             }
34197             
34198             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34199                 e.stopEvent();
34200             }
34201         };
34202         
34203         this.el.on("keypress", keyPress, this);
34204     },
34205     
34206     validateValue : function(value)
34207     {
34208         
34209         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34210             return false;
34211         }
34212         
34213         var num = this.parseValue(value);
34214         
34215         if(isNaN(num)){
34216             this.markInvalid(String.format(this.nanText, value));
34217             return false;
34218         }
34219         
34220         if(num < this.minValue){
34221             this.markInvalid(String.format(this.minText, this.minValue));
34222             return false;
34223         }
34224         
34225         if(num > this.maxValue){
34226             this.markInvalid(String.format(this.maxText, this.maxValue));
34227             return false;
34228         }
34229         
34230         return true;
34231     },
34232
34233     getValue : function()
34234     {
34235         var v = this.hiddenEl().getValue();
34236         
34237         return this.fixPrecision(this.parseValue(v));
34238     },
34239
34240     parseValue : function(value)
34241     {
34242         if(this.thousandsDelimiter) {
34243             value += "";
34244             r = new RegExp(",", "g");
34245             value = value.replace(r, "");
34246         }
34247         
34248         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34249         return isNaN(value) ? '' : value;
34250     },
34251
34252     fixPrecision : function(value)
34253     {
34254         if(this.thousandsDelimiter) {
34255             value += "";
34256             r = new RegExp(",", "g");
34257             value = value.replace(r, "");
34258         }
34259         
34260         var nan = isNaN(value);
34261         
34262         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34263             return nan ? '' : value;
34264         }
34265         return parseFloat(value).toFixed(this.decimalPrecision);
34266     },
34267
34268     setValue : function(v)
34269     {
34270         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34271         
34272         this.value = v;
34273         
34274         if(this.rendered){
34275             
34276             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34277             
34278             this.inputEl().dom.value = (v == '') ? '' :
34279                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34280             
34281             if(!this.allowZero && v === '0') {
34282                 this.hiddenEl().dom.value = '';
34283                 this.inputEl().dom.value = '';
34284             }
34285             
34286             this.validate();
34287         }
34288     },
34289
34290     decimalPrecisionFcn : function(v)
34291     {
34292         return Math.floor(v);
34293     },
34294
34295     beforeBlur : function()
34296     {
34297         var v = this.parseValue(this.getRawValue());
34298         
34299         if(v || v === 0 || v === ''){
34300             this.setValue(v);
34301         }
34302     },
34303     
34304     hiddenEl : function()
34305     {
34306         return this.el.select('input.hidden-number-input',true).first();
34307     }
34308     
34309 });
34310
34311  
34312
34313 /*
34314 * Licence: LGPL
34315 */
34316
34317 /**
34318  * @class Roo.bootstrap.DocumentSlider
34319  * @extends Roo.bootstrap.Component
34320  * Bootstrap DocumentSlider class
34321  * 
34322  * @constructor
34323  * Create a new DocumentViewer
34324  * @param {Object} config The config object
34325  */
34326
34327 Roo.bootstrap.DocumentSlider = function(config){
34328     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34329     
34330     this.files = [];
34331     
34332     this.addEvents({
34333         /**
34334          * @event initial
34335          * Fire after initEvent
34336          * @param {Roo.bootstrap.DocumentSlider} this
34337          */
34338         "initial" : true,
34339         /**
34340          * @event update
34341          * Fire after update
34342          * @param {Roo.bootstrap.DocumentSlider} this
34343          */
34344         "update" : true,
34345         /**
34346          * @event click
34347          * Fire after click
34348          * @param {Roo.bootstrap.DocumentSlider} this
34349          */
34350         "click" : true
34351     });
34352 };
34353
34354 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34355     
34356     files : false,
34357     
34358     indicator : 0,
34359     
34360     getAutoCreate : function()
34361     {
34362         var cfg = {
34363             tag : 'div',
34364             cls : 'roo-document-slider',
34365             cn : [
34366                 {
34367                     tag : 'div',
34368                     cls : 'roo-document-slider-header',
34369                     cn : [
34370                         {
34371                             tag : 'div',
34372                             cls : 'roo-document-slider-header-title'
34373                         }
34374                     ]
34375                 },
34376                 {
34377                     tag : 'div',
34378                     cls : 'roo-document-slider-body',
34379                     cn : [
34380                         {
34381                             tag : 'div',
34382                             cls : 'roo-document-slider-prev',
34383                             cn : [
34384                                 {
34385                                     tag : 'i',
34386                                     cls : 'fa fa-chevron-left'
34387                                 }
34388                             ]
34389                         },
34390                         {
34391                             tag : 'div',
34392                             cls : 'roo-document-slider-thumb',
34393                             cn : [
34394                                 {
34395                                     tag : 'img',
34396                                     cls : 'roo-document-slider-image'
34397                                 }
34398                             ]
34399                         },
34400                         {
34401                             tag : 'div',
34402                             cls : 'roo-document-slider-next',
34403                             cn : [
34404                                 {
34405                                     tag : 'i',
34406                                     cls : 'fa fa-chevron-right'
34407                                 }
34408                             ]
34409                         }
34410                     ]
34411                 }
34412             ]
34413         };
34414         
34415         return cfg;
34416     },
34417     
34418     initEvents : function()
34419     {
34420         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34421         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34422         
34423         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34424         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34425         
34426         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34427         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34428         
34429         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34430         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34431         
34432         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34433         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34434         
34435         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34436         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34437         
34438         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34439         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34440         
34441         this.thumbEl.on('click', this.onClick, this);
34442         
34443         this.prevIndicator.on('click', this.prev, this);
34444         
34445         this.nextIndicator.on('click', this.next, this);
34446         
34447     },
34448     
34449     initial : function()
34450     {
34451         if(this.files.length){
34452             this.indicator = 1;
34453             this.update()
34454         }
34455         
34456         this.fireEvent('initial', this);
34457     },
34458     
34459     update : function()
34460     {
34461         this.imageEl.attr('src', this.files[this.indicator - 1]);
34462         
34463         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34464         
34465         this.prevIndicator.show();
34466         
34467         if(this.indicator == 1){
34468             this.prevIndicator.hide();
34469         }
34470         
34471         this.nextIndicator.show();
34472         
34473         if(this.indicator == this.files.length){
34474             this.nextIndicator.hide();
34475         }
34476         
34477         this.thumbEl.scrollTo('top');
34478         
34479         this.fireEvent('update', this);
34480     },
34481     
34482     onClick : function(e)
34483     {
34484         e.preventDefault();
34485         
34486         this.fireEvent('click', this);
34487     },
34488     
34489     prev : function(e)
34490     {
34491         e.preventDefault();
34492         
34493         this.indicator = Math.max(1, this.indicator - 1);
34494         
34495         this.update();
34496     },
34497     
34498     next : function(e)
34499     {
34500         e.preventDefault();
34501         
34502         this.indicator = Math.min(this.files.length, this.indicator + 1);
34503         
34504         this.update();
34505     }
34506 });
34507 /*
34508  * - LGPL
34509  *
34510  * RadioSet
34511  *
34512  *
34513  */
34514
34515 /**
34516  * @class Roo.bootstrap.RadioSet
34517  * @extends Roo.bootstrap.Input
34518  * Bootstrap RadioSet class
34519  * @cfg {String} indicatorpos (left|right) default left
34520  * @cfg {Boolean} inline (true|false) inline the element (default true)
34521  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34522  * @constructor
34523  * Create a new RadioSet
34524  * @param {Object} config The config object
34525  */
34526
34527 Roo.bootstrap.RadioSet = function(config){
34528     
34529     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34530     
34531     this.radioes = [];
34532     
34533     Roo.bootstrap.RadioSet.register(this);
34534     
34535     this.addEvents({
34536         /**
34537         * @event check
34538         * Fires when the element is checked or unchecked.
34539         * @param {Roo.bootstrap.RadioSet} this This radio
34540         * @param {Roo.bootstrap.Radio} item The checked item
34541         */
34542        check : true,
34543        /**
34544         * @event click
34545         * Fires when the element is click.
34546         * @param {Roo.bootstrap.RadioSet} this This radio set
34547         * @param {Roo.bootstrap.Radio} item The checked item
34548         * @param {Roo.EventObject} e The event object
34549         */
34550        click : true
34551     });
34552     
34553 };
34554
34555 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34556
34557     radioes : false,
34558     
34559     inline : true,
34560     
34561     weight : '',
34562     
34563     indicatorpos : 'left',
34564     
34565     getAutoCreate : function()
34566     {
34567         var label = {
34568             tag : 'label',
34569             cls : 'roo-radio-set-label',
34570             cn : [
34571                 {
34572                     tag : 'span',
34573                     html : this.fieldLabel
34574                 }
34575             ]
34576         };
34577         if (Roo.bootstrap.version == 3) {
34578             
34579             
34580             if(this.indicatorpos == 'left'){
34581                 label.cn.unshift({
34582                     tag : 'i',
34583                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34584                     tooltip : 'This field is required'
34585                 });
34586             } else {
34587                 label.cn.push({
34588                     tag : 'i',
34589                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34590                     tooltip : 'This field is required'
34591                 });
34592             }
34593         }
34594         var items = {
34595             tag : 'div',
34596             cls : 'roo-radio-set-items'
34597         };
34598         
34599         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34600         
34601         if (align === 'left' && this.fieldLabel.length) {
34602             
34603             items = {
34604                 cls : "roo-radio-set-right", 
34605                 cn: [
34606                     items
34607                 ]
34608             };
34609             
34610             if(this.labelWidth > 12){
34611                 label.style = "width: " + this.labelWidth + 'px';
34612             }
34613             
34614             if(this.labelWidth < 13 && this.labelmd == 0){
34615                 this.labelmd = this.labelWidth;
34616             }
34617             
34618             if(this.labellg > 0){
34619                 label.cls += ' col-lg-' + this.labellg;
34620                 items.cls += ' col-lg-' + (12 - this.labellg);
34621             }
34622             
34623             if(this.labelmd > 0){
34624                 label.cls += ' col-md-' + this.labelmd;
34625                 items.cls += ' col-md-' + (12 - this.labelmd);
34626             }
34627             
34628             if(this.labelsm > 0){
34629                 label.cls += ' col-sm-' + this.labelsm;
34630                 items.cls += ' col-sm-' + (12 - this.labelsm);
34631             }
34632             
34633             if(this.labelxs > 0){
34634                 label.cls += ' col-xs-' + this.labelxs;
34635                 items.cls += ' col-xs-' + (12 - this.labelxs);
34636             }
34637         }
34638         
34639         var cfg = {
34640             tag : 'div',
34641             cls : 'roo-radio-set',
34642             cn : [
34643                 {
34644                     tag : 'input',
34645                     cls : 'roo-radio-set-input',
34646                     type : 'hidden',
34647                     name : this.name,
34648                     value : this.value ? this.value :  ''
34649                 },
34650                 label,
34651                 items
34652             ]
34653         };
34654         
34655         if(this.weight.length){
34656             cfg.cls += ' roo-radio-' + this.weight;
34657         }
34658         
34659         if(this.inline) {
34660             cfg.cls += ' roo-radio-set-inline';
34661         }
34662         
34663         var settings=this;
34664         ['xs','sm','md','lg'].map(function(size){
34665             if (settings[size]) {
34666                 cfg.cls += ' col-' + size + '-' + settings[size];
34667             }
34668         });
34669         
34670         return cfg;
34671         
34672     },
34673
34674     initEvents : function()
34675     {
34676         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34677         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34678         
34679         if(!this.fieldLabel.length){
34680             this.labelEl.hide();
34681         }
34682         
34683         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34684         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34685         
34686         this.indicator = this.indicatorEl();
34687         
34688         if(this.indicator){
34689             this.indicator.addClass('invisible');
34690         }
34691         
34692         this.originalValue = this.getValue();
34693         
34694     },
34695     
34696     inputEl: function ()
34697     {
34698         return this.el.select('.roo-radio-set-input', true).first();
34699     },
34700     
34701     getChildContainer : function()
34702     {
34703         return this.itemsEl;
34704     },
34705     
34706     register : function(item)
34707     {
34708         this.radioes.push(item);
34709         
34710     },
34711     
34712     validate : function()
34713     {   
34714         if(this.getVisibilityEl().hasClass('hidden')){
34715             return true;
34716         }
34717         
34718         var valid = false;
34719         
34720         Roo.each(this.radioes, function(i){
34721             if(!i.checked){
34722                 return;
34723             }
34724             
34725             valid = true;
34726             return false;
34727         });
34728         
34729         if(this.allowBlank) {
34730             return true;
34731         }
34732         
34733         if(this.disabled || valid){
34734             this.markValid();
34735             return true;
34736         }
34737         
34738         this.markInvalid();
34739         return false;
34740         
34741     },
34742     
34743     markValid : function()
34744     {
34745         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34746             this.indicatorEl().removeClass('visible');
34747             this.indicatorEl().addClass('invisible');
34748         }
34749         
34750         
34751         if (Roo.bootstrap.version == 3) {
34752             this.el.removeClass([this.invalidClass, this.validClass]);
34753             this.el.addClass(this.validClass);
34754         } else {
34755             this.el.removeClass(['is-invalid','is-valid']);
34756             this.el.addClass(['is-valid']);
34757         }
34758         this.fireEvent('valid', this);
34759     },
34760     
34761     markInvalid : function(msg)
34762     {
34763         if(this.allowBlank || this.disabled){
34764             return;
34765         }
34766         
34767         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34768             this.indicatorEl().removeClass('invisible');
34769             this.indicatorEl().addClass('visible');
34770         }
34771         if (Roo.bootstrap.version == 3) {
34772             this.el.removeClass([this.invalidClass, this.validClass]);
34773             this.el.addClass(this.invalidClass);
34774         } else {
34775             this.el.removeClass(['is-invalid','is-valid']);
34776             this.el.addClass(['is-invalid']);
34777         }
34778         
34779         this.fireEvent('invalid', this, msg);
34780         
34781     },
34782     
34783     setValue : function(v, suppressEvent)
34784     {   
34785         if(this.value === v){
34786             return;
34787         }
34788         
34789         this.value = v;
34790         
34791         if(this.rendered){
34792             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34793         }
34794         
34795         Roo.each(this.radioes, function(i){
34796             i.checked = false;
34797             i.el.removeClass('checked');
34798         });
34799         
34800         Roo.each(this.radioes, function(i){
34801             
34802             if(i.value === v || i.value.toString() === v.toString()){
34803                 i.checked = true;
34804                 i.el.addClass('checked');
34805                 
34806                 if(suppressEvent !== true){
34807                     this.fireEvent('check', this, i);
34808                 }
34809                 
34810                 return false;
34811             }
34812             
34813         }, this);
34814         
34815         this.validate();
34816     },
34817     
34818     clearInvalid : function(){
34819         
34820         if(!this.el || this.preventMark){
34821             return;
34822         }
34823         
34824         this.el.removeClass([this.invalidClass]);
34825         
34826         this.fireEvent('valid', this);
34827     }
34828     
34829 });
34830
34831 Roo.apply(Roo.bootstrap.RadioSet, {
34832     
34833     groups: {},
34834     
34835     register : function(set)
34836     {
34837         this.groups[set.name] = set;
34838     },
34839     
34840     get: function(name) 
34841     {
34842         if (typeof(this.groups[name]) == 'undefined') {
34843             return false;
34844         }
34845         
34846         return this.groups[name] ;
34847     }
34848     
34849 });
34850 /*
34851  * Based on:
34852  * Ext JS Library 1.1.1
34853  * Copyright(c) 2006-2007, Ext JS, LLC.
34854  *
34855  * Originally Released Under LGPL - original licence link has changed is not relivant.
34856  *
34857  * Fork - LGPL
34858  * <script type="text/javascript">
34859  */
34860
34861
34862 /**
34863  * @class Roo.bootstrap.SplitBar
34864  * @extends Roo.util.Observable
34865  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34866  * <br><br>
34867  * Usage:
34868  * <pre><code>
34869 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34870                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34871 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34872 split.minSize = 100;
34873 split.maxSize = 600;
34874 split.animate = true;
34875 split.on('moved', splitterMoved);
34876 </code></pre>
34877  * @constructor
34878  * Create a new SplitBar
34879  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34880  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34881  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34882  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34883                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34884                         position of the SplitBar).
34885  */
34886 Roo.bootstrap.SplitBar = function(cfg){
34887     
34888     /** @private */
34889     
34890     //{
34891     //  dragElement : elm
34892     //  resizingElement: el,
34893         // optional..
34894     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34895     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34896         // existingProxy ???
34897     //}
34898     
34899     this.el = Roo.get(cfg.dragElement, true);
34900     this.el.dom.unselectable = "on";
34901     /** @private */
34902     this.resizingEl = Roo.get(cfg.resizingElement, true);
34903
34904     /**
34905      * @private
34906      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34907      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34908      * @type Number
34909      */
34910     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34911     
34912     /**
34913      * The minimum size of the resizing element. (Defaults to 0)
34914      * @type Number
34915      */
34916     this.minSize = 0;
34917     
34918     /**
34919      * The maximum size of the resizing element. (Defaults to 2000)
34920      * @type Number
34921      */
34922     this.maxSize = 2000;
34923     
34924     /**
34925      * Whether to animate the transition to the new size
34926      * @type Boolean
34927      */
34928     this.animate = false;
34929     
34930     /**
34931      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34932      * @type Boolean
34933      */
34934     this.useShim = false;
34935     
34936     /** @private */
34937     this.shim = null;
34938     
34939     if(!cfg.existingProxy){
34940         /** @private */
34941         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34942     }else{
34943         this.proxy = Roo.get(cfg.existingProxy).dom;
34944     }
34945     /** @private */
34946     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34947     
34948     /** @private */
34949     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34950     
34951     /** @private */
34952     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34953     
34954     /** @private */
34955     this.dragSpecs = {};
34956     
34957     /**
34958      * @private The adapter to use to positon and resize elements
34959      */
34960     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34961     this.adapter.init(this);
34962     
34963     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34964         /** @private */
34965         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34966         this.el.addClass("roo-splitbar-h");
34967     }else{
34968         /** @private */
34969         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34970         this.el.addClass("roo-splitbar-v");
34971     }
34972     
34973     this.addEvents({
34974         /**
34975          * @event resize
34976          * Fires when the splitter is moved (alias for {@link #event-moved})
34977          * @param {Roo.bootstrap.SplitBar} this
34978          * @param {Number} newSize the new width or height
34979          */
34980         "resize" : true,
34981         /**
34982          * @event moved
34983          * Fires when the splitter is moved
34984          * @param {Roo.bootstrap.SplitBar} this
34985          * @param {Number} newSize the new width or height
34986          */
34987         "moved" : true,
34988         /**
34989          * @event beforeresize
34990          * Fires before the splitter is dragged
34991          * @param {Roo.bootstrap.SplitBar} this
34992          */
34993         "beforeresize" : true,
34994
34995         "beforeapply" : true
34996     });
34997
34998     Roo.util.Observable.call(this);
34999 };
35000
35001 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35002     onStartProxyDrag : function(x, y){
35003         this.fireEvent("beforeresize", this);
35004         if(!this.overlay){
35005             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35006             o.unselectable();
35007             o.enableDisplayMode("block");
35008             // all splitbars share the same overlay
35009             Roo.bootstrap.SplitBar.prototype.overlay = o;
35010         }
35011         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35012         this.overlay.show();
35013         Roo.get(this.proxy).setDisplayed("block");
35014         var size = this.adapter.getElementSize(this);
35015         this.activeMinSize = this.getMinimumSize();;
35016         this.activeMaxSize = this.getMaximumSize();;
35017         var c1 = size - this.activeMinSize;
35018         var c2 = Math.max(this.activeMaxSize - size, 0);
35019         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35020             this.dd.resetConstraints();
35021             this.dd.setXConstraint(
35022                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35023                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35024             );
35025             this.dd.setYConstraint(0, 0);
35026         }else{
35027             this.dd.resetConstraints();
35028             this.dd.setXConstraint(0, 0);
35029             this.dd.setYConstraint(
35030                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35031                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35032             );
35033          }
35034         this.dragSpecs.startSize = size;
35035         this.dragSpecs.startPoint = [x, y];
35036         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35037     },
35038     
35039     /** 
35040      * @private Called after the drag operation by the DDProxy
35041      */
35042     onEndProxyDrag : function(e){
35043         Roo.get(this.proxy).setDisplayed(false);
35044         var endPoint = Roo.lib.Event.getXY(e);
35045         if(this.overlay){
35046             this.overlay.hide();
35047         }
35048         var newSize;
35049         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35050             newSize = this.dragSpecs.startSize + 
35051                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35052                     endPoint[0] - this.dragSpecs.startPoint[0] :
35053                     this.dragSpecs.startPoint[0] - endPoint[0]
35054                 );
35055         }else{
35056             newSize = this.dragSpecs.startSize + 
35057                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35058                     endPoint[1] - this.dragSpecs.startPoint[1] :
35059                     this.dragSpecs.startPoint[1] - endPoint[1]
35060                 );
35061         }
35062         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35063         if(newSize != this.dragSpecs.startSize){
35064             if(this.fireEvent('beforeapply', this, newSize) !== false){
35065                 this.adapter.setElementSize(this, newSize);
35066                 this.fireEvent("moved", this, newSize);
35067                 this.fireEvent("resize", this, newSize);
35068             }
35069         }
35070     },
35071     
35072     /**
35073      * Get the adapter this SplitBar uses
35074      * @return The adapter object
35075      */
35076     getAdapter : function(){
35077         return this.adapter;
35078     },
35079     
35080     /**
35081      * Set the adapter this SplitBar uses
35082      * @param {Object} adapter A SplitBar adapter object
35083      */
35084     setAdapter : function(adapter){
35085         this.adapter = adapter;
35086         this.adapter.init(this);
35087     },
35088     
35089     /**
35090      * Gets the minimum size for the resizing element
35091      * @return {Number} The minimum size
35092      */
35093     getMinimumSize : function(){
35094         return this.minSize;
35095     },
35096     
35097     /**
35098      * Sets the minimum size for the resizing element
35099      * @param {Number} minSize The minimum size
35100      */
35101     setMinimumSize : function(minSize){
35102         this.minSize = minSize;
35103     },
35104     
35105     /**
35106      * Gets the maximum size for the resizing element
35107      * @return {Number} The maximum size
35108      */
35109     getMaximumSize : function(){
35110         return this.maxSize;
35111     },
35112     
35113     /**
35114      * Sets the maximum size for the resizing element
35115      * @param {Number} maxSize The maximum size
35116      */
35117     setMaximumSize : function(maxSize){
35118         this.maxSize = maxSize;
35119     },
35120     
35121     /**
35122      * Sets the initialize size for the resizing element
35123      * @param {Number} size The initial size
35124      */
35125     setCurrentSize : function(size){
35126         var oldAnimate = this.animate;
35127         this.animate = false;
35128         this.adapter.setElementSize(this, size);
35129         this.animate = oldAnimate;
35130     },
35131     
35132     /**
35133      * Destroy this splitbar. 
35134      * @param {Boolean} removeEl True to remove the element
35135      */
35136     destroy : function(removeEl){
35137         if(this.shim){
35138             this.shim.remove();
35139         }
35140         this.dd.unreg();
35141         this.proxy.parentNode.removeChild(this.proxy);
35142         if(removeEl){
35143             this.el.remove();
35144         }
35145     }
35146 });
35147
35148 /**
35149  * @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.
35150  */
35151 Roo.bootstrap.SplitBar.createProxy = function(dir){
35152     var proxy = new Roo.Element(document.createElement("div"));
35153     proxy.unselectable();
35154     var cls = 'roo-splitbar-proxy';
35155     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35156     document.body.appendChild(proxy.dom);
35157     return proxy.dom;
35158 };
35159
35160 /** 
35161  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35162  * Default Adapter. It assumes the splitter and resizing element are not positioned
35163  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35164  */
35165 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35166 };
35167
35168 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35169     // do nothing for now
35170     init : function(s){
35171     
35172     },
35173     /**
35174      * Called before drag operations to get the current size of the resizing element. 
35175      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35176      */
35177      getElementSize : function(s){
35178         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35179             return s.resizingEl.getWidth();
35180         }else{
35181             return s.resizingEl.getHeight();
35182         }
35183     },
35184     
35185     /**
35186      * Called after drag operations to set the size of the resizing element.
35187      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35188      * @param {Number} newSize The new size to set
35189      * @param {Function} onComplete A function to be invoked when resizing is complete
35190      */
35191     setElementSize : function(s, newSize, onComplete){
35192         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35193             if(!s.animate){
35194                 s.resizingEl.setWidth(newSize);
35195                 if(onComplete){
35196                     onComplete(s, newSize);
35197                 }
35198             }else{
35199                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35200             }
35201         }else{
35202             
35203             if(!s.animate){
35204                 s.resizingEl.setHeight(newSize);
35205                 if(onComplete){
35206                     onComplete(s, newSize);
35207                 }
35208             }else{
35209                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35210             }
35211         }
35212     }
35213 };
35214
35215 /** 
35216  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35217  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35218  * Adapter that  moves the splitter element to align with the resized sizing element. 
35219  * Used with an absolute positioned SplitBar.
35220  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35221  * document.body, make sure you assign an id to the body element.
35222  */
35223 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35224     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35225     this.container = Roo.get(container);
35226 };
35227
35228 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35229     init : function(s){
35230         this.basic.init(s);
35231     },
35232     
35233     getElementSize : function(s){
35234         return this.basic.getElementSize(s);
35235     },
35236     
35237     setElementSize : function(s, newSize, onComplete){
35238         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35239     },
35240     
35241     moveSplitter : function(s){
35242         var yes = Roo.bootstrap.SplitBar;
35243         switch(s.placement){
35244             case yes.LEFT:
35245                 s.el.setX(s.resizingEl.getRight());
35246                 break;
35247             case yes.RIGHT:
35248                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35249                 break;
35250             case yes.TOP:
35251                 s.el.setY(s.resizingEl.getBottom());
35252                 break;
35253             case yes.BOTTOM:
35254                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35255                 break;
35256         }
35257     }
35258 };
35259
35260 /**
35261  * Orientation constant - Create a vertical SplitBar
35262  * @static
35263  * @type Number
35264  */
35265 Roo.bootstrap.SplitBar.VERTICAL = 1;
35266
35267 /**
35268  * Orientation constant - Create a horizontal SplitBar
35269  * @static
35270  * @type Number
35271  */
35272 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35273
35274 /**
35275  * Placement constant - The resizing element is to the left of the splitter element
35276  * @static
35277  * @type Number
35278  */
35279 Roo.bootstrap.SplitBar.LEFT = 1;
35280
35281 /**
35282  * Placement constant - The resizing element is to the right of the splitter element
35283  * @static
35284  * @type Number
35285  */
35286 Roo.bootstrap.SplitBar.RIGHT = 2;
35287
35288 /**
35289  * Placement constant - The resizing element is positioned above the splitter element
35290  * @static
35291  * @type Number
35292  */
35293 Roo.bootstrap.SplitBar.TOP = 3;
35294
35295 /**
35296  * Placement constant - The resizing element is positioned under splitter element
35297  * @static
35298  * @type Number
35299  */
35300 Roo.bootstrap.SplitBar.BOTTOM = 4;
35301 Roo.namespace("Roo.bootstrap.layout");/*
35302  * Based on:
35303  * Ext JS Library 1.1.1
35304  * Copyright(c) 2006-2007, Ext JS, LLC.
35305  *
35306  * Originally Released Under LGPL - original licence link has changed is not relivant.
35307  *
35308  * Fork - LGPL
35309  * <script type="text/javascript">
35310  */
35311
35312 /**
35313  * @class Roo.bootstrap.layout.Manager
35314  * @extends Roo.bootstrap.Component
35315  * Base class for layout managers.
35316  */
35317 Roo.bootstrap.layout.Manager = function(config)
35318 {
35319     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35320
35321
35322
35323
35324
35325     /** false to disable window resize monitoring @type Boolean */
35326     this.monitorWindowResize = true;
35327     this.regions = {};
35328     this.addEvents({
35329         /**
35330          * @event layout
35331          * Fires when a layout is performed.
35332          * @param {Roo.LayoutManager} this
35333          */
35334         "layout" : true,
35335         /**
35336          * @event regionresized
35337          * Fires when the user resizes a region.
35338          * @param {Roo.LayoutRegion} region The resized region
35339          * @param {Number} newSize The new size (width for east/west, height for north/south)
35340          */
35341         "regionresized" : true,
35342         /**
35343          * @event regioncollapsed
35344          * Fires when a region is collapsed.
35345          * @param {Roo.LayoutRegion} region The collapsed region
35346          */
35347         "regioncollapsed" : true,
35348         /**
35349          * @event regionexpanded
35350          * Fires when a region is expanded.
35351          * @param {Roo.LayoutRegion} region The expanded region
35352          */
35353         "regionexpanded" : true
35354     });
35355     this.updating = false;
35356
35357     if (config.el) {
35358         this.el = Roo.get(config.el);
35359         this.initEvents();
35360     }
35361
35362 };
35363
35364 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35365
35366
35367     regions : null,
35368
35369     monitorWindowResize : true,
35370
35371
35372     updating : false,
35373
35374
35375     onRender : function(ct, position)
35376     {
35377         if(!this.el){
35378             this.el = Roo.get(ct);
35379             this.initEvents();
35380         }
35381         //this.fireEvent('render',this);
35382     },
35383
35384
35385     initEvents: function()
35386     {
35387
35388
35389         // ie scrollbar fix
35390         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35391             document.body.scroll = "no";
35392         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35393             this.el.position('relative');
35394         }
35395         this.id = this.el.id;
35396         this.el.addClass("roo-layout-container");
35397         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35398         if(this.el.dom != document.body ) {
35399             this.el.on('resize', this.layout,this);
35400             this.el.on('show', this.layout,this);
35401         }
35402
35403     },
35404
35405     /**
35406      * Returns true if this layout is currently being updated
35407      * @return {Boolean}
35408      */
35409     isUpdating : function(){
35410         return this.updating;
35411     },
35412
35413     /**
35414      * Suspend the LayoutManager from doing auto-layouts while
35415      * making multiple add or remove calls
35416      */
35417     beginUpdate : function(){
35418         this.updating = true;
35419     },
35420
35421     /**
35422      * Restore auto-layouts and optionally disable the manager from performing a layout
35423      * @param {Boolean} noLayout true to disable a layout update
35424      */
35425     endUpdate : function(noLayout){
35426         this.updating = false;
35427         if(!noLayout){
35428             this.layout();
35429         }
35430     },
35431
35432     layout: function(){
35433         // abstract...
35434     },
35435
35436     onRegionResized : function(region, newSize){
35437         this.fireEvent("regionresized", region, newSize);
35438         this.layout();
35439     },
35440
35441     onRegionCollapsed : function(region){
35442         this.fireEvent("regioncollapsed", region);
35443     },
35444
35445     onRegionExpanded : function(region){
35446         this.fireEvent("regionexpanded", region);
35447     },
35448
35449     /**
35450      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35451      * performs box-model adjustments.
35452      * @return {Object} The size as an object {width: (the width), height: (the height)}
35453      */
35454     getViewSize : function()
35455     {
35456         var size;
35457         if(this.el.dom != document.body){
35458             size = this.el.getSize();
35459         }else{
35460             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35461         }
35462         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35463         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35464         return size;
35465     },
35466
35467     /**
35468      * Returns the Element this layout is bound to.
35469      * @return {Roo.Element}
35470      */
35471     getEl : function(){
35472         return this.el;
35473     },
35474
35475     /**
35476      * Returns the specified region.
35477      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35478      * @return {Roo.LayoutRegion}
35479      */
35480     getRegion : function(target){
35481         return this.regions[target.toLowerCase()];
35482     },
35483
35484     onWindowResize : function(){
35485         if(this.monitorWindowResize){
35486             this.layout();
35487         }
35488     }
35489 });
35490 /*
35491  * Based on:
35492  * Ext JS Library 1.1.1
35493  * Copyright(c) 2006-2007, Ext JS, LLC.
35494  *
35495  * Originally Released Under LGPL - original licence link has changed is not relivant.
35496  *
35497  * Fork - LGPL
35498  * <script type="text/javascript">
35499  */
35500 /**
35501  * @class Roo.bootstrap.layout.Border
35502  * @extends Roo.bootstrap.layout.Manager
35503  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35504  * please see: examples/bootstrap/nested.html<br><br>
35505  
35506 <b>The container the layout is rendered into can be either the body element or any other element.
35507 If it is not the body element, the container needs to either be an absolute positioned element,
35508 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35509 the container size if it is not the body element.</b>
35510
35511 * @constructor
35512 * Create a new Border
35513 * @param {Object} config Configuration options
35514  */
35515 Roo.bootstrap.layout.Border = function(config){
35516     config = config || {};
35517     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35518     
35519     
35520     
35521     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35522         if(config[region]){
35523             config[region].region = region;
35524             this.addRegion(config[region]);
35525         }
35526     },this);
35527     
35528 };
35529
35530 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35531
35532 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35533     
35534     parent : false, // this might point to a 'nest' or a ???
35535     
35536     /**
35537      * Creates and adds a new region if it doesn't already exist.
35538      * @param {String} target The target region key (north, south, east, west or center).
35539      * @param {Object} config The regions config object
35540      * @return {BorderLayoutRegion} The new region
35541      */
35542     addRegion : function(config)
35543     {
35544         if(!this.regions[config.region]){
35545             var r = this.factory(config);
35546             this.bindRegion(r);
35547         }
35548         return this.regions[config.region];
35549     },
35550
35551     // private (kinda)
35552     bindRegion : function(r){
35553         this.regions[r.config.region] = r;
35554         
35555         r.on("visibilitychange",    this.layout, this);
35556         r.on("paneladded",          this.layout, this);
35557         r.on("panelremoved",        this.layout, this);
35558         r.on("invalidated",         this.layout, this);
35559         r.on("resized",             this.onRegionResized, this);
35560         r.on("collapsed",           this.onRegionCollapsed, this);
35561         r.on("expanded",            this.onRegionExpanded, this);
35562     },
35563
35564     /**
35565      * Performs a layout update.
35566      */
35567     layout : function()
35568     {
35569         if(this.updating) {
35570             return;
35571         }
35572         
35573         // render all the rebions if they have not been done alreayd?
35574         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35575             if(this.regions[region] && !this.regions[region].bodyEl){
35576                 this.regions[region].onRender(this.el)
35577             }
35578         },this);
35579         
35580         var size = this.getViewSize();
35581         var w = size.width;
35582         var h = size.height;
35583         var centerW = w;
35584         var centerH = h;
35585         var centerY = 0;
35586         var centerX = 0;
35587         //var x = 0, y = 0;
35588
35589         var rs = this.regions;
35590         var north = rs["north"];
35591         var south = rs["south"]; 
35592         var west = rs["west"];
35593         var east = rs["east"];
35594         var center = rs["center"];
35595         //if(this.hideOnLayout){ // not supported anymore
35596             //c.el.setStyle("display", "none");
35597         //}
35598         if(north && north.isVisible()){
35599             var b = north.getBox();
35600             var m = north.getMargins();
35601             b.width = w - (m.left+m.right);
35602             b.x = m.left;
35603             b.y = m.top;
35604             centerY = b.height + b.y + m.bottom;
35605             centerH -= centerY;
35606             north.updateBox(this.safeBox(b));
35607         }
35608         if(south && south.isVisible()){
35609             var b = south.getBox();
35610             var m = south.getMargins();
35611             b.width = w - (m.left+m.right);
35612             b.x = m.left;
35613             var totalHeight = (b.height + m.top + m.bottom);
35614             b.y = h - totalHeight + m.top;
35615             centerH -= totalHeight;
35616             south.updateBox(this.safeBox(b));
35617         }
35618         if(west && west.isVisible()){
35619             var b = west.getBox();
35620             var m = west.getMargins();
35621             b.height = centerH - (m.top+m.bottom);
35622             b.x = m.left;
35623             b.y = centerY + m.top;
35624             var totalWidth = (b.width + m.left + m.right);
35625             centerX += totalWidth;
35626             centerW -= totalWidth;
35627             west.updateBox(this.safeBox(b));
35628         }
35629         if(east && east.isVisible()){
35630             var b = east.getBox();
35631             var m = east.getMargins();
35632             b.height = centerH - (m.top+m.bottom);
35633             var totalWidth = (b.width + m.left + m.right);
35634             b.x = w - totalWidth + m.left;
35635             b.y = centerY + m.top;
35636             centerW -= totalWidth;
35637             east.updateBox(this.safeBox(b));
35638         }
35639         if(center){
35640             var m = center.getMargins();
35641             var centerBox = {
35642                 x: centerX + m.left,
35643                 y: centerY + m.top,
35644                 width: centerW - (m.left+m.right),
35645                 height: centerH - (m.top+m.bottom)
35646             };
35647             //if(this.hideOnLayout){
35648                 //center.el.setStyle("display", "block");
35649             //}
35650             center.updateBox(this.safeBox(centerBox));
35651         }
35652         this.el.repaint();
35653         this.fireEvent("layout", this);
35654     },
35655
35656     // private
35657     safeBox : function(box){
35658         box.width = Math.max(0, box.width);
35659         box.height = Math.max(0, box.height);
35660         return box;
35661     },
35662
35663     /**
35664      * Adds a ContentPanel (or subclass) to this layout.
35665      * @param {String} target The target region key (north, south, east, west or center).
35666      * @param {Roo.ContentPanel} panel The panel to add
35667      * @return {Roo.ContentPanel} The added panel
35668      */
35669     add : function(target, panel){
35670          
35671         target = target.toLowerCase();
35672         return this.regions[target].add(panel);
35673     },
35674
35675     /**
35676      * Remove a ContentPanel (or subclass) to this layout.
35677      * @param {String} target The target region key (north, south, east, west or center).
35678      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35679      * @return {Roo.ContentPanel} The removed panel
35680      */
35681     remove : function(target, panel){
35682         target = target.toLowerCase();
35683         return this.regions[target].remove(panel);
35684     },
35685
35686     /**
35687      * Searches all regions for a panel with the specified id
35688      * @param {String} panelId
35689      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35690      */
35691     findPanel : function(panelId){
35692         var rs = this.regions;
35693         for(var target in rs){
35694             if(typeof rs[target] != "function"){
35695                 var p = rs[target].getPanel(panelId);
35696                 if(p){
35697                     return p;
35698                 }
35699             }
35700         }
35701         return null;
35702     },
35703
35704     /**
35705      * Searches all regions for a panel with the specified id and activates (shows) it.
35706      * @param {String/ContentPanel} panelId The panels id or the panel itself
35707      * @return {Roo.ContentPanel} The shown panel or null
35708      */
35709     showPanel : function(panelId) {
35710       var rs = this.regions;
35711       for(var target in rs){
35712          var r = rs[target];
35713          if(typeof r != "function"){
35714             if(r.hasPanel(panelId)){
35715                return r.showPanel(panelId);
35716             }
35717          }
35718       }
35719       return null;
35720    },
35721
35722    /**
35723      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35724      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35725      */
35726    /*
35727     restoreState : function(provider){
35728         if(!provider){
35729             provider = Roo.state.Manager;
35730         }
35731         var sm = new Roo.LayoutStateManager();
35732         sm.init(this, provider);
35733     },
35734 */
35735  
35736  
35737     /**
35738      * Adds a xtype elements to the layout.
35739      * <pre><code>
35740
35741 layout.addxtype({
35742        xtype : 'ContentPanel',
35743        region: 'west',
35744        items: [ .... ]
35745    }
35746 );
35747
35748 layout.addxtype({
35749         xtype : 'NestedLayoutPanel',
35750         region: 'west',
35751         layout: {
35752            center: { },
35753            west: { }   
35754         },
35755         items : [ ... list of content panels or nested layout panels.. ]
35756    }
35757 );
35758 </code></pre>
35759      * @param {Object} cfg Xtype definition of item to add.
35760      */
35761     addxtype : function(cfg)
35762     {
35763         // basically accepts a pannel...
35764         // can accept a layout region..!?!?
35765         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35766         
35767         
35768         // theory?  children can only be panels??
35769         
35770         //if (!cfg.xtype.match(/Panel$/)) {
35771         //    return false;
35772         //}
35773         var ret = false;
35774         
35775         if (typeof(cfg.region) == 'undefined') {
35776             Roo.log("Failed to add Panel, region was not set");
35777             Roo.log(cfg);
35778             return false;
35779         }
35780         var region = cfg.region;
35781         delete cfg.region;
35782         
35783           
35784         var xitems = [];
35785         if (cfg.items) {
35786             xitems = cfg.items;
35787             delete cfg.items;
35788         }
35789         var nb = false;
35790         
35791         if ( region == 'center') {
35792             Roo.log("Center: " + cfg.title);
35793         }
35794         
35795         
35796         switch(cfg.xtype) 
35797         {
35798             case 'Content':  // ContentPanel (el, cfg)
35799             case 'Scroll':  // ContentPanel (el, cfg)
35800             case 'View': 
35801                 cfg.autoCreate = cfg.autoCreate || true;
35802                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35803                 //} else {
35804                 //    var el = this.el.createChild();
35805                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35806                 //}
35807                 
35808                 this.add(region, ret);
35809                 break;
35810             
35811             /*
35812             case 'TreePanel': // our new panel!
35813                 cfg.el = this.el.createChild();
35814                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35815                 this.add(region, ret);
35816                 break;
35817             */
35818             
35819             case 'Nest': 
35820                 // create a new Layout (which is  a Border Layout...
35821                 
35822                 var clayout = cfg.layout;
35823                 clayout.el  = this.el.createChild();
35824                 clayout.items   = clayout.items  || [];
35825                 
35826                 delete cfg.layout;
35827                 
35828                 // replace this exitems with the clayout ones..
35829                 xitems = clayout.items;
35830                  
35831                 // force background off if it's in center...
35832                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35833                     cfg.background = false;
35834                 }
35835                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35836                 
35837                 
35838                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35839                 //console.log('adding nested layout panel '  + cfg.toSource());
35840                 this.add(region, ret);
35841                 nb = {}; /// find first...
35842                 break;
35843             
35844             case 'Grid':
35845                 
35846                 // needs grid and region
35847                 
35848                 //var el = this.getRegion(region).el.createChild();
35849                 /*
35850                  *var el = this.el.createChild();
35851                 // create the grid first...
35852                 cfg.grid.container = el;
35853                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35854                 */
35855                 
35856                 if (region == 'center' && this.active ) {
35857                     cfg.background = false;
35858                 }
35859                 
35860                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35861                 
35862                 this.add(region, ret);
35863                 /*
35864                 if (cfg.background) {
35865                     // render grid on panel activation (if panel background)
35866                     ret.on('activate', function(gp) {
35867                         if (!gp.grid.rendered) {
35868                     //        gp.grid.render(el);
35869                         }
35870                     });
35871                 } else {
35872                   //  cfg.grid.render(el);
35873                 }
35874                 */
35875                 break;
35876            
35877            
35878             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35879                 // it was the old xcomponent building that caused this before.
35880                 // espeically if border is the top element in the tree.
35881                 ret = this;
35882                 break; 
35883                 
35884                     
35885                 
35886                 
35887                 
35888             default:
35889                 /*
35890                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35891                     
35892                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35893                     this.add(region, ret);
35894                 } else {
35895                 */
35896                     Roo.log(cfg);
35897                     throw "Can not add '" + cfg.xtype + "' to Border";
35898                     return null;
35899              
35900                                 
35901              
35902         }
35903         this.beginUpdate();
35904         // add children..
35905         var region = '';
35906         var abn = {};
35907         Roo.each(xitems, function(i)  {
35908             region = nb && i.region ? i.region : false;
35909             
35910             var add = ret.addxtype(i);
35911            
35912             if (region) {
35913                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35914                 if (!i.background) {
35915                     abn[region] = nb[region] ;
35916                 }
35917             }
35918             
35919         });
35920         this.endUpdate();
35921
35922         // make the last non-background panel active..
35923         //if (nb) { Roo.log(abn); }
35924         if (nb) {
35925             
35926             for(var r in abn) {
35927                 region = this.getRegion(r);
35928                 if (region) {
35929                     // tried using nb[r], but it does not work..
35930                      
35931                     region.showPanel(abn[r]);
35932                    
35933                 }
35934             }
35935         }
35936         return ret;
35937         
35938     },
35939     
35940     
35941 // private
35942     factory : function(cfg)
35943     {
35944         
35945         var validRegions = Roo.bootstrap.layout.Border.regions;
35946
35947         var target = cfg.region;
35948         cfg.mgr = this;
35949         
35950         var r = Roo.bootstrap.layout;
35951         Roo.log(target);
35952         switch(target){
35953             case "north":
35954                 return new r.North(cfg);
35955             case "south":
35956                 return new r.South(cfg);
35957             case "east":
35958                 return new r.East(cfg);
35959             case "west":
35960                 return new r.West(cfg);
35961             case "center":
35962                 return new r.Center(cfg);
35963         }
35964         throw 'Layout region "'+target+'" not supported.';
35965     }
35966     
35967     
35968 });
35969  /*
35970  * Based on:
35971  * Ext JS Library 1.1.1
35972  * Copyright(c) 2006-2007, Ext JS, LLC.
35973  *
35974  * Originally Released Under LGPL - original licence link has changed is not relivant.
35975  *
35976  * Fork - LGPL
35977  * <script type="text/javascript">
35978  */
35979  
35980 /**
35981  * @class Roo.bootstrap.layout.Basic
35982  * @extends Roo.util.Observable
35983  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35984  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35985  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35986  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35987  * @cfg {string}   region  the region that it inhabits..
35988  * @cfg {bool}   skipConfig skip config?
35989  * 
35990
35991  */
35992 Roo.bootstrap.layout.Basic = function(config){
35993     
35994     this.mgr = config.mgr;
35995     
35996     this.position = config.region;
35997     
35998     var skipConfig = config.skipConfig;
35999     
36000     this.events = {
36001         /**
36002          * @scope Roo.BasicLayoutRegion
36003          */
36004         
36005         /**
36006          * @event beforeremove
36007          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36008          * @param {Roo.LayoutRegion} this
36009          * @param {Roo.ContentPanel} panel The panel
36010          * @param {Object} e The cancel event object
36011          */
36012         "beforeremove" : true,
36013         /**
36014          * @event invalidated
36015          * Fires when the layout for this region is changed.
36016          * @param {Roo.LayoutRegion} this
36017          */
36018         "invalidated" : true,
36019         /**
36020          * @event visibilitychange
36021          * Fires when this region is shown or hidden 
36022          * @param {Roo.LayoutRegion} this
36023          * @param {Boolean} visibility true or false
36024          */
36025         "visibilitychange" : true,
36026         /**
36027          * @event paneladded
36028          * Fires when a panel is added. 
36029          * @param {Roo.LayoutRegion} this
36030          * @param {Roo.ContentPanel} panel The panel
36031          */
36032         "paneladded" : true,
36033         /**
36034          * @event panelremoved
36035          * Fires when a panel is removed. 
36036          * @param {Roo.LayoutRegion} this
36037          * @param {Roo.ContentPanel} panel The panel
36038          */
36039         "panelremoved" : true,
36040         /**
36041          * @event beforecollapse
36042          * Fires when this region before collapse.
36043          * @param {Roo.LayoutRegion} this
36044          */
36045         "beforecollapse" : true,
36046         /**
36047          * @event collapsed
36048          * Fires when this region is collapsed.
36049          * @param {Roo.LayoutRegion} this
36050          */
36051         "collapsed" : true,
36052         /**
36053          * @event expanded
36054          * Fires when this region is expanded.
36055          * @param {Roo.LayoutRegion} this
36056          */
36057         "expanded" : true,
36058         /**
36059          * @event slideshow
36060          * Fires when this region is slid into view.
36061          * @param {Roo.LayoutRegion} this
36062          */
36063         "slideshow" : true,
36064         /**
36065          * @event slidehide
36066          * Fires when this region slides out of view. 
36067          * @param {Roo.LayoutRegion} this
36068          */
36069         "slidehide" : true,
36070         /**
36071          * @event panelactivated
36072          * Fires when a panel is activated. 
36073          * @param {Roo.LayoutRegion} this
36074          * @param {Roo.ContentPanel} panel The activated panel
36075          */
36076         "panelactivated" : true,
36077         /**
36078          * @event resized
36079          * Fires when the user resizes this region. 
36080          * @param {Roo.LayoutRegion} this
36081          * @param {Number} newSize The new size (width for east/west, height for north/south)
36082          */
36083         "resized" : true
36084     };
36085     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36086     this.panels = new Roo.util.MixedCollection();
36087     this.panels.getKey = this.getPanelId.createDelegate(this);
36088     this.box = null;
36089     this.activePanel = null;
36090     // ensure listeners are added...
36091     
36092     if (config.listeners || config.events) {
36093         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36094             listeners : config.listeners || {},
36095             events : config.events || {}
36096         });
36097     }
36098     
36099     if(skipConfig !== true){
36100         this.applyConfig(config);
36101     }
36102 };
36103
36104 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36105 {
36106     getPanelId : function(p){
36107         return p.getId();
36108     },
36109     
36110     applyConfig : function(config){
36111         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36112         this.config = config;
36113         
36114     },
36115     
36116     /**
36117      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36118      * the width, for horizontal (north, south) the height.
36119      * @param {Number} newSize The new width or height
36120      */
36121     resizeTo : function(newSize){
36122         var el = this.el ? this.el :
36123                  (this.activePanel ? this.activePanel.getEl() : null);
36124         if(el){
36125             switch(this.position){
36126                 case "east":
36127                 case "west":
36128                     el.setWidth(newSize);
36129                     this.fireEvent("resized", this, newSize);
36130                 break;
36131                 case "north":
36132                 case "south":
36133                     el.setHeight(newSize);
36134                     this.fireEvent("resized", this, newSize);
36135                 break;                
36136             }
36137         }
36138     },
36139     
36140     getBox : function(){
36141         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36142     },
36143     
36144     getMargins : function(){
36145         return this.margins;
36146     },
36147     
36148     updateBox : function(box){
36149         this.box = box;
36150         var el = this.activePanel.getEl();
36151         el.dom.style.left = box.x + "px";
36152         el.dom.style.top = box.y + "px";
36153         this.activePanel.setSize(box.width, box.height);
36154     },
36155     
36156     /**
36157      * Returns the container element for this region.
36158      * @return {Roo.Element}
36159      */
36160     getEl : function(){
36161         return this.activePanel;
36162     },
36163     
36164     /**
36165      * Returns true if this region is currently visible.
36166      * @return {Boolean}
36167      */
36168     isVisible : function(){
36169         return this.activePanel ? true : false;
36170     },
36171     
36172     setActivePanel : function(panel){
36173         panel = this.getPanel(panel);
36174         if(this.activePanel && this.activePanel != panel){
36175             this.activePanel.setActiveState(false);
36176             this.activePanel.getEl().setLeftTop(-10000,-10000);
36177         }
36178         this.activePanel = panel;
36179         panel.setActiveState(true);
36180         if(this.box){
36181             panel.setSize(this.box.width, this.box.height);
36182         }
36183         this.fireEvent("panelactivated", this, panel);
36184         this.fireEvent("invalidated");
36185     },
36186     
36187     /**
36188      * Show the specified panel.
36189      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36190      * @return {Roo.ContentPanel} The shown panel or null
36191      */
36192     showPanel : function(panel){
36193         panel = this.getPanel(panel);
36194         if(panel){
36195             this.setActivePanel(panel);
36196         }
36197         return panel;
36198     },
36199     
36200     /**
36201      * Get the active panel for this region.
36202      * @return {Roo.ContentPanel} The active panel or null
36203      */
36204     getActivePanel : function(){
36205         return this.activePanel;
36206     },
36207     
36208     /**
36209      * Add the passed ContentPanel(s)
36210      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36211      * @return {Roo.ContentPanel} The panel added (if only one was added)
36212      */
36213     add : function(panel){
36214         if(arguments.length > 1){
36215             for(var i = 0, len = arguments.length; i < len; i++) {
36216                 this.add(arguments[i]);
36217             }
36218             return null;
36219         }
36220         if(this.hasPanel(panel)){
36221             this.showPanel(panel);
36222             return panel;
36223         }
36224         var el = panel.getEl();
36225         if(el.dom.parentNode != this.mgr.el.dom){
36226             this.mgr.el.dom.appendChild(el.dom);
36227         }
36228         if(panel.setRegion){
36229             panel.setRegion(this);
36230         }
36231         this.panels.add(panel);
36232         el.setStyle("position", "absolute");
36233         if(!panel.background){
36234             this.setActivePanel(panel);
36235             if(this.config.initialSize && this.panels.getCount()==1){
36236                 this.resizeTo(this.config.initialSize);
36237             }
36238         }
36239         this.fireEvent("paneladded", this, panel);
36240         return panel;
36241     },
36242     
36243     /**
36244      * Returns true if the panel is in this region.
36245      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36246      * @return {Boolean}
36247      */
36248     hasPanel : function(panel){
36249         if(typeof panel == "object"){ // must be panel obj
36250             panel = panel.getId();
36251         }
36252         return this.getPanel(panel) ? true : false;
36253     },
36254     
36255     /**
36256      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36257      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36258      * @param {Boolean} preservePanel Overrides the config preservePanel option
36259      * @return {Roo.ContentPanel} The panel that was removed
36260      */
36261     remove : function(panel, preservePanel){
36262         panel = this.getPanel(panel);
36263         if(!panel){
36264             return null;
36265         }
36266         var e = {};
36267         this.fireEvent("beforeremove", this, panel, e);
36268         if(e.cancel === true){
36269             return null;
36270         }
36271         var panelId = panel.getId();
36272         this.panels.removeKey(panelId);
36273         return panel;
36274     },
36275     
36276     /**
36277      * Returns the panel specified or null if it's not in this region.
36278      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36279      * @return {Roo.ContentPanel}
36280      */
36281     getPanel : function(id){
36282         if(typeof id == "object"){ // must be panel obj
36283             return id;
36284         }
36285         return this.panels.get(id);
36286     },
36287     
36288     /**
36289      * Returns this regions position (north/south/east/west/center).
36290      * @return {String} 
36291      */
36292     getPosition: function(){
36293         return this.position;    
36294     }
36295 });/*
36296  * Based on:
36297  * Ext JS Library 1.1.1
36298  * Copyright(c) 2006-2007, Ext JS, LLC.
36299  *
36300  * Originally Released Under LGPL - original licence link has changed is not relivant.
36301  *
36302  * Fork - LGPL
36303  * <script type="text/javascript">
36304  */
36305  
36306 /**
36307  * @class Roo.bootstrap.layout.Region
36308  * @extends Roo.bootstrap.layout.Basic
36309  * This class represents a region in a layout manager.
36310  
36311  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36312  * @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})
36313  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36314  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36315  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36316  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36317  * @cfg {String}    title           The title for the region (overrides panel titles)
36318  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36319  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36320  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36321  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36322  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36323  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36324  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36325  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36326  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36327  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36328
36329  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36330  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36331  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36332  * @cfg {Number}    width           For East/West panels
36333  * @cfg {Number}    height          For North/South panels
36334  * @cfg {Boolean}   split           To show the splitter
36335  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36336  * 
36337  * @cfg {string}   cls             Extra CSS classes to add to region
36338  * 
36339  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36340  * @cfg {string}   region  the region that it inhabits..
36341  *
36342
36343  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36344  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36345
36346  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36347  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36348  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36349  */
36350 Roo.bootstrap.layout.Region = function(config)
36351 {
36352     this.applyConfig(config);
36353
36354     var mgr = config.mgr;
36355     var pos = config.region;
36356     config.skipConfig = true;
36357     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36358     
36359     if (mgr.el) {
36360         this.onRender(mgr.el);   
36361     }
36362      
36363     this.visible = true;
36364     this.collapsed = false;
36365     this.unrendered_panels = [];
36366 };
36367
36368 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36369
36370     position: '', // set by wrapper (eg. north/south etc..)
36371     unrendered_panels : null,  // unrendered panels.
36372     
36373     tabPosition : false,
36374     
36375     mgr: false, // points to 'Border'
36376     
36377     
36378     createBody : function(){
36379         /** This region's body element 
36380         * @type Roo.Element */
36381         this.bodyEl = this.el.createChild({
36382                 tag: "div",
36383                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36384         });
36385     },
36386
36387     onRender: function(ctr, pos)
36388     {
36389         var dh = Roo.DomHelper;
36390         /** This region's container element 
36391         * @type Roo.Element */
36392         this.el = dh.append(ctr.dom, {
36393                 tag: "div",
36394                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36395             }, true);
36396         /** This region's title element 
36397         * @type Roo.Element */
36398     
36399         this.titleEl = dh.append(this.el.dom,  {
36400                 tag: "div",
36401                 unselectable: "on",
36402                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36403                 children:[
36404                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36405                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36406                 ]
36407             }, true);
36408         
36409         this.titleEl.enableDisplayMode();
36410         /** This region's title text element 
36411         * @type HTMLElement */
36412         this.titleTextEl = this.titleEl.dom.firstChild;
36413         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36414         /*
36415         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36416         this.closeBtn.enableDisplayMode();
36417         this.closeBtn.on("click", this.closeClicked, this);
36418         this.closeBtn.hide();
36419     */
36420         this.createBody(this.config);
36421         if(this.config.hideWhenEmpty){
36422             this.hide();
36423             this.on("paneladded", this.validateVisibility, this);
36424             this.on("panelremoved", this.validateVisibility, this);
36425         }
36426         if(this.autoScroll){
36427             this.bodyEl.setStyle("overflow", "auto");
36428         }else{
36429             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36430         }
36431         //if(c.titlebar !== false){
36432             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36433                 this.titleEl.hide();
36434             }else{
36435                 this.titleEl.show();
36436                 if(this.config.title){
36437                     this.titleTextEl.innerHTML = this.config.title;
36438                 }
36439             }
36440         //}
36441         if(this.config.collapsed){
36442             this.collapse(true);
36443         }
36444         if(this.config.hidden){
36445             this.hide();
36446         }
36447         
36448         if (this.unrendered_panels && this.unrendered_panels.length) {
36449             for (var i =0;i< this.unrendered_panels.length; i++) {
36450                 this.add(this.unrendered_panels[i]);
36451             }
36452             this.unrendered_panels = null;
36453             
36454         }
36455         
36456     },
36457     
36458     applyConfig : function(c)
36459     {
36460         /*
36461          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36462             var dh = Roo.DomHelper;
36463             if(c.titlebar !== false){
36464                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36465                 this.collapseBtn.on("click", this.collapse, this);
36466                 this.collapseBtn.enableDisplayMode();
36467                 /*
36468                 if(c.showPin === true || this.showPin){
36469                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36470                     this.stickBtn.enableDisplayMode();
36471                     this.stickBtn.on("click", this.expand, this);
36472                     this.stickBtn.hide();
36473                 }
36474                 
36475             }
36476             */
36477             /** This region's collapsed element
36478             * @type Roo.Element */
36479             /*
36480              *
36481             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36482                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36483             ]}, true);
36484             
36485             if(c.floatable !== false){
36486                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36487                this.collapsedEl.on("click", this.collapseClick, this);
36488             }
36489
36490             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36491                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36492                    id: "message", unselectable: "on", style:{"float":"left"}});
36493                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36494              }
36495             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36496             this.expandBtn.on("click", this.expand, this);
36497             
36498         }
36499         
36500         if(this.collapseBtn){
36501             this.collapseBtn.setVisible(c.collapsible == true);
36502         }
36503         
36504         this.cmargins = c.cmargins || this.cmargins ||
36505                          (this.position == "west" || this.position == "east" ?
36506                              {top: 0, left: 2, right:2, bottom: 0} :
36507                              {top: 2, left: 0, right:0, bottom: 2});
36508         */
36509         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36510         
36511         
36512         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36513         
36514         this.autoScroll = c.autoScroll || false;
36515         
36516         
36517        
36518         
36519         this.duration = c.duration || .30;
36520         this.slideDuration = c.slideDuration || .45;
36521         this.config = c;
36522        
36523     },
36524     /**
36525      * Returns true if this region is currently visible.
36526      * @return {Boolean}
36527      */
36528     isVisible : function(){
36529         return this.visible;
36530     },
36531
36532     /**
36533      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36534      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36535      */
36536     //setCollapsedTitle : function(title){
36537     //    title = title || "&#160;";
36538      //   if(this.collapsedTitleTextEl){
36539       //      this.collapsedTitleTextEl.innerHTML = title;
36540        // }
36541     //},
36542
36543     getBox : function(){
36544         var b;
36545       //  if(!this.collapsed){
36546             b = this.el.getBox(false, true);
36547        // }else{
36548           //  b = this.collapsedEl.getBox(false, true);
36549         //}
36550         return b;
36551     },
36552
36553     getMargins : function(){
36554         return this.margins;
36555         //return this.collapsed ? this.cmargins : this.margins;
36556     },
36557 /*
36558     highlight : function(){
36559         this.el.addClass("x-layout-panel-dragover");
36560     },
36561
36562     unhighlight : function(){
36563         this.el.removeClass("x-layout-panel-dragover");
36564     },
36565 */
36566     updateBox : function(box)
36567     {
36568         if (!this.bodyEl) {
36569             return; // not rendered yet..
36570         }
36571         
36572         this.box = box;
36573         if(!this.collapsed){
36574             this.el.dom.style.left = box.x + "px";
36575             this.el.dom.style.top = box.y + "px";
36576             this.updateBody(box.width, box.height);
36577         }else{
36578             this.collapsedEl.dom.style.left = box.x + "px";
36579             this.collapsedEl.dom.style.top = box.y + "px";
36580             this.collapsedEl.setSize(box.width, box.height);
36581         }
36582         if(this.tabs){
36583             this.tabs.autoSizeTabs();
36584         }
36585     },
36586
36587     updateBody : function(w, h)
36588     {
36589         if(w !== null){
36590             this.el.setWidth(w);
36591             w -= this.el.getBorderWidth("rl");
36592             if(this.config.adjustments){
36593                 w += this.config.adjustments[0];
36594             }
36595         }
36596         if(h !== null && h > 0){
36597             this.el.setHeight(h);
36598             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36599             h -= this.el.getBorderWidth("tb");
36600             if(this.config.adjustments){
36601                 h += this.config.adjustments[1];
36602             }
36603             this.bodyEl.setHeight(h);
36604             if(this.tabs){
36605                 h = this.tabs.syncHeight(h);
36606             }
36607         }
36608         if(this.panelSize){
36609             w = w !== null ? w : this.panelSize.width;
36610             h = h !== null ? h : this.panelSize.height;
36611         }
36612         if(this.activePanel){
36613             var el = this.activePanel.getEl();
36614             w = w !== null ? w : el.getWidth();
36615             h = h !== null ? h : el.getHeight();
36616             this.panelSize = {width: w, height: h};
36617             this.activePanel.setSize(w, h);
36618         }
36619         if(Roo.isIE && this.tabs){
36620             this.tabs.el.repaint();
36621         }
36622     },
36623
36624     /**
36625      * Returns the container element for this region.
36626      * @return {Roo.Element}
36627      */
36628     getEl : function(){
36629         return this.el;
36630     },
36631
36632     /**
36633      * Hides this region.
36634      */
36635     hide : function(){
36636         //if(!this.collapsed){
36637             this.el.dom.style.left = "-2000px";
36638             this.el.hide();
36639         //}else{
36640          //   this.collapsedEl.dom.style.left = "-2000px";
36641          //   this.collapsedEl.hide();
36642        // }
36643         this.visible = false;
36644         this.fireEvent("visibilitychange", this, false);
36645     },
36646
36647     /**
36648      * Shows this region if it was previously hidden.
36649      */
36650     show : function(){
36651         //if(!this.collapsed){
36652             this.el.show();
36653         //}else{
36654         //    this.collapsedEl.show();
36655        // }
36656         this.visible = true;
36657         this.fireEvent("visibilitychange", this, true);
36658     },
36659 /*
36660     closeClicked : function(){
36661         if(this.activePanel){
36662             this.remove(this.activePanel);
36663         }
36664     },
36665
36666     collapseClick : function(e){
36667         if(this.isSlid){
36668            e.stopPropagation();
36669            this.slideIn();
36670         }else{
36671            e.stopPropagation();
36672            this.slideOut();
36673         }
36674     },
36675 */
36676     /**
36677      * Collapses this region.
36678      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36679      */
36680     /*
36681     collapse : function(skipAnim, skipCheck = false){
36682         if(this.collapsed) {
36683             return;
36684         }
36685         
36686         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36687             
36688             this.collapsed = true;
36689             if(this.split){
36690                 this.split.el.hide();
36691             }
36692             if(this.config.animate && skipAnim !== true){
36693                 this.fireEvent("invalidated", this);
36694                 this.animateCollapse();
36695             }else{
36696                 this.el.setLocation(-20000,-20000);
36697                 this.el.hide();
36698                 this.collapsedEl.show();
36699                 this.fireEvent("collapsed", this);
36700                 this.fireEvent("invalidated", this);
36701             }
36702         }
36703         
36704     },
36705 */
36706     animateCollapse : function(){
36707         // overridden
36708     },
36709
36710     /**
36711      * Expands this region if it was previously collapsed.
36712      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36713      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36714      */
36715     /*
36716     expand : function(e, skipAnim){
36717         if(e) {
36718             e.stopPropagation();
36719         }
36720         if(!this.collapsed || this.el.hasActiveFx()) {
36721             return;
36722         }
36723         if(this.isSlid){
36724             this.afterSlideIn();
36725             skipAnim = true;
36726         }
36727         this.collapsed = false;
36728         if(this.config.animate && skipAnim !== true){
36729             this.animateExpand();
36730         }else{
36731             this.el.show();
36732             if(this.split){
36733                 this.split.el.show();
36734             }
36735             this.collapsedEl.setLocation(-2000,-2000);
36736             this.collapsedEl.hide();
36737             this.fireEvent("invalidated", this);
36738             this.fireEvent("expanded", this);
36739         }
36740     },
36741 */
36742     animateExpand : function(){
36743         // overridden
36744     },
36745
36746     initTabs : function()
36747     {
36748         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36749         
36750         var ts = new Roo.bootstrap.panel.Tabs({
36751             el: this.bodyEl.dom,
36752             region : this,
36753             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36754             disableTooltips: this.config.disableTabTips,
36755             toolbar : this.config.toolbar
36756         });
36757         
36758         if(this.config.hideTabs){
36759             ts.stripWrap.setDisplayed(false);
36760         }
36761         this.tabs = ts;
36762         ts.resizeTabs = this.config.resizeTabs === true;
36763         ts.minTabWidth = this.config.minTabWidth || 40;
36764         ts.maxTabWidth = this.config.maxTabWidth || 250;
36765         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36766         ts.monitorResize = false;
36767         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36768         ts.bodyEl.addClass('roo-layout-tabs-body');
36769         this.panels.each(this.initPanelAsTab, this);
36770     },
36771
36772     initPanelAsTab : function(panel){
36773         var ti = this.tabs.addTab(
36774             panel.getEl().id,
36775             panel.getTitle(),
36776             null,
36777             this.config.closeOnTab && panel.isClosable(),
36778             panel.tpl
36779         );
36780         if(panel.tabTip !== undefined){
36781             ti.setTooltip(panel.tabTip);
36782         }
36783         ti.on("activate", function(){
36784               this.setActivePanel(panel);
36785         }, this);
36786         
36787         if(this.config.closeOnTab){
36788             ti.on("beforeclose", function(t, e){
36789                 e.cancel = true;
36790                 this.remove(panel);
36791             }, this);
36792         }
36793         
36794         panel.tabItem = ti;
36795         
36796         return ti;
36797     },
36798
36799     updatePanelTitle : function(panel, title)
36800     {
36801         if(this.activePanel == panel){
36802             this.updateTitle(title);
36803         }
36804         if(this.tabs){
36805             var ti = this.tabs.getTab(panel.getEl().id);
36806             ti.setText(title);
36807             if(panel.tabTip !== undefined){
36808                 ti.setTooltip(panel.tabTip);
36809             }
36810         }
36811     },
36812
36813     updateTitle : function(title){
36814         if(this.titleTextEl && !this.config.title){
36815             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36816         }
36817     },
36818
36819     setActivePanel : function(panel)
36820     {
36821         panel = this.getPanel(panel);
36822         if(this.activePanel && this.activePanel != panel){
36823             if(this.activePanel.setActiveState(false) === false){
36824                 return;
36825             }
36826         }
36827         this.activePanel = panel;
36828         panel.setActiveState(true);
36829         if(this.panelSize){
36830             panel.setSize(this.panelSize.width, this.panelSize.height);
36831         }
36832         if(this.closeBtn){
36833             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36834         }
36835         this.updateTitle(panel.getTitle());
36836         if(this.tabs){
36837             this.fireEvent("invalidated", this);
36838         }
36839         this.fireEvent("panelactivated", this, panel);
36840     },
36841
36842     /**
36843      * Shows the specified panel.
36844      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36845      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36846      */
36847     showPanel : function(panel)
36848     {
36849         panel = this.getPanel(panel);
36850         if(panel){
36851             if(this.tabs){
36852                 var tab = this.tabs.getTab(panel.getEl().id);
36853                 if(tab.isHidden()){
36854                     this.tabs.unhideTab(tab.id);
36855                 }
36856                 tab.activate();
36857             }else{
36858                 this.setActivePanel(panel);
36859             }
36860         }
36861         return panel;
36862     },
36863
36864     /**
36865      * Get the active panel for this region.
36866      * @return {Roo.ContentPanel} The active panel or null
36867      */
36868     getActivePanel : function(){
36869         return this.activePanel;
36870     },
36871
36872     validateVisibility : function(){
36873         if(this.panels.getCount() < 1){
36874             this.updateTitle("&#160;");
36875             this.closeBtn.hide();
36876             this.hide();
36877         }else{
36878             if(!this.isVisible()){
36879                 this.show();
36880             }
36881         }
36882     },
36883
36884     /**
36885      * Adds the passed ContentPanel(s) to this region.
36886      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36887      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36888      */
36889     add : function(panel)
36890     {
36891         if(arguments.length > 1){
36892             for(var i = 0, len = arguments.length; i < len; i++) {
36893                 this.add(arguments[i]);
36894             }
36895             return null;
36896         }
36897         
36898         // if we have not been rendered yet, then we can not really do much of this..
36899         if (!this.bodyEl) {
36900             this.unrendered_panels.push(panel);
36901             return panel;
36902         }
36903         
36904         
36905         
36906         
36907         if(this.hasPanel(panel)){
36908             this.showPanel(panel);
36909             return panel;
36910         }
36911         panel.setRegion(this);
36912         this.panels.add(panel);
36913        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36914             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36915             // and hide them... ???
36916             this.bodyEl.dom.appendChild(panel.getEl().dom);
36917             if(panel.background !== true){
36918                 this.setActivePanel(panel);
36919             }
36920             this.fireEvent("paneladded", this, panel);
36921             return panel;
36922         }
36923         */
36924         if(!this.tabs){
36925             this.initTabs();
36926         }else{
36927             this.initPanelAsTab(panel);
36928         }
36929         
36930         
36931         if(panel.background !== true){
36932             this.tabs.activate(panel.getEl().id);
36933         }
36934         this.fireEvent("paneladded", this, panel);
36935         return panel;
36936     },
36937
36938     /**
36939      * Hides the tab for the specified panel.
36940      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36941      */
36942     hidePanel : function(panel){
36943         if(this.tabs && (panel = this.getPanel(panel))){
36944             this.tabs.hideTab(panel.getEl().id);
36945         }
36946     },
36947
36948     /**
36949      * Unhides the tab for a previously hidden panel.
36950      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36951      */
36952     unhidePanel : function(panel){
36953         if(this.tabs && (panel = this.getPanel(panel))){
36954             this.tabs.unhideTab(panel.getEl().id);
36955         }
36956     },
36957
36958     clearPanels : function(){
36959         while(this.panels.getCount() > 0){
36960              this.remove(this.panels.first());
36961         }
36962     },
36963
36964     /**
36965      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36966      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36967      * @param {Boolean} preservePanel Overrides the config preservePanel option
36968      * @return {Roo.ContentPanel} The panel that was removed
36969      */
36970     remove : function(panel, preservePanel)
36971     {
36972         panel = this.getPanel(panel);
36973         if(!panel){
36974             return null;
36975         }
36976         var e = {};
36977         this.fireEvent("beforeremove", this, panel, e);
36978         if(e.cancel === true){
36979             return null;
36980         }
36981         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36982         var panelId = panel.getId();
36983         this.panels.removeKey(panelId);
36984         if(preservePanel){
36985             document.body.appendChild(panel.getEl().dom);
36986         }
36987         if(this.tabs){
36988             this.tabs.removeTab(panel.getEl().id);
36989         }else if (!preservePanel){
36990             this.bodyEl.dom.removeChild(panel.getEl().dom);
36991         }
36992         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36993             var p = this.panels.first();
36994             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36995             tempEl.appendChild(p.getEl().dom);
36996             this.bodyEl.update("");
36997             this.bodyEl.dom.appendChild(p.getEl().dom);
36998             tempEl = null;
36999             this.updateTitle(p.getTitle());
37000             this.tabs = null;
37001             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37002             this.setActivePanel(p);
37003         }
37004         panel.setRegion(null);
37005         if(this.activePanel == panel){
37006             this.activePanel = null;
37007         }
37008         if(this.config.autoDestroy !== false && preservePanel !== true){
37009             try{panel.destroy();}catch(e){}
37010         }
37011         this.fireEvent("panelremoved", this, panel);
37012         return panel;
37013     },
37014
37015     /**
37016      * Returns the TabPanel component used by this region
37017      * @return {Roo.TabPanel}
37018      */
37019     getTabs : function(){
37020         return this.tabs;
37021     },
37022
37023     createTool : function(parentEl, className){
37024         var btn = Roo.DomHelper.append(parentEl, {
37025             tag: "div",
37026             cls: "x-layout-tools-button",
37027             children: [ {
37028                 tag: "div",
37029                 cls: "roo-layout-tools-button-inner " + className,
37030                 html: "&#160;"
37031             }]
37032         }, true);
37033         btn.addClassOnOver("roo-layout-tools-button-over");
37034         return btn;
37035     }
37036 });/*
37037  * Based on:
37038  * Ext JS Library 1.1.1
37039  * Copyright(c) 2006-2007, Ext JS, LLC.
37040  *
37041  * Originally Released Under LGPL - original licence link has changed is not relivant.
37042  *
37043  * Fork - LGPL
37044  * <script type="text/javascript">
37045  */
37046  
37047
37048
37049 /**
37050  * @class Roo.SplitLayoutRegion
37051  * @extends Roo.LayoutRegion
37052  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37053  */
37054 Roo.bootstrap.layout.Split = function(config){
37055     this.cursor = config.cursor;
37056     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37057 };
37058
37059 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37060 {
37061     splitTip : "Drag to resize.",
37062     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37063     useSplitTips : false,
37064
37065     applyConfig : function(config){
37066         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37067     },
37068     
37069     onRender : function(ctr,pos) {
37070         
37071         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37072         if(!this.config.split){
37073             return;
37074         }
37075         if(!this.split){
37076             
37077             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37078                             tag: "div",
37079                             id: this.el.id + "-split",
37080                             cls: "roo-layout-split roo-layout-split-"+this.position,
37081                             html: "&#160;"
37082             });
37083             /** The SplitBar for this region 
37084             * @type Roo.SplitBar */
37085             // does not exist yet...
37086             Roo.log([this.position, this.orientation]);
37087             
37088             this.split = new Roo.bootstrap.SplitBar({
37089                 dragElement : splitEl,
37090                 resizingElement: this.el,
37091                 orientation : this.orientation
37092             });
37093             
37094             this.split.on("moved", this.onSplitMove, this);
37095             this.split.useShim = this.config.useShim === true;
37096             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37097             if(this.useSplitTips){
37098                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37099             }
37100             //if(config.collapsible){
37101             //    this.split.el.on("dblclick", this.collapse,  this);
37102             //}
37103         }
37104         if(typeof this.config.minSize != "undefined"){
37105             this.split.minSize = this.config.minSize;
37106         }
37107         if(typeof this.config.maxSize != "undefined"){
37108             this.split.maxSize = this.config.maxSize;
37109         }
37110         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37111             this.hideSplitter();
37112         }
37113         
37114     },
37115
37116     getHMaxSize : function(){
37117          var cmax = this.config.maxSize || 10000;
37118          var center = this.mgr.getRegion("center");
37119          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37120     },
37121
37122     getVMaxSize : function(){
37123          var cmax = this.config.maxSize || 10000;
37124          var center = this.mgr.getRegion("center");
37125          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37126     },
37127
37128     onSplitMove : function(split, newSize){
37129         this.fireEvent("resized", this, newSize);
37130     },
37131     
37132     /** 
37133      * Returns the {@link Roo.SplitBar} for this region.
37134      * @return {Roo.SplitBar}
37135      */
37136     getSplitBar : function(){
37137         return this.split;
37138     },
37139     
37140     hide : function(){
37141         this.hideSplitter();
37142         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37143     },
37144
37145     hideSplitter : function(){
37146         if(this.split){
37147             this.split.el.setLocation(-2000,-2000);
37148             this.split.el.hide();
37149         }
37150     },
37151
37152     show : function(){
37153         if(this.split){
37154             this.split.el.show();
37155         }
37156         Roo.bootstrap.layout.Split.superclass.show.call(this);
37157     },
37158     
37159     beforeSlide: function(){
37160         if(Roo.isGecko){// firefox overflow auto bug workaround
37161             this.bodyEl.clip();
37162             if(this.tabs) {
37163                 this.tabs.bodyEl.clip();
37164             }
37165             if(this.activePanel){
37166                 this.activePanel.getEl().clip();
37167                 
37168                 if(this.activePanel.beforeSlide){
37169                     this.activePanel.beforeSlide();
37170                 }
37171             }
37172         }
37173     },
37174     
37175     afterSlide : function(){
37176         if(Roo.isGecko){// firefox overflow auto bug workaround
37177             this.bodyEl.unclip();
37178             if(this.tabs) {
37179                 this.tabs.bodyEl.unclip();
37180             }
37181             if(this.activePanel){
37182                 this.activePanel.getEl().unclip();
37183                 if(this.activePanel.afterSlide){
37184                     this.activePanel.afterSlide();
37185                 }
37186             }
37187         }
37188     },
37189
37190     initAutoHide : function(){
37191         if(this.autoHide !== false){
37192             if(!this.autoHideHd){
37193                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37194                 this.autoHideHd = {
37195                     "mouseout": function(e){
37196                         if(!e.within(this.el, true)){
37197                             st.delay(500);
37198                         }
37199                     },
37200                     "mouseover" : function(e){
37201                         st.cancel();
37202                     },
37203                     scope : this
37204                 };
37205             }
37206             this.el.on(this.autoHideHd);
37207         }
37208     },
37209
37210     clearAutoHide : function(){
37211         if(this.autoHide !== false){
37212             this.el.un("mouseout", this.autoHideHd.mouseout);
37213             this.el.un("mouseover", this.autoHideHd.mouseover);
37214         }
37215     },
37216
37217     clearMonitor : function(){
37218         Roo.get(document).un("click", this.slideInIf, this);
37219     },
37220
37221     // these names are backwards but not changed for compat
37222     slideOut : function(){
37223         if(this.isSlid || this.el.hasActiveFx()){
37224             return;
37225         }
37226         this.isSlid = true;
37227         if(this.collapseBtn){
37228             this.collapseBtn.hide();
37229         }
37230         this.closeBtnState = this.closeBtn.getStyle('display');
37231         this.closeBtn.hide();
37232         if(this.stickBtn){
37233             this.stickBtn.show();
37234         }
37235         this.el.show();
37236         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37237         this.beforeSlide();
37238         this.el.setStyle("z-index", 10001);
37239         this.el.slideIn(this.getSlideAnchor(), {
37240             callback: function(){
37241                 this.afterSlide();
37242                 this.initAutoHide();
37243                 Roo.get(document).on("click", this.slideInIf, this);
37244                 this.fireEvent("slideshow", this);
37245             },
37246             scope: this,
37247             block: true
37248         });
37249     },
37250
37251     afterSlideIn : function(){
37252         this.clearAutoHide();
37253         this.isSlid = false;
37254         this.clearMonitor();
37255         this.el.setStyle("z-index", "");
37256         if(this.collapseBtn){
37257             this.collapseBtn.show();
37258         }
37259         this.closeBtn.setStyle('display', this.closeBtnState);
37260         if(this.stickBtn){
37261             this.stickBtn.hide();
37262         }
37263         this.fireEvent("slidehide", this);
37264     },
37265
37266     slideIn : function(cb){
37267         if(!this.isSlid || this.el.hasActiveFx()){
37268             Roo.callback(cb);
37269             return;
37270         }
37271         this.isSlid = false;
37272         this.beforeSlide();
37273         this.el.slideOut(this.getSlideAnchor(), {
37274             callback: function(){
37275                 this.el.setLeftTop(-10000, -10000);
37276                 this.afterSlide();
37277                 this.afterSlideIn();
37278                 Roo.callback(cb);
37279             },
37280             scope: this,
37281             block: true
37282         });
37283     },
37284     
37285     slideInIf : function(e){
37286         if(!e.within(this.el)){
37287             this.slideIn();
37288         }
37289     },
37290
37291     animateCollapse : function(){
37292         this.beforeSlide();
37293         this.el.setStyle("z-index", 20000);
37294         var anchor = this.getSlideAnchor();
37295         this.el.slideOut(anchor, {
37296             callback : function(){
37297                 this.el.setStyle("z-index", "");
37298                 this.collapsedEl.slideIn(anchor, {duration:.3});
37299                 this.afterSlide();
37300                 this.el.setLocation(-10000,-10000);
37301                 this.el.hide();
37302                 this.fireEvent("collapsed", this);
37303             },
37304             scope: this,
37305             block: true
37306         });
37307     },
37308
37309     animateExpand : function(){
37310         this.beforeSlide();
37311         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37312         this.el.setStyle("z-index", 20000);
37313         this.collapsedEl.hide({
37314             duration:.1
37315         });
37316         this.el.slideIn(this.getSlideAnchor(), {
37317             callback : function(){
37318                 this.el.setStyle("z-index", "");
37319                 this.afterSlide();
37320                 if(this.split){
37321                     this.split.el.show();
37322                 }
37323                 this.fireEvent("invalidated", this);
37324                 this.fireEvent("expanded", this);
37325             },
37326             scope: this,
37327             block: true
37328         });
37329     },
37330
37331     anchors : {
37332         "west" : "left",
37333         "east" : "right",
37334         "north" : "top",
37335         "south" : "bottom"
37336     },
37337
37338     sanchors : {
37339         "west" : "l",
37340         "east" : "r",
37341         "north" : "t",
37342         "south" : "b"
37343     },
37344
37345     canchors : {
37346         "west" : "tl-tr",
37347         "east" : "tr-tl",
37348         "north" : "tl-bl",
37349         "south" : "bl-tl"
37350     },
37351
37352     getAnchor : function(){
37353         return this.anchors[this.position];
37354     },
37355
37356     getCollapseAnchor : function(){
37357         return this.canchors[this.position];
37358     },
37359
37360     getSlideAnchor : function(){
37361         return this.sanchors[this.position];
37362     },
37363
37364     getAlignAdj : function(){
37365         var cm = this.cmargins;
37366         switch(this.position){
37367             case "west":
37368                 return [0, 0];
37369             break;
37370             case "east":
37371                 return [0, 0];
37372             break;
37373             case "north":
37374                 return [0, 0];
37375             break;
37376             case "south":
37377                 return [0, 0];
37378             break;
37379         }
37380     },
37381
37382     getExpandAdj : function(){
37383         var c = this.collapsedEl, cm = this.cmargins;
37384         switch(this.position){
37385             case "west":
37386                 return [-(cm.right+c.getWidth()+cm.left), 0];
37387             break;
37388             case "east":
37389                 return [cm.right+c.getWidth()+cm.left, 0];
37390             break;
37391             case "north":
37392                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37393             break;
37394             case "south":
37395                 return [0, cm.top+cm.bottom+c.getHeight()];
37396             break;
37397         }
37398     }
37399 });/*
37400  * Based on:
37401  * Ext JS Library 1.1.1
37402  * Copyright(c) 2006-2007, Ext JS, LLC.
37403  *
37404  * Originally Released Under LGPL - original licence link has changed is not relivant.
37405  *
37406  * Fork - LGPL
37407  * <script type="text/javascript">
37408  */
37409 /*
37410  * These classes are private internal classes
37411  */
37412 Roo.bootstrap.layout.Center = function(config){
37413     config.region = "center";
37414     Roo.bootstrap.layout.Region.call(this, config);
37415     this.visible = true;
37416     this.minWidth = config.minWidth || 20;
37417     this.minHeight = config.minHeight || 20;
37418 };
37419
37420 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37421     hide : function(){
37422         // center panel can't be hidden
37423     },
37424     
37425     show : function(){
37426         // center panel can't be hidden
37427     },
37428     
37429     getMinWidth: function(){
37430         return this.minWidth;
37431     },
37432     
37433     getMinHeight: function(){
37434         return this.minHeight;
37435     }
37436 });
37437
37438
37439
37440
37441  
37442
37443
37444
37445
37446
37447
37448 Roo.bootstrap.layout.North = function(config)
37449 {
37450     config.region = 'north';
37451     config.cursor = 'n-resize';
37452     
37453     Roo.bootstrap.layout.Split.call(this, config);
37454     
37455     
37456     if(this.split){
37457         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37458         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37459         this.split.el.addClass("roo-layout-split-v");
37460     }
37461     var size = config.initialSize || config.height;
37462     if(typeof size != "undefined"){
37463         this.el.setHeight(size);
37464     }
37465 };
37466 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37467 {
37468     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37469     
37470     
37471     
37472     getBox : function(){
37473         if(this.collapsed){
37474             return this.collapsedEl.getBox();
37475         }
37476         var box = this.el.getBox();
37477         if(this.split){
37478             box.height += this.split.el.getHeight();
37479         }
37480         return box;
37481     },
37482     
37483     updateBox : function(box){
37484         if(this.split && !this.collapsed){
37485             box.height -= this.split.el.getHeight();
37486             this.split.el.setLeft(box.x);
37487             this.split.el.setTop(box.y+box.height);
37488             this.split.el.setWidth(box.width);
37489         }
37490         if(this.collapsed){
37491             this.updateBody(box.width, null);
37492         }
37493         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37494     }
37495 });
37496
37497
37498
37499
37500
37501 Roo.bootstrap.layout.South = function(config){
37502     config.region = 'south';
37503     config.cursor = 's-resize';
37504     Roo.bootstrap.layout.Split.call(this, config);
37505     if(this.split){
37506         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37507         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37508         this.split.el.addClass("roo-layout-split-v");
37509     }
37510     var size = config.initialSize || config.height;
37511     if(typeof size != "undefined"){
37512         this.el.setHeight(size);
37513     }
37514 };
37515
37516 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37517     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37518     getBox : function(){
37519         if(this.collapsed){
37520             return this.collapsedEl.getBox();
37521         }
37522         var box = this.el.getBox();
37523         if(this.split){
37524             var sh = this.split.el.getHeight();
37525             box.height += sh;
37526             box.y -= sh;
37527         }
37528         return box;
37529     },
37530     
37531     updateBox : function(box){
37532         if(this.split && !this.collapsed){
37533             var sh = this.split.el.getHeight();
37534             box.height -= sh;
37535             box.y += sh;
37536             this.split.el.setLeft(box.x);
37537             this.split.el.setTop(box.y-sh);
37538             this.split.el.setWidth(box.width);
37539         }
37540         if(this.collapsed){
37541             this.updateBody(box.width, null);
37542         }
37543         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37544     }
37545 });
37546
37547 Roo.bootstrap.layout.East = function(config){
37548     config.region = "east";
37549     config.cursor = "e-resize";
37550     Roo.bootstrap.layout.Split.call(this, config);
37551     if(this.split){
37552         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37553         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37554         this.split.el.addClass("roo-layout-split-h");
37555     }
37556     var size = config.initialSize || config.width;
37557     if(typeof size != "undefined"){
37558         this.el.setWidth(size);
37559     }
37560 };
37561 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37562     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37563     getBox : function(){
37564         if(this.collapsed){
37565             return this.collapsedEl.getBox();
37566         }
37567         var box = this.el.getBox();
37568         if(this.split){
37569             var sw = this.split.el.getWidth();
37570             box.width += sw;
37571             box.x -= sw;
37572         }
37573         return box;
37574     },
37575
37576     updateBox : function(box){
37577         if(this.split && !this.collapsed){
37578             var sw = this.split.el.getWidth();
37579             box.width -= sw;
37580             this.split.el.setLeft(box.x);
37581             this.split.el.setTop(box.y);
37582             this.split.el.setHeight(box.height);
37583             box.x += sw;
37584         }
37585         if(this.collapsed){
37586             this.updateBody(null, box.height);
37587         }
37588         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37589     }
37590 });
37591
37592 Roo.bootstrap.layout.West = function(config){
37593     config.region = "west";
37594     config.cursor = "w-resize";
37595     
37596     Roo.bootstrap.layout.Split.call(this, config);
37597     if(this.split){
37598         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37599         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37600         this.split.el.addClass("roo-layout-split-h");
37601     }
37602     
37603 };
37604 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37605     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37606     
37607     onRender: function(ctr, pos)
37608     {
37609         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37610         var size = this.config.initialSize || this.config.width;
37611         if(typeof size != "undefined"){
37612             this.el.setWidth(size);
37613         }
37614     },
37615     
37616     getBox : function(){
37617         if(this.collapsed){
37618             return this.collapsedEl.getBox();
37619         }
37620         var box = this.el.getBox();
37621         if(this.split){
37622             box.width += this.split.el.getWidth();
37623         }
37624         return box;
37625     },
37626     
37627     updateBox : function(box){
37628         if(this.split && !this.collapsed){
37629             var sw = this.split.el.getWidth();
37630             box.width -= sw;
37631             this.split.el.setLeft(box.x+box.width);
37632             this.split.el.setTop(box.y);
37633             this.split.el.setHeight(box.height);
37634         }
37635         if(this.collapsed){
37636             this.updateBody(null, box.height);
37637         }
37638         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37639     }
37640 });Roo.namespace("Roo.bootstrap.panel");/*
37641  * Based on:
37642  * Ext JS Library 1.1.1
37643  * Copyright(c) 2006-2007, Ext JS, LLC.
37644  *
37645  * Originally Released Under LGPL - original licence link has changed is not relivant.
37646  *
37647  * Fork - LGPL
37648  * <script type="text/javascript">
37649  */
37650 /**
37651  * @class Roo.ContentPanel
37652  * @extends Roo.util.Observable
37653  * A basic ContentPanel element.
37654  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37655  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37656  * @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
37657  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37658  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37659  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37660  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37661  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37662  * @cfg {String} title          The title for this panel
37663  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37664  * @cfg {String} url            Calls {@link #setUrl} with this value
37665  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37666  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37667  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37668  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37669  * @cfg {Boolean} badges render the badges
37670
37671  * @constructor
37672  * Create a new ContentPanel.
37673  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37674  * @param {String/Object} config A string to set only the title or a config object
37675  * @param {String} content (optional) Set the HTML content for this panel
37676  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37677  */
37678 Roo.bootstrap.panel.Content = function( config){
37679     
37680     this.tpl = config.tpl || false;
37681     
37682     var el = config.el;
37683     var content = config.content;
37684
37685     if(config.autoCreate){ // xtype is available if this is called from factory
37686         el = Roo.id();
37687     }
37688     this.el = Roo.get(el);
37689     if(!this.el && config && config.autoCreate){
37690         if(typeof config.autoCreate == "object"){
37691             if(!config.autoCreate.id){
37692                 config.autoCreate.id = config.id||el;
37693             }
37694             this.el = Roo.DomHelper.append(document.body,
37695                         config.autoCreate, true);
37696         }else{
37697             var elcfg =  {   tag: "div",
37698                             cls: "roo-layout-inactive-content",
37699                             id: config.id||el
37700                             };
37701             if (config.html) {
37702                 elcfg.html = config.html;
37703                 
37704             }
37705                         
37706             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37707         }
37708     } 
37709     this.closable = false;
37710     this.loaded = false;
37711     this.active = false;
37712    
37713       
37714     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37715         
37716         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37717         
37718         this.wrapEl = this.el; //this.el.wrap();
37719         var ti = [];
37720         if (config.toolbar.items) {
37721             ti = config.toolbar.items ;
37722             delete config.toolbar.items ;
37723         }
37724         
37725         var nitems = [];
37726         this.toolbar.render(this.wrapEl, 'before');
37727         for(var i =0;i < ti.length;i++) {
37728           //  Roo.log(['add child', items[i]]);
37729             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37730         }
37731         this.toolbar.items = nitems;
37732         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37733         delete config.toolbar;
37734         
37735     }
37736     /*
37737     // xtype created footer. - not sure if will work as we normally have to render first..
37738     if (this.footer && !this.footer.el && this.footer.xtype) {
37739         if (!this.wrapEl) {
37740             this.wrapEl = this.el.wrap();
37741         }
37742     
37743         this.footer.container = this.wrapEl.createChild();
37744          
37745         this.footer = Roo.factory(this.footer, Roo);
37746         
37747     }
37748     */
37749     
37750      if(typeof config == "string"){
37751         this.title = config;
37752     }else{
37753         Roo.apply(this, config);
37754     }
37755     
37756     if(this.resizeEl){
37757         this.resizeEl = Roo.get(this.resizeEl, true);
37758     }else{
37759         this.resizeEl = this.el;
37760     }
37761     // handle view.xtype
37762     
37763  
37764     
37765     
37766     this.addEvents({
37767         /**
37768          * @event activate
37769          * Fires when this panel is activated. 
37770          * @param {Roo.ContentPanel} this
37771          */
37772         "activate" : true,
37773         /**
37774          * @event deactivate
37775          * Fires when this panel is activated. 
37776          * @param {Roo.ContentPanel} this
37777          */
37778         "deactivate" : true,
37779
37780         /**
37781          * @event resize
37782          * Fires when this panel is resized if fitToFrame is true.
37783          * @param {Roo.ContentPanel} this
37784          * @param {Number} width The width after any component adjustments
37785          * @param {Number} height The height after any component adjustments
37786          */
37787         "resize" : true,
37788         
37789          /**
37790          * @event render
37791          * Fires when this tab is created
37792          * @param {Roo.ContentPanel} this
37793          */
37794         "render" : true
37795         
37796         
37797         
37798     });
37799     
37800
37801     
37802     
37803     if(this.autoScroll){
37804         this.resizeEl.setStyle("overflow", "auto");
37805     } else {
37806         // fix randome scrolling
37807         //this.el.on('scroll', function() {
37808         //    Roo.log('fix random scolling');
37809         //    this.scrollTo('top',0); 
37810         //});
37811     }
37812     content = content || this.content;
37813     if(content){
37814         this.setContent(content);
37815     }
37816     if(config && config.url){
37817         this.setUrl(this.url, this.params, this.loadOnce);
37818     }
37819     
37820     
37821     
37822     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37823     
37824     if (this.view && typeof(this.view.xtype) != 'undefined') {
37825         this.view.el = this.el.appendChild(document.createElement("div"));
37826         this.view = Roo.factory(this.view); 
37827         this.view.render  &&  this.view.render(false, '');  
37828     }
37829     
37830     
37831     this.fireEvent('render', this);
37832 };
37833
37834 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37835     
37836     tabTip : '',
37837     
37838     setRegion : function(region){
37839         this.region = region;
37840         this.setActiveClass(region && !this.background);
37841     },
37842     
37843     
37844     setActiveClass: function(state)
37845     {
37846         if(state){
37847            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37848            this.el.setStyle('position','relative');
37849         }else{
37850            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37851            this.el.setStyle('position', 'absolute');
37852         } 
37853     },
37854     
37855     /**
37856      * Returns the toolbar for this Panel if one was configured. 
37857      * @return {Roo.Toolbar} 
37858      */
37859     getToolbar : function(){
37860         return this.toolbar;
37861     },
37862     
37863     setActiveState : function(active)
37864     {
37865         this.active = active;
37866         this.setActiveClass(active);
37867         if(!active){
37868             if(this.fireEvent("deactivate", this) === false){
37869                 return false;
37870             }
37871             return true;
37872         }
37873         this.fireEvent("activate", this);
37874         return true;
37875     },
37876     /**
37877      * Updates this panel's element
37878      * @param {String} content The new content
37879      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37880     */
37881     setContent : function(content, loadScripts){
37882         this.el.update(content, loadScripts);
37883     },
37884
37885     ignoreResize : function(w, h){
37886         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37887             return true;
37888         }else{
37889             this.lastSize = {width: w, height: h};
37890             return false;
37891         }
37892     },
37893     /**
37894      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37895      * @return {Roo.UpdateManager} The UpdateManager
37896      */
37897     getUpdateManager : function(){
37898         return this.el.getUpdateManager();
37899     },
37900      /**
37901      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37902      * @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:
37903 <pre><code>
37904 panel.load({
37905     url: "your-url.php",
37906     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37907     callback: yourFunction,
37908     scope: yourObject, //(optional scope)
37909     discardUrl: false,
37910     nocache: false,
37911     text: "Loading...",
37912     timeout: 30,
37913     scripts: false
37914 });
37915 </code></pre>
37916      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37917      * 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.
37918      * @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}
37919      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37920      * @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.
37921      * @return {Roo.ContentPanel} this
37922      */
37923     load : function(){
37924         var um = this.el.getUpdateManager();
37925         um.update.apply(um, arguments);
37926         return this;
37927     },
37928
37929
37930     /**
37931      * 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.
37932      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37933      * @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)
37934      * @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)
37935      * @return {Roo.UpdateManager} The UpdateManager
37936      */
37937     setUrl : function(url, params, loadOnce){
37938         if(this.refreshDelegate){
37939             this.removeListener("activate", this.refreshDelegate);
37940         }
37941         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37942         this.on("activate", this.refreshDelegate);
37943         return this.el.getUpdateManager();
37944     },
37945     
37946     _handleRefresh : function(url, params, loadOnce){
37947         if(!loadOnce || !this.loaded){
37948             var updater = this.el.getUpdateManager();
37949             updater.update(url, params, this._setLoaded.createDelegate(this));
37950         }
37951     },
37952     
37953     _setLoaded : function(){
37954         this.loaded = true;
37955     }, 
37956     
37957     /**
37958      * Returns this panel's id
37959      * @return {String} 
37960      */
37961     getId : function(){
37962         return this.el.id;
37963     },
37964     
37965     /** 
37966      * Returns this panel's element - used by regiosn to add.
37967      * @return {Roo.Element} 
37968      */
37969     getEl : function(){
37970         return this.wrapEl || this.el;
37971     },
37972     
37973    
37974     
37975     adjustForComponents : function(width, height)
37976     {
37977         //Roo.log('adjustForComponents ');
37978         if(this.resizeEl != this.el){
37979             width -= this.el.getFrameWidth('lr');
37980             height -= this.el.getFrameWidth('tb');
37981         }
37982         if(this.toolbar){
37983             var te = this.toolbar.getEl();
37984             te.setWidth(width);
37985             height -= te.getHeight();
37986         }
37987         if(this.footer){
37988             var te = this.footer.getEl();
37989             te.setWidth(width);
37990             height -= te.getHeight();
37991         }
37992         
37993         
37994         if(this.adjustments){
37995             width += this.adjustments[0];
37996             height += this.adjustments[1];
37997         }
37998         return {"width": width, "height": height};
37999     },
38000     
38001     setSize : function(width, height){
38002         if(this.fitToFrame && !this.ignoreResize(width, height)){
38003             if(this.fitContainer && this.resizeEl != this.el){
38004                 this.el.setSize(width, height);
38005             }
38006             var size = this.adjustForComponents(width, height);
38007             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38008             this.fireEvent('resize', this, size.width, size.height);
38009         }
38010     },
38011     
38012     /**
38013      * Returns this panel's title
38014      * @return {String} 
38015      */
38016     getTitle : function(){
38017         
38018         if (typeof(this.title) != 'object') {
38019             return this.title;
38020         }
38021         
38022         var t = '';
38023         for (var k in this.title) {
38024             if (!this.title.hasOwnProperty(k)) {
38025                 continue;
38026             }
38027             
38028             if (k.indexOf('-') >= 0) {
38029                 var s = k.split('-');
38030                 for (var i = 0; i<s.length; i++) {
38031                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38032                 }
38033             } else {
38034                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38035             }
38036         }
38037         return t;
38038     },
38039     
38040     /**
38041      * Set this panel's title
38042      * @param {String} title
38043      */
38044     setTitle : function(title){
38045         this.title = title;
38046         if(this.region){
38047             this.region.updatePanelTitle(this, title);
38048         }
38049     },
38050     
38051     /**
38052      * Returns true is this panel was configured to be closable
38053      * @return {Boolean} 
38054      */
38055     isClosable : function(){
38056         return this.closable;
38057     },
38058     
38059     beforeSlide : function(){
38060         this.el.clip();
38061         this.resizeEl.clip();
38062     },
38063     
38064     afterSlide : function(){
38065         this.el.unclip();
38066         this.resizeEl.unclip();
38067     },
38068     
38069     /**
38070      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38071      *   Will fail silently if the {@link #setUrl} method has not been called.
38072      *   This does not activate the panel, just updates its content.
38073      */
38074     refresh : function(){
38075         if(this.refreshDelegate){
38076            this.loaded = false;
38077            this.refreshDelegate();
38078         }
38079     },
38080     
38081     /**
38082      * Destroys this panel
38083      */
38084     destroy : function(){
38085         this.el.removeAllListeners();
38086         var tempEl = document.createElement("span");
38087         tempEl.appendChild(this.el.dom);
38088         tempEl.innerHTML = "";
38089         this.el.remove();
38090         this.el = null;
38091     },
38092     
38093     /**
38094      * form - if the content panel contains a form - this is a reference to it.
38095      * @type {Roo.form.Form}
38096      */
38097     form : false,
38098     /**
38099      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38100      *    This contains a reference to it.
38101      * @type {Roo.View}
38102      */
38103     view : false,
38104     
38105       /**
38106      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38107      * <pre><code>
38108
38109 layout.addxtype({
38110        xtype : 'Form',
38111        items: [ .... ]
38112    }
38113 );
38114
38115 </code></pre>
38116      * @param {Object} cfg Xtype definition of item to add.
38117      */
38118     
38119     
38120     getChildContainer: function () {
38121         return this.getEl();
38122     }
38123     
38124     
38125     /*
38126         var  ret = new Roo.factory(cfg);
38127         return ret;
38128         
38129         
38130         // add form..
38131         if (cfg.xtype.match(/^Form$/)) {
38132             
38133             var el;
38134             //if (this.footer) {
38135             //    el = this.footer.container.insertSibling(false, 'before');
38136             //} else {
38137                 el = this.el.createChild();
38138             //}
38139
38140             this.form = new  Roo.form.Form(cfg);
38141             
38142             
38143             if ( this.form.allItems.length) {
38144                 this.form.render(el.dom);
38145             }
38146             return this.form;
38147         }
38148         // should only have one of theses..
38149         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38150             // views.. should not be just added - used named prop 'view''
38151             
38152             cfg.el = this.el.appendChild(document.createElement("div"));
38153             // factory?
38154             
38155             var ret = new Roo.factory(cfg);
38156              
38157              ret.render && ret.render(false, ''); // render blank..
38158             this.view = ret;
38159             return ret;
38160         }
38161         return false;
38162     }
38163     \*/
38164 });
38165  
38166 /**
38167  * @class Roo.bootstrap.panel.Grid
38168  * @extends Roo.bootstrap.panel.Content
38169  * @constructor
38170  * Create a new GridPanel.
38171  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38172  * @param {Object} config A the config object
38173   
38174  */
38175
38176
38177
38178 Roo.bootstrap.panel.Grid = function(config)
38179 {
38180     
38181       
38182     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38183         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38184
38185     config.el = this.wrapper;
38186     //this.el = this.wrapper;
38187     
38188       if (config.container) {
38189         // ctor'ed from a Border/panel.grid
38190         
38191         
38192         this.wrapper.setStyle("overflow", "hidden");
38193         this.wrapper.addClass('roo-grid-container');
38194
38195     }
38196     
38197     
38198     if(config.toolbar){
38199         var tool_el = this.wrapper.createChild();    
38200         this.toolbar = Roo.factory(config.toolbar);
38201         var ti = [];
38202         if (config.toolbar.items) {
38203             ti = config.toolbar.items ;
38204             delete config.toolbar.items ;
38205         }
38206         
38207         var nitems = [];
38208         this.toolbar.render(tool_el);
38209         for(var i =0;i < ti.length;i++) {
38210           //  Roo.log(['add child', items[i]]);
38211             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38212         }
38213         this.toolbar.items = nitems;
38214         
38215         delete config.toolbar;
38216     }
38217     
38218     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38219     config.grid.scrollBody = true;;
38220     config.grid.monitorWindowResize = false; // turn off autosizing
38221     config.grid.autoHeight = false;
38222     config.grid.autoWidth = false;
38223     
38224     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38225     
38226     if (config.background) {
38227         // render grid on panel activation (if panel background)
38228         this.on('activate', function(gp) {
38229             if (!gp.grid.rendered) {
38230                 gp.grid.render(this.wrapper);
38231                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38232             }
38233         });
38234             
38235     } else {
38236         this.grid.render(this.wrapper);
38237         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38238
38239     }
38240     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38241     // ??? needed ??? config.el = this.wrapper;
38242     
38243     
38244     
38245   
38246     // xtype created footer. - not sure if will work as we normally have to render first..
38247     if (this.footer && !this.footer.el && this.footer.xtype) {
38248         
38249         var ctr = this.grid.getView().getFooterPanel(true);
38250         this.footer.dataSource = this.grid.dataSource;
38251         this.footer = Roo.factory(this.footer, Roo);
38252         this.footer.render(ctr);
38253         
38254     }
38255     
38256     
38257     
38258     
38259      
38260 };
38261
38262 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38263     getId : function(){
38264         return this.grid.id;
38265     },
38266     
38267     /**
38268      * Returns the grid for this panel
38269      * @return {Roo.bootstrap.Table} 
38270      */
38271     getGrid : function(){
38272         return this.grid;    
38273     },
38274     
38275     setSize : function(width, height){
38276         if(!this.ignoreResize(width, height)){
38277             var grid = this.grid;
38278             var size = this.adjustForComponents(width, height);
38279             var gridel = grid.getGridEl();
38280             gridel.setSize(size.width, size.height);
38281             /*
38282             var thd = grid.getGridEl().select('thead',true).first();
38283             var tbd = grid.getGridEl().select('tbody', true).first();
38284             if (tbd) {
38285                 tbd.setSize(width, height - thd.getHeight());
38286             }
38287             */
38288             grid.autoSize();
38289         }
38290     },
38291      
38292     
38293     
38294     beforeSlide : function(){
38295         this.grid.getView().scroller.clip();
38296     },
38297     
38298     afterSlide : function(){
38299         this.grid.getView().scroller.unclip();
38300     },
38301     
38302     destroy : function(){
38303         this.grid.destroy();
38304         delete this.grid;
38305         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38306     }
38307 });
38308
38309 /**
38310  * @class Roo.bootstrap.panel.Nest
38311  * @extends Roo.bootstrap.panel.Content
38312  * @constructor
38313  * Create a new Panel, that can contain a layout.Border.
38314  * 
38315  * 
38316  * @param {Roo.BorderLayout} layout The layout for this panel
38317  * @param {String/Object} config A string to set only the title or a config object
38318  */
38319 Roo.bootstrap.panel.Nest = function(config)
38320 {
38321     // construct with only one argument..
38322     /* FIXME - implement nicer consturctors
38323     if (layout.layout) {
38324         config = layout;
38325         layout = config.layout;
38326         delete config.layout;
38327     }
38328     if (layout.xtype && !layout.getEl) {
38329         // then layout needs constructing..
38330         layout = Roo.factory(layout, Roo);
38331     }
38332     */
38333     
38334     config.el =  config.layout.getEl();
38335     
38336     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38337     
38338     config.layout.monitorWindowResize = false; // turn off autosizing
38339     this.layout = config.layout;
38340     this.layout.getEl().addClass("roo-layout-nested-layout");
38341     this.layout.parent = this;
38342     
38343     
38344     
38345     
38346 };
38347
38348 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38349
38350     setSize : function(width, height){
38351         if(!this.ignoreResize(width, height)){
38352             var size = this.adjustForComponents(width, height);
38353             var el = this.layout.getEl();
38354             if (size.height < 1) {
38355                 el.setWidth(size.width);   
38356             } else {
38357                 el.setSize(size.width, size.height);
38358             }
38359             var touch = el.dom.offsetWidth;
38360             this.layout.layout();
38361             // ie requires a double layout on the first pass
38362             if(Roo.isIE && !this.initialized){
38363                 this.initialized = true;
38364                 this.layout.layout();
38365             }
38366         }
38367     },
38368     
38369     // activate all subpanels if not currently active..
38370     
38371     setActiveState : function(active){
38372         this.active = active;
38373         this.setActiveClass(active);
38374         
38375         if(!active){
38376             this.fireEvent("deactivate", this);
38377             return;
38378         }
38379         
38380         this.fireEvent("activate", this);
38381         // not sure if this should happen before or after..
38382         if (!this.layout) {
38383             return; // should not happen..
38384         }
38385         var reg = false;
38386         for (var r in this.layout.regions) {
38387             reg = this.layout.getRegion(r);
38388             if (reg.getActivePanel()) {
38389                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38390                 reg.setActivePanel(reg.getActivePanel());
38391                 continue;
38392             }
38393             if (!reg.panels.length) {
38394                 continue;
38395             }
38396             reg.showPanel(reg.getPanel(0));
38397         }
38398         
38399         
38400         
38401         
38402     },
38403     
38404     /**
38405      * Returns the nested BorderLayout for this panel
38406      * @return {Roo.BorderLayout} 
38407      */
38408     getLayout : function(){
38409         return this.layout;
38410     },
38411     
38412      /**
38413      * Adds a xtype elements to the layout of the nested panel
38414      * <pre><code>
38415
38416 panel.addxtype({
38417        xtype : 'ContentPanel',
38418        region: 'west',
38419        items: [ .... ]
38420    }
38421 );
38422
38423 panel.addxtype({
38424         xtype : 'NestedLayoutPanel',
38425         region: 'west',
38426         layout: {
38427            center: { },
38428            west: { }   
38429         },
38430         items : [ ... list of content panels or nested layout panels.. ]
38431    }
38432 );
38433 </code></pre>
38434      * @param {Object} cfg Xtype definition of item to add.
38435      */
38436     addxtype : function(cfg) {
38437         return this.layout.addxtype(cfg);
38438     
38439     }
38440 });/*
38441  * Based on:
38442  * Ext JS Library 1.1.1
38443  * Copyright(c) 2006-2007, Ext JS, LLC.
38444  *
38445  * Originally Released Under LGPL - original licence link has changed is not relivant.
38446  *
38447  * Fork - LGPL
38448  * <script type="text/javascript">
38449  */
38450 /**
38451  * @class Roo.TabPanel
38452  * @extends Roo.util.Observable
38453  * A lightweight tab container.
38454  * <br><br>
38455  * Usage:
38456  * <pre><code>
38457 // basic tabs 1, built from existing content
38458 var tabs = new Roo.TabPanel("tabs1");
38459 tabs.addTab("script", "View Script");
38460 tabs.addTab("markup", "View Markup");
38461 tabs.activate("script");
38462
38463 // more advanced tabs, built from javascript
38464 var jtabs = new Roo.TabPanel("jtabs");
38465 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38466
38467 // set up the UpdateManager
38468 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38469 var updater = tab2.getUpdateManager();
38470 updater.setDefaultUrl("ajax1.htm");
38471 tab2.on('activate', updater.refresh, updater, true);
38472
38473 // Use setUrl for Ajax loading
38474 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38475 tab3.setUrl("ajax2.htm", null, true);
38476
38477 // Disabled tab
38478 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38479 tab4.disable();
38480
38481 jtabs.activate("jtabs-1");
38482  * </code></pre>
38483  * @constructor
38484  * Create a new TabPanel.
38485  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38486  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38487  */
38488 Roo.bootstrap.panel.Tabs = function(config){
38489     /**
38490     * The container element for this TabPanel.
38491     * @type Roo.Element
38492     */
38493     this.el = Roo.get(config.el);
38494     delete config.el;
38495     if(config){
38496         if(typeof config == "boolean"){
38497             this.tabPosition = config ? "bottom" : "top";
38498         }else{
38499             Roo.apply(this, config);
38500         }
38501     }
38502     
38503     if(this.tabPosition == "bottom"){
38504         // if tabs are at the bottom = create the body first.
38505         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38506         this.el.addClass("roo-tabs-bottom");
38507     }
38508     // next create the tabs holders
38509     
38510     if (this.tabPosition == "west"){
38511         
38512         var reg = this.region; // fake it..
38513         while (reg) {
38514             if (!reg.mgr.parent) {
38515                 break;
38516             }
38517             reg = reg.mgr.parent.region;
38518         }
38519         Roo.log("got nest?");
38520         Roo.log(reg);
38521         if (reg.mgr.getRegion('west')) {
38522             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38523             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38524             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38525             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38526             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38527         
38528             
38529         }
38530         
38531         
38532     } else {
38533      
38534         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38535         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38536         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38537         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38538     }
38539     
38540     
38541     if(Roo.isIE){
38542         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38543     }
38544     
38545     // finally - if tabs are at the top, then create the body last..
38546     if(this.tabPosition != "bottom"){
38547         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38548          * @type Roo.Element
38549          */
38550         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38551         this.el.addClass("roo-tabs-top");
38552     }
38553     this.items = [];
38554
38555     this.bodyEl.setStyle("position", "relative");
38556
38557     this.active = null;
38558     this.activateDelegate = this.activate.createDelegate(this);
38559
38560     this.addEvents({
38561         /**
38562          * @event tabchange
38563          * Fires when the active tab changes
38564          * @param {Roo.TabPanel} this
38565          * @param {Roo.TabPanelItem} activePanel The new active tab
38566          */
38567         "tabchange": true,
38568         /**
38569          * @event beforetabchange
38570          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38571          * @param {Roo.TabPanel} this
38572          * @param {Object} e Set cancel to true on this object to cancel the tab change
38573          * @param {Roo.TabPanelItem} tab The tab being changed to
38574          */
38575         "beforetabchange" : true
38576     });
38577
38578     Roo.EventManager.onWindowResize(this.onResize, this);
38579     this.cpad = this.el.getPadding("lr");
38580     this.hiddenCount = 0;
38581
38582
38583     // toolbar on the tabbar support...
38584     if (this.toolbar) {
38585         alert("no toolbar support yet");
38586         this.toolbar  = false;
38587         /*
38588         var tcfg = this.toolbar;
38589         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38590         this.toolbar = new Roo.Toolbar(tcfg);
38591         if (Roo.isSafari) {
38592             var tbl = tcfg.container.child('table', true);
38593             tbl.setAttribute('width', '100%');
38594         }
38595         */
38596         
38597     }
38598    
38599
38600
38601     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38602 };
38603
38604 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38605     /*
38606      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38607      */
38608     tabPosition : "top",
38609     /*
38610      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38611      */
38612     currentTabWidth : 0,
38613     /*
38614      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38615      */
38616     minTabWidth : 40,
38617     /*
38618      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38619      */
38620     maxTabWidth : 250,
38621     /*
38622      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38623      */
38624     preferredTabWidth : 175,
38625     /*
38626      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38627      */
38628     resizeTabs : false,
38629     /*
38630      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38631      */
38632     monitorResize : true,
38633     /*
38634      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38635      */
38636     toolbar : false,  // set by caller..
38637     
38638     region : false, /// set by caller
38639     
38640     disableTooltips : true, // not used yet...
38641
38642     /**
38643      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38644      * @param {String} id The id of the div to use <b>or create</b>
38645      * @param {String} text The text for the tab
38646      * @param {String} content (optional) Content to put in the TabPanelItem body
38647      * @param {Boolean} closable (optional) True to create a close icon on the tab
38648      * @return {Roo.TabPanelItem} The created TabPanelItem
38649      */
38650     addTab : function(id, text, content, closable, tpl)
38651     {
38652         var item = new Roo.bootstrap.panel.TabItem({
38653             panel: this,
38654             id : id,
38655             text : text,
38656             closable : closable,
38657             tpl : tpl
38658         });
38659         this.addTabItem(item);
38660         if(content){
38661             item.setContent(content);
38662         }
38663         return item;
38664     },
38665
38666     /**
38667      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38668      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38669      * @return {Roo.TabPanelItem}
38670      */
38671     getTab : function(id){
38672         return this.items[id];
38673     },
38674
38675     /**
38676      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38677      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38678      */
38679     hideTab : function(id){
38680         var t = this.items[id];
38681         if(!t.isHidden()){
38682            t.setHidden(true);
38683            this.hiddenCount++;
38684            this.autoSizeTabs();
38685         }
38686     },
38687
38688     /**
38689      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38690      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38691      */
38692     unhideTab : function(id){
38693         var t = this.items[id];
38694         if(t.isHidden()){
38695            t.setHidden(false);
38696            this.hiddenCount--;
38697            this.autoSizeTabs();
38698         }
38699     },
38700
38701     /**
38702      * Adds an existing {@link Roo.TabPanelItem}.
38703      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38704      */
38705     addTabItem : function(item)
38706     {
38707         this.items[item.id] = item;
38708         this.items.push(item);
38709         this.autoSizeTabs();
38710       //  if(this.resizeTabs){
38711     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38712   //         this.autoSizeTabs();
38713 //        }else{
38714 //            item.autoSize();
38715        // }
38716     },
38717
38718     /**
38719      * Removes a {@link Roo.TabPanelItem}.
38720      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38721      */
38722     removeTab : function(id){
38723         var items = this.items;
38724         var tab = items[id];
38725         if(!tab) { return; }
38726         var index = items.indexOf(tab);
38727         if(this.active == tab && items.length > 1){
38728             var newTab = this.getNextAvailable(index);
38729             if(newTab) {
38730                 newTab.activate();
38731             }
38732         }
38733         this.stripEl.dom.removeChild(tab.pnode.dom);
38734         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38735             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38736         }
38737         items.splice(index, 1);
38738         delete this.items[tab.id];
38739         tab.fireEvent("close", tab);
38740         tab.purgeListeners();
38741         this.autoSizeTabs();
38742     },
38743
38744     getNextAvailable : function(start){
38745         var items = this.items;
38746         var index = start;
38747         // look for a next tab that will slide over to
38748         // replace the one being removed
38749         while(index < items.length){
38750             var item = items[++index];
38751             if(item && !item.isHidden()){
38752                 return item;
38753             }
38754         }
38755         // if one isn't found select the previous tab (on the left)
38756         index = start;
38757         while(index >= 0){
38758             var item = items[--index];
38759             if(item && !item.isHidden()){
38760                 return item;
38761             }
38762         }
38763         return null;
38764     },
38765
38766     /**
38767      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38768      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38769      */
38770     disableTab : function(id){
38771         var tab = this.items[id];
38772         if(tab && this.active != tab){
38773             tab.disable();
38774         }
38775     },
38776
38777     /**
38778      * Enables a {@link Roo.TabPanelItem} that is disabled.
38779      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38780      */
38781     enableTab : function(id){
38782         var tab = this.items[id];
38783         tab.enable();
38784     },
38785
38786     /**
38787      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38788      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38789      * @return {Roo.TabPanelItem} The TabPanelItem.
38790      */
38791     activate : function(id)
38792     {
38793         //Roo.log('activite:'  + id);
38794         
38795         var tab = this.items[id];
38796         if(!tab){
38797             return null;
38798         }
38799         if(tab == this.active || tab.disabled){
38800             return tab;
38801         }
38802         var e = {};
38803         this.fireEvent("beforetabchange", this, e, tab);
38804         if(e.cancel !== true && !tab.disabled){
38805             if(this.active){
38806                 this.active.hide();
38807             }
38808             this.active = this.items[id];
38809             this.active.show();
38810             this.fireEvent("tabchange", this, this.active);
38811         }
38812         return tab;
38813     },
38814
38815     /**
38816      * Gets the active {@link Roo.TabPanelItem}.
38817      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38818      */
38819     getActiveTab : function(){
38820         return this.active;
38821     },
38822
38823     /**
38824      * Updates the tab body element to fit the height of the container element
38825      * for overflow scrolling
38826      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38827      */
38828     syncHeight : function(targetHeight){
38829         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38830         var bm = this.bodyEl.getMargins();
38831         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38832         this.bodyEl.setHeight(newHeight);
38833         return newHeight;
38834     },
38835
38836     onResize : function(){
38837         if(this.monitorResize){
38838             this.autoSizeTabs();
38839         }
38840     },
38841
38842     /**
38843      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38844      */
38845     beginUpdate : function(){
38846         this.updating = true;
38847     },
38848
38849     /**
38850      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38851      */
38852     endUpdate : function(){
38853         this.updating = false;
38854         this.autoSizeTabs();
38855     },
38856
38857     /**
38858      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38859      */
38860     autoSizeTabs : function()
38861     {
38862         var count = this.items.length;
38863         var vcount = count - this.hiddenCount;
38864         
38865         if (vcount < 2) {
38866             this.stripEl.hide();
38867         } else {
38868             this.stripEl.show();
38869         }
38870         
38871         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38872             return;
38873         }
38874         
38875         
38876         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38877         var availWidth = Math.floor(w / vcount);
38878         var b = this.stripBody;
38879         if(b.getWidth() > w){
38880             var tabs = this.items;
38881             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38882             if(availWidth < this.minTabWidth){
38883                 /*if(!this.sleft){    // incomplete scrolling code
38884                     this.createScrollButtons();
38885                 }
38886                 this.showScroll();
38887                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38888             }
38889         }else{
38890             if(this.currentTabWidth < this.preferredTabWidth){
38891                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38892             }
38893         }
38894     },
38895
38896     /**
38897      * Returns the number of tabs in this TabPanel.
38898      * @return {Number}
38899      */
38900      getCount : function(){
38901          return this.items.length;
38902      },
38903
38904     /**
38905      * Resizes all the tabs to the passed width
38906      * @param {Number} The new width
38907      */
38908     setTabWidth : function(width){
38909         this.currentTabWidth = width;
38910         for(var i = 0, len = this.items.length; i < len; i++) {
38911                 if(!this.items[i].isHidden()) {
38912                 this.items[i].setWidth(width);
38913             }
38914         }
38915     },
38916
38917     /**
38918      * Destroys this TabPanel
38919      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38920      */
38921     destroy : function(removeEl){
38922         Roo.EventManager.removeResizeListener(this.onResize, this);
38923         for(var i = 0, len = this.items.length; i < len; i++){
38924             this.items[i].purgeListeners();
38925         }
38926         if(removeEl === true){
38927             this.el.update("");
38928             this.el.remove();
38929         }
38930     },
38931     
38932     createStrip : function(container)
38933     {
38934         var strip = document.createElement("nav");
38935         strip.className = Roo.bootstrap.version == 4 ?
38936             "navbar-light bg-light" : 
38937             "navbar navbar-default"; //"x-tabs-wrap";
38938         container.appendChild(strip);
38939         return strip;
38940     },
38941     
38942     createStripList : function(strip)
38943     {
38944         // div wrapper for retard IE
38945         // returns the "tr" element.
38946         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38947         //'<div class="x-tabs-strip-wrap">'+
38948           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38949           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38950         return strip.firstChild; //.firstChild.firstChild.firstChild;
38951     },
38952     createBody : function(container)
38953     {
38954         var body = document.createElement("div");
38955         Roo.id(body, "tab-body");
38956         //Roo.fly(body).addClass("x-tabs-body");
38957         Roo.fly(body).addClass("tab-content");
38958         container.appendChild(body);
38959         return body;
38960     },
38961     createItemBody :function(bodyEl, id){
38962         var body = Roo.getDom(id);
38963         if(!body){
38964             body = document.createElement("div");
38965             body.id = id;
38966         }
38967         //Roo.fly(body).addClass("x-tabs-item-body");
38968         Roo.fly(body).addClass("tab-pane");
38969          bodyEl.insertBefore(body, bodyEl.firstChild);
38970         return body;
38971     },
38972     /** @private */
38973     createStripElements :  function(stripEl, text, closable, tpl)
38974     {
38975         var td = document.createElement("li"); // was td..
38976         td.className = 'nav-item';
38977         
38978         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38979         
38980         
38981         stripEl.appendChild(td);
38982         /*if(closable){
38983             td.className = "x-tabs-closable";
38984             if(!this.closeTpl){
38985                 this.closeTpl = new Roo.Template(
38986                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38987                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38988                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38989                 );
38990             }
38991             var el = this.closeTpl.overwrite(td, {"text": text});
38992             var close = el.getElementsByTagName("div")[0];
38993             var inner = el.getElementsByTagName("em")[0];
38994             return {"el": el, "close": close, "inner": inner};
38995         } else {
38996         */
38997         // not sure what this is..
38998 //            if(!this.tabTpl){
38999                 //this.tabTpl = new Roo.Template(
39000                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39001                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39002                 //);
39003 //                this.tabTpl = new Roo.Template(
39004 //                   '<a href="#">' +
39005 //                   '<span unselectable="on"' +
39006 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39007 //                            ' >{text}</span></a>'
39008 //                );
39009 //                
39010 //            }
39011
39012
39013             var template = tpl || this.tabTpl || false;
39014             
39015             if(!template){
39016                 template =  new Roo.Template(
39017                         Roo.bootstrap.version == 4 ? 
39018                             (
39019                                 '<a class="nav-link" href="#" unselectable="on"' +
39020                                      (this.disableTooltips ? '' : ' title="{text}"') +
39021                                      ' >{text}</a>'
39022                             ) : (
39023                                 '<a class="nav-link" href="#">' +
39024                                 '<span unselectable="on"' +
39025                                          (this.disableTooltips ? '' : ' title="{text}"') +
39026                                     ' >{text}</span></a>'
39027                             )
39028                 );
39029             }
39030             
39031             switch (typeof(template)) {
39032                 case 'object' :
39033                     break;
39034                 case 'string' :
39035                     template = new Roo.Template(template);
39036                     break;
39037                 default :
39038                     break;
39039             }
39040             
39041             var el = template.overwrite(td, {"text": text});
39042             
39043             var inner = el.getElementsByTagName("span")[0];
39044             
39045             return {"el": el, "inner": inner};
39046             
39047     }
39048         
39049     
39050 });
39051
39052 /**
39053  * @class Roo.TabPanelItem
39054  * @extends Roo.util.Observable
39055  * Represents an individual item (tab plus body) in a TabPanel.
39056  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39057  * @param {String} id The id of this TabPanelItem
39058  * @param {String} text The text for the tab of this TabPanelItem
39059  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39060  */
39061 Roo.bootstrap.panel.TabItem = function(config){
39062     /**
39063      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39064      * @type Roo.TabPanel
39065      */
39066     this.tabPanel = config.panel;
39067     /**
39068      * The id for this TabPanelItem
39069      * @type String
39070      */
39071     this.id = config.id;
39072     /** @private */
39073     this.disabled = false;
39074     /** @private */
39075     this.text = config.text;
39076     /** @private */
39077     this.loaded = false;
39078     this.closable = config.closable;
39079
39080     /**
39081      * The body element for this TabPanelItem.
39082      * @type Roo.Element
39083      */
39084     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39085     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39086     this.bodyEl.setStyle("display", "block");
39087     this.bodyEl.setStyle("zoom", "1");
39088     //this.hideAction();
39089
39090     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39091     /** @private */
39092     this.el = Roo.get(els.el);
39093     this.inner = Roo.get(els.inner, true);
39094      this.textEl = Roo.bootstrap.version == 4 ?
39095         this.el : Roo.get(this.el.dom.firstChild, true);
39096
39097     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39098     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39099
39100     
39101 //    this.el.on("mousedown", this.onTabMouseDown, this);
39102     this.el.on("click", this.onTabClick, this);
39103     /** @private */
39104     if(config.closable){
39105         var c = Roo.get(els.close, true);
39106         c.dom.title = this.closeText;
39107         c.addClassOnOver("close-over");
39108         c.on("click", this.closeClick, this);
39109      }
39110
39111     this.addEvents({
39112          /**
39113          * @event activate
39114          * Fires when this tab becomes the active tab.
39115          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39116          * @param {Roo.TabPanelItem} this
39117          */
39118         "activate": true,
39119         /**
39120          * @event beforeclose
39121          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39122          * @param {Roo.TabPanelItem} this
39123          * @param {Object} e Set cancel to true on this object to cancel the close.
39124          */
39125         "beforeclose": true,
39126         /**
39127          * @event close
39128          * Fires when this tab is closed.
39129          * @param {Roo.TabPanelItem} this
39130          */
39131          "close": true,
39132         /**
39133          * @event deactivate
39134          * Fires when this tab is no longer the active tab.
39135          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39136          * @param {Roo.TabPanelItem} this
39137          */
39138          "deactivate" : true
39139     });
39140     this.hidden = false;
39141
39142     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39143 };
39144
39145 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39146            {
39147     purgeListeners : function(){
39148        Roo.util.Observable.prototype.purgeListeners.call(this);
39149        this.el.removeAllListeners();
39150     },
39151     /**
39152      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39153      */
39154     show : function(){
39155         this.status_node.addClass("active");
39156         this.showAction();
39157         if(Roo.isOpera){
39158             this.tabPanel.stripWrap.repaint();
39159         }
39160         this.fireEvent("activate", this.tabPanel, this);
39161     },
39162
39163     /**
39164      * Returns true if this tab is the active tab.
39165      * @return {Boolean}
39166      */
39167     isActive : function(){
39168         return this.tabPanel.getActiveTab() == this;
39169     },
39170
39171     /**
39172      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39173      */
39174     hide : function(){
39175         this.status_node.removeClass("active");
39176         this.hideAction();
39177         this.fireEvent("deactivate", this.tabPanel, this);
39178     },
39179
39180     hideAction : function(){
39181         this.bodyEl.hide();
39182         this.bodyEl.setStyle("position", "absolute");
39183         this.bodyEl.setLeft("-20000px");
39184         this.bodyEl.setTop("-20000px");
39185     },
39186
39187     showAction : function(){
39188         this.bodyEl.setStyle("position", "relative");
39189         this.bodyEl.setTop("");
39190         this.bodyEl.setLeft("");
39191         this.bodyEl.show();
39192     },
39193
39194     /**
39195      * Set the tooltip for the tab.
39196      * @param {String} tooltip The tab's tooltip
39197      */
39198     setTooltip : function(text){
39199         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39200             this.textEl.dom.qtip = text;
39201             this.textEl.dom.removeAttribute('title');
39202         }else{
39203             this.textEl.dom.title = text;
39204         }
39205     },
39206
39207     onTabClick : function(e){
39208         e.preventDefault();
39209         this.tabPanel.activate(this.id);
39210     },
39211
39212     onTabMouseDown : function(e){
39213         e.preventDefault();
39214         this.tabPanel.activate(this.id);
39215     },
39216 /*
39217     getWidth : function(){
39218         return this.inner.getWidth();
39219     },
39220
39221     setWidth : function(width){
39222         var iwidth = width - this.linode.getPadding("lr");
39223         this.inner.setWidth(iwidth);
39224         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39225         this.linode.setWidth(width);
39226     },
39227 */
39228     /**
39229      * Show or hide the tab
39230      * @param {Boolean} hidden True to hide or false to show.
39231      */
39232     setHidden : function(hidden){
39233         this.hidden = hidden;
39234         this.linode.setStyle("display", hidden ? "none" : "");
39235     },
39236
39237     /**
39238      * Returns true if this tab is "hidden"
39239      * @return {Boolean}
39240      */
39241     isHidden : function(){
39242         return this.hidden;
39243     },
39244
39245     /**
39246      * Returns the text for this tab
39247      * @return {String}
39248      */
39249     getText : function(){
39250         return this.text;
39251     },
39252     /*
39253     autoSize : function(){
39254         //this.el.beginMeasure();
39255         this.textEl.setWidth(1);
39256         /*
39257          *  #2804 [new] Tabs in Roojs
39258          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39259          */
39260         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39261         //this.el.endMeasure();
39262     //},
39263
39264     /**
39265      * Sets the text for the tab (Note: this also sets the tooltip text)
39266      * @param {String} text The tab's text and tooltip
39267      */
39268     setText : function(text){
39269         this.text = text;
39270         this.textEl.update(text);
39271         this.setTooltip(text);
39272         //if(!this.tabPanel.resizeTabs){
39273         //    this.autoSize();
39274         //}
39275     },
39276     /**
39277      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39278      */
39279     activate : function(){
39280         this.tabPanel.activate(this.id);
39281     },
39282
39283     /**
39284      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39285      */
39286     disable : function(){
39287         if(this.tabPanel.active != this){
39288             this.disabled = true;
39289             this.status_node.addClass("disabled");
39290         }
39291     },
39292
39293     /**
39294      * Enables this TabPanelItem if it was previously disabled.
39295      */
39296     enable : function(){
39297         this.disabled = false;
39298         this.status_node.removeClass("disabled");
39299     },
39300
39301     /**
39302      * Sets the content for this TabPanelItem.
39303      * @param {String} content The content
39304      * @param {Boolean} loadScripts true to look for and load scripts
39305      */
39306     setContent : function(content, loadScripts){
39307         this.bodyEl.update(content, loadScripts);
39308     },
39309
39310     /**
39311      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39312      * @return {Roo.UpdateManager} The UpdateManager
39313      */
39314     getUpdateManager : function(){
39315         return this.bodyEl.getUpdateManager();
39316     },
39317
39318     /**
39319      * Set a URL to be used to load the content for this TabPanelItem.
39320      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39321      * @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)
39322      * @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)
39323      * @return {Roo.UpdateManager} The UpdateManager
39324      */
39325     setUrl : function(url, params, loadOnce){
39326         if(this.refreshDelegate){
39327             this.un('activate', this.refreshDelegate);
39328         }
39329         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39330         this.on("activate", this.refreshDelegate);
39331         return this.bodyEl.getUpdateManager();
39332     },
39333
39334     /** @private */
39335     _handleRefresh : function(url, params, loadOnce){
39336         if(!loadOnce || !this.loaded){
39337             var updater = this.bodyEl.getUpdateManager();
39338             updater.update(url, params, this._setLoaded.createDelegate(this));
39339         }
39340     },
39341
39342     /**
39343      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39344      *   Will fail silently if the setUrl method has not been called.
39345      *   This does not activate the panel, just updates its content.
39346      */
39347     refresh : function(){
39348         if(this.refreshDelegate){
39349            this.loaded = false;
39350            this.refreshDelegate();
39351         }
39352     },
39353
39354     /** @private */
39355     _setLoaded : function(){
39356         this.loaded = true;
39357     },
39358
39359     /** @private */
39360     closeClick : function(e){
39361         var o = {};
39362         e.stopEvent();
39363         this.fireEvent("beforeclose", this, o);
39364         if(o.cancel !== true){
39365             this.tabPanel.removeTab(this.id);
39366         }
39367     },
39368     /**
39369      * The text displayed in the tooltip for the close icon.
39370      * @type String
39371      */
39372     closeText : "Close this tab"
39373 });
39374 /**
39375 *    This script refer to:
39376 *    Title: International Telephone Input
39377 *    Author: Jack O'Connor
39378 *    Code version:  v12.1.12
39379 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39380 **/
39381
39382 Roo.bootstrap.PhoneInputData = function() {
39383     var d = [
39384       [
39385         "Afghanistan (‫افغانستان‬‎)",
39386         "af",
39387         "93"
39388       ],
39389       [
39390         "Albania (Shqipëri)",
39391         "al",
39392         "355"
39393       ],
39394       [
39395         "Algeria (‫الجزائر‬‎)",
39396         "dz",
39397         "213"
39398       ],
39399       [
39400         "American Samoa",
39401         "as",
39402         "1684"
39403       ],
39404       [
39405         "Andorra",
39406         "ad",
39407         "376"
39408       ],
39409       [
39410         "Angola",
39411         "ao",
39412         "244"
39413       ],
39414       [
39415         "Anguilla",
39416         "ai",
39417         "1264"
39418       ],
39419       [
39420         "Antigua and Barbuda",
39421         "ag",
39422         "1268"
39423       ],
39424       [
39425         "Argentina",
39426         "ar",
39427         "54"
39428       ],
39429       [
39430         "Armenia (Հայաստան)",
39431         "am",
39432         "374"
39433       ],
39434       [
39435         "Aruba",
39436         "aw",
39437         "297"
39438       ],
39439       [
39440         "Australia",
39441         "au",
39442         "61",
39443         0
39444       ],
39445       [
39446         "Austria (Österreich)",
39447         "at",
39448         "43"
39449       ],
39450       [
39451         "Azerbaijan (Azərbaycan)",
39452         "az",
39453         "994"
39454       ],
39455       [
39456         "Bahamas",
39457         "bs",
39458         "1242"
39459       ],
39460       [
39461         "Bahrain (‫البحرين‬‎)",
39462         "bh",
39463         "973"
39464       ],
39465       [
39466         "Bangladesh (বাংলাদেশ)",
39467         "bd",
39468         "880"
39469       ],
39470       [
39471         "Barbados",
39472         "bb",
39473         "1246"
39474       ],
39475       [
39476         "Belarus (Беларусь)",
39477         "by",
39478         "375"
39479       ],
39480       [
39481         "Belgium (België)",
39482         "be",
39483         "32"
39484       ],
39485       [
39486         "Belize",
39487         "bz",
39488         "501"
39489       ],
39490       [
39491         "Benin (Bénin)",
39492         "bj",
39493         "229"
39494       ],
39495       [
39496         "Bermuda",
39497         "bm",
39498         "1441"
39499       ],
39500       [
39501         "Bhutan (འབྲུག)",
39502         "bt",
39503         "975"
39504       ],
39505       [
39506         "Bolivia",
39507         "bo",
39508         "591"
39509       ],
39510       [
39511         "Bosnia and Herzegovina (Босна и Херцеговина)",
39512         "ba",
39513         "387"
39514       ],
39515       [
39516         "Botswana",
39517         "bw",
39518         "267"
39519       ],
39520       [
39521         "Brazil (Brasil)",
39522         "br",
39523         "55"
39524       ],
39525       [
39526         "British Indian Ocean Territory",
39527         "io",
39528         "246"
39529       ],
39530       [
39531         "British Virgin Islands",
39532         "vg",
39533         "1284"
39534       ],
39535       [
39536         "Brunei",
39537         "bn",
39538         "673"
39539       ],
39540       [
39541         "Bulgaria (България)",
39542         "bg",
39543         "359"
39544       ],
39545       [
39546         "Burkina Faso",
39547         "bf",
39548         "226"
39549       ],
39550       [
39551         "Burundi (Uburundi)",
39552         "bi",
39553         "257"
39554       ],
39555       [
39556         "Cambodia (កម្ពុជា)",
39557         "kh",
39558         "855"
39559       ],
39560       [
39561         "Cameroon (Cameroun)",
39562         "cm",
39563         "237"
39564       ],
39565       [
39566         "Canada",
39567         "ca",
39568         "1",
39569         1,
39570         ["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"]
39571       ],
39572       [
39573         "Cape Verde (Kabu Verdi)",
39574         "cv",
39575         "238"
39576       ],
39577       [
39578         "Caribbean Netherlands",
39579         "bq",
39580         "599",
39581         1
39582       ],
39583       [
39584         "Cayman Islands",
39585         "ky",
39586         "1345"
39587       ],
39588       [
39589         "Central African Republic (République centrafricaine)",
39590         "cf",
39591         "236"
39592       ],
39593       [
39594         "Chad (Tchad)",
39595         "td",
39596         "235"
39597       ],
39598       [
39599         "Chile",
39600         "cl",
39601         "56"
39602       ],
39603       [
39604         "China (中国)",
39605         "cn",
39606         "86"
39607       ],
39608       [
39609         "Christmas Island",
39610         "cx",
39611         "61",
39612         2
39613       ],
39614       [
39615         "Cocos (Keeling) Islands",
39616         "cc",
39617         "61",
39618         1
39619       ],
39620       [
39621         "Colombia",
39622         "co",
39623         "57"
39624       ],
39625       [
39626         "Comoros (‫جزر القمر‬‎)",
39627         "km",
39628         "269"
39629       ],
39630       [
39631         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39632         "cd",
39633         "243"
39634       ],
39635       [
39636         "Congo (Republic) (Congo-Brazzaville)",
39637         "cg",
39638         "242"
39639       ],
39640       [
39641         "Cook Islands",
39642         "ck",
39643         "682"
39644       ],
39645       [
39646         "Costa Rica",
39647         "cr",
39648         "506"
39649       ],
39650       [
39651         "Côte d’Ivoire",
39652         "ci",
39653         "225"
39654       ],
39655       [
39656         "Croatia (Hrvatska)",
39657         "hr",
39658         "385"
39659       ],
39660       [
39661         "Cuba",
39662         "cu",
39663         "53"
39664       ],
39665       [
39666         "Curaçao",
39667         "cw",
39668         "599",
39669         0
39670       ],
39671       [
39672         "Cyprus (Κύπρος)",
39673         "cy",
39674         "357"
39675       ],
39676       [
39677         "Czech Republic (Česká republika)",
39678         "cz",
39679         "420"
39680       ],
39681       [
39682         "Denmark (Danmark)",
39683         "dk",
39684         "45"
39685       ],
39686       [
39687         "Djibouti",
39688         "dj",
39689         "253"
39690       ],
39691       [
39692         "Dominica",
39693         "dm",
39694         "1767"
39695       ],
39696       [
39697         "Dominican Republic (República Dominicana)",
39698         "do",
39699         "1",
39700         2,
39701         ["809", "829", "849"]
39702       ],
39703       [
39704         "Ecuador",
39705         "ec",
39706         "593"
39707       ],
39708       [
39709         "Egypt (‫مصر‬‎)",
39710         "eg",
39711         "20"
39712       ],
39713       [
39714         "El Salvador",
39715         "sv",
39716         "503"
39717       ],
39718       [
39719         "Equatorial Guinea (Guinea Ecuatorial)",
39720         "gq",
39721         "240"
39722       ],
39723       [
39724         "Eritrea",
39725         "er",
39726         "291"
39727       ],
39728       [
39729         "Estonia (Eesti)",
39730         "ee",
39731         "372"
39732       ],
39733       [
39734         "Ethiopia",
39735         "et",
39736         "251"
39737       ],
39738       [
39739         "Falkland Islands (Islas Malvinas)",
39740         "fk",
39741         "500"
39742       ],
39743       [
39744         "Faroe Islands (Føroyar)",
39745         "fo",
39746         "298"
39747       ],
39748       [
39749         "Fiji",
39750         "fj",
39751         "679"
39752       ],
39753       [
39754         "Finland (Suomi)",
39755         "fi",
39756         "358",
39757         0
39758       ],
39759       [
39760         "France",
39761         "fr",
39762         "33"
39763       ],
39764       [
39765         "French Guiana (Guyane française)",
39766         "gf",
39767         "594"
39768       ],
39769       [
39770         "French Polynesia (Polynésie française)",
39771         "pf",
39772         "689"
39773       ],
39774       [
39775         "Gabon",
39776         "ga",
39777         "241"
39778       ],
39779       [
39780         "Gambia",
39781         "gm",
39782         "220"
39783       ],
39784       [
39785         "Georgia (საქართველო)",
39786         "ge",
39787         "995"
39788       ],
39789       [
39790         "Germany (Deutschland)",
39791         "de",
39792         "49"
39793       ],
39794       [
39795         "Ghana (Gaana)",
39796         "gh",
39797         "233"
39798       ],
39799       [
39800         "Gibraltar",
39801         "gi",
39802         "350"
39803       ],
39804       [
39805         "Greece (Ελλάδα)",
39806         "gr",
39807         "30"
39808       ],
39809       [
39810         "Greenland (Kalaallit Nunaat)",
39811         "gl",
39812         "299"
39813       ],
39814       [
39815         "Grenada",
39816         "gd",
39817         "1473"
39818       ],
39819       [
39820         "Guadeloupe",
39821         "gp",
39822         "590",
39823         0
39824       ],
39825       [
39826         "Guam",
39827         "gu",
39828         "1671"
39829       ],
39830       [
39831         "Guatemala",
39832         "gt",
39833         "502"
39834       ],
39835       [
39836         "Guernsey",
39837         "gg",
39838         "44",
39839         1
39840       ],
39841       [
39842         "Guinea (Guinée)",
39843         "gn",
39844         "224"
39845       ],
39846       [
39847         "Guinea-Bissau (Guiné Bissau)",
39848         "gw",
39849         "245"
39850       ],
39851       [
39852         "Guyana",
39853         "gy",
39854         "592"
39855       ],
39856       [
39857         "Haiti",
39858         "ht",
39859         "509"
39860       ],
39861       [
39862         "Honduras",
39863         "hn",
39864         "504"
39865       ],
39866       [
39867         "Hong Kong (香港)",
39868         "hk",
39869         "852"
39870       ],
39871       [
39872         "Hungary (Magyarország)",
39873         "hu",
39874         "36"
39875       ],
39876       [
39877         "Iceland (Ísland)",
39878         "is",
39879         "354"
39880       ],
39881       [
39882         "India (भारत)",
39883         "in",
39884         "91"
39885       ],
39886       [
39887         "Indonesia",
39888         "id",
39889         "62"
39890       ],
39891       [
39892         "Iran (‫ایران‬‎)",
39893         "ir",
39894         "98"
39895       ],
39896       [
39897         "Iraq (‫العراق‬‎)",
39898         "iq",
39899         "964"
39900       ],
39901       [
39902         "Ireland",
39903         "ie",
39904         "353"
39905       ],
39906       [
39907         "Isle of Man",
39908         "im",
39909         "44",
39910         2
39911       ],
39912       [
39913         "Israel (‫ישראל‬‎)",
39914         "il",
39915         "972"
39916       ],
39917       [
39918         "Italy (Italia)",
39919         "it",
39920         "39",
39921         0
39922       ],
39923       [
39924         "Jamaica",
39925         "jm",
39926         "1876"
39927       ],
39928       [
39929         "Japan (日本)",
39930         "jp",
39931         "81"
39932       ],
39933       [
39934         "Jersey",
39935         "je",
39936         "44",
39937         3
39938       ],
39939       [
39940         "Jordan (‫الأردن‬‎)",
39941         "jo",
39942         "962"
39943       ],
39944       [
39945         "Kazakhstan (Казахстан)",
39946         "kz",
39947         "7",
39948         1
39949       ],
39950       [
39951         "Kenya",
39952         "ke",
39953         "254"
39954       ],
39955       [
39956         "Kiribati",
39957         "ki",
39958         "686"
39959       ],
39960       [
39961         "Kosovo",
39962         "xk",
39963         "383"
39964       ],
39965       [
39966         "Kuwait (‫الكويت‬‎)",
39967         "kw",
39968         "965"
39969       ],
39970       [
39971         "Kyrgyzstan (Кыргызстан)",
39972         "kg",
39973         "996"
39974       ],
39975       [
39976         "Laos (ລາວ)",
39977         "la",
39978         "856"
39979       ],
39980       [
39981         "Latvia (Latvija)",
39982         "lv",
39983         "371"
39984       ],
39985       [
39986         "Lebanon (‫لبنان‬‎)",
39987         "lb",
39988         "961"
39989       ],
39990       [
39991         "Lesotho",
39992         "ls",
39993         "266"
39994       ],
39995       [
39996         "Liberia",
39997         "lr",
39998         "231"
39999       ],
40000       [
40001         "Libya (‫ليبيا‬‎)",
40002         "ly",
40003         "218"
40004       ],
40005       [
40006         "Liechtenstein",
40007         "li",
40008         "423"
40009       ],
40010       [
40011         "Lithuania (Lietuva)",
40012         "lt",
40013         "370"
40014       ],
40015       [
40016         "Luxembourg",
40017         "lu",
40018         "352"
40019       ],
40020       [
40021         "Macau (澳門)",
40022         "mo",
40023         "853"
40024       ],
40025       [
40026         "Macedonia (FYROM) (Македонија)",
40027         "mk",
40028         "389"
40029       ],
40030       [
40031         "Madagascar (Madagasikara)",
40032         "mg",
40033         "261"
40034       ],
40035       [
40036         "Malawi",
40037         "mw",
40038         "265"
40039       ],
40040       [
40041         "Malaysia",
40042         "my",
40043         "60"
40044       ],
40045       [
40046         "Maldives",
40047         "mv",
40048         "960"
40049       ],
40050       [
40051         "Mali",
40052         "ml",
40053         "223"
40054       ],
40055       [
40056         "Malta",
40057         "mt",
40058         "356"
40059       ],
40060       [
40061         "Marshall Islands",
40062         "mh",
40063         "692"
40064       ],
40065       [
40066         "Martinique",
40067         "mq",
40068         "596"
40069       ],
40070       [
40071         "Mauritania (‫موريتانيا‬‎)",
40072         "mr",
40073         "222"
40074       ],
40075       [
40076         "Mauritius (Moris)",
40077         "mu",
40078         "230"
40079       ],
40080       [
40081         "Mayotte",
40082         "yt",
40083         "262",
40084         1
40085       ],
40086       [
40087         "Mexico (México)",
40088         "mx",
40089         "52"
40090       ],
40091       [
40092         "Micronesia",
40093         "fm",
40094         "691"
40095       ],
40096       [
40097         "Moldova (Republica Moldova)",
40098         "md",
40099         "373"
40100       ],
40101       [
40102         "Monaco",
40103         "mc",
40104         "377"
40105       ],
40106       [
40107         "Mongolia (Монгол)",
40108         "mn",
40109         "976"
40110       ],
40111       [
40112         "Montenegro (Crna Gora)",
40113         "me",
40114         "382"
40115       ],
40116       [
40117         "Montserrat",
40118         "ms",
40119         "1664"
40120       ],
40121       [
40122         "Morocco (‫المغرب‬‎)",
40123         "ma",
40124         "212",
40125         0
40126       ],
40127       [
40128         "Mozambique (Moçambique)",
40129         "mz",
40130         "258"
40131       ],
40132       [
40133         "Myanmar (Burma) (မြန်မာ)",
40134         "mm",
40135         "95"
40136       ],
40137       [
40138         "Namibia (Namibië)",
40139         "na",
40140         "264"
40141       ],
40142       [
40143         "Nauru",
40144         "nr",
40145         "674"
40146       ],
40147       [
40148         "Nepal (नेपाल)",
40149         "np",
40150         "977"
40151       ],
40152       [
40153         "Netherlands (Nederland)",
40154         "nl",
40155         "31"
40156       ],
40157       [
40158         "New Caledonia (Nouvelle-Calédonie)",
40159         "nc",
40160         "687"
40161       ],
40162       [
40163         "New Zealand",
40164         "nz",
40165         "64"
40166       ],
40167       [
40168         "Nicaragua",
40169         "ni",
40170         "505"
40171       ],
40172       [
40173         "Niger (Nijar)",
40174         "ne",
40175         "227"
40176       ],
40177       [
40178         "Nigeria",
40179         "ng",
40180         "234"
40181       ],
40182       [
40183         "Niue",
40184         "nu",
40185         "683"
40186       ],
40187       [
40188         "Norfolk Island",
40189         "nf",
40190         "672"
40191       ],
40192       [
40193         "North Korea (조선 민주주의 인민 공화국)",
40194         "kp",
40195         "850"
40196       ],
40197       [
40198         "Northern Mariana Islands",
40199         "mp",
40200         "1670"
40201       ],
40202       [
40203         "Norway (Norge)",
40204         "no",
40205         "47",
40206         0
40207       ],
40208       [
40209         "Oman (‫عُمان‬‎)",
40210         "om",
40211         "968"
40212       ],
40213       [
40214         "Pakistan (‫پاکستان‬‎)",
40215         "pk",
40216         "92"
40217       ],
40218       [
40219         "Palau",
40220         "pw",
40221         "680"
40222       ],
40223       [
40224         "Palestine (‫فلسطين‬‎)",
40225         "ps",
40226         "970"
40227       ],
40228       [
40229         "Panama (Panamá)",
40230         "pa",
40231         "507"
40232       ],
40233       [
40234         "Papua New Guinea",
40235         "pg",
40236         "675"
40237       ],
40238       [
40239         "Paraguay",
40240         "py",
40241         "595"
40242       ],
40243       [
40244         "Peru (Perú)",
40245         "pe",
40246         "51"
40247       ],
40248       [
40249         "Philippines",
40250         "ph",
40251         "63"
40252       ],
40253       [
40254         "Poland (Polska)",
40255         "pl",
40256         "48"
40257       ],
40258       [
40259         "Portugal",
40260         "pt",
40261         "351"
40262       ],
40263       [
40264         "Puerto Rico",
40265         "pr",
40266         "1",
40267         3,
40268         ["787", "939"]
40269       ],
40270       [
40271         "Qatar (‫قطر‬‎)",
40272         "qa",
40273         "974"
40274       ],
40275       [
40276         "Réunion (La Réunion)",
40277         "re",
40278         "262",
40279         0
40280       ],
40281       [
40282         "Romania (România)",
40283         "ro",
40284         "40"
40285       ],
40286       [
40287         "Russia (Россия)",
40288         "ru",
40289         "7",
40290         0
40291       ],
40292       [
40293         "Rwanda",
40294         "rw",
40295         "250"
40296       ],
40297       [
40298         "Saint Barthélemy",
40299         "bl",
40300         "590",
40301         1
40302       ],
40303       [
40304         "Saint Helena",
40305         "sh",
40306         "290"
40307       ],
40308       [
40309         "Saint Kitts and Nevis",
40310         "kn",
40311         "1869"
40312       ],
40313       [
40314         "Saint Lucia",
40315         "lc",
40316         "1758"
40317       ],
40318       [
40319         "Saint Martin (Saint-Martin (partie française))",
40320         "mf",
40321         "590",
40322         2
40323       ],
40324       [
40325         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40326         "pm",
40327         "508"
40328       ],
40329       [
40330         "Saint Vincent and the Grenadines",
40331         "vc",
40332         "1784"
40333       ],
40334       [
40335         "Samoa",
40336         "ws",
40337         "685"
40338       ],
40339       [
40340         "San Marino",
40341         "sm",
40342         "378"
40343       ],
40344       [
40345         "São Tomé and Príncipe (São Tomé e Príncipe)",
40346         "st",
40347         "239"
40348       ],
40349       [
40350         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40351         "sa",
40352         "966"
40353       ],
40354       [
40355         "Senegal (Sénégal)",
40356         "sn",
40357         "221"
40358       ],
40359       [
40360         "Serbia (Србија)",
40361         "rs",
40362         "381"
40363       ],
40364       [
40365         "Seychelles",
40366         "sc",
40367         "248"
40368       ],
40369       [
40370         "Sierra Leone",
40371         "sl",
40372         "232"
40373       ],
40374       [
40375         "Singapore",
40376         "sg",
40377         "65"
40378       ],
40379       [
40380         "Sint Maarten",
40381         "sx",
40382         "1721"
40383       ],
40384       [
40385         "Slovakia (Slovensko)",
40386         "sk",
40387         "421"
40388       ],
40389       [
40390         "Slovenia (Slovenija)",
40391         "si",
40392         "386"
40393       ],
40394       [
40395         "Solomon Islands",
40396         "sb",
40397         "677"
40398       ],
40399       [
40400         "Somalia (Soomaaliya)",
40401         "so",
40402         "252"
40403       ],
40404       [
40405         "South Africa",
40406         "za",
40407         "27"
40408       ],
40409       [
40410         "South Korea (대한민국)",
40411         "kr",
40412         "82"
40413       ],
40414       [
40415         "South Sudan (‫جنوب السودان‬‎)",
40416         "ss",
40417         "211"
40418       ],
40419       [
40420         "Spain (España)",
40421         "es",
40422         "34"
40423       ],
40424       [
40425         "Sri Lanka (ශ්‍රී ලංකාව)",
40426         "lk",
40427         "94"
40428       ],
40429       [
40430         "Sudan (‫السودان‬‎)",
40431         "sd",
40432         "249"
40433       ],
40434       [
40435         "Suriname",
40436         "sr",
40437         "597"
40438       ],
40439       [
40440         "Svalbard and Jan Mayen",
40441         "sj",
40442         "47",
40443         1
40444       ],
40445       [
40446         "Swaziland",
40447         "sz",
40448         "268"
40449       ],
40450       [
40451         "Sweden (Sverige)",
40452         "se",
40453         "46"
40454       ],
40455       [
40456         "Switzerland (Schweiz)",
40457         "ch",
40458         "41"
40459       ],
40460       [
40461         "Syria (‫سوريا‬‎)",
40462         "sy",
40463         "963"
40464       ],
40465       [
40466         "Taiwan (台灣)",
40467         "tw",
40468         "886"
40469       ],
40470       [
40471         "Tajikistan",
40472         "tj",
40473         "992"
40474       ],
40475       [
40476         "Tanzania",
40477         "tz",
40478         "255"
40479       ],
40480       [
40481         "Thailand (ไทย)",
40482         "th",
40483         "66"
40484       ],
40485       [
40486         "Timor-Leste",
40487         "tl",
40488         "670"
40489       ],
40490       [
40491         "Togo",
40492         "tg",
40493         "228"
40494       ],
40495       [
40496         "Tokelau",
40497         "tk",
40498         "690"
40499       ],
40500       [
40501         "Tonga",
40502         "to",
40503         "676"
40504       ],
40505       [
40506         "Trinidad and Tobago",
40507         "tt",
40508         "1868"
40509       ],
40510       [
40511         "Tunisia (‫تونس‬‎)",
40512         "tn",
40513         "216"
40514       ],
40515       [
40516         "Turkey (Türkiye)",
40517         "tr",
40518         "90"
40519       ],
40520       [
40521         "Turkmenistan",
40522         "tm",
40523         "993"
40524       ],
40525       [
40526         "Turks and Caicos Islands",
40527         "tc",
40528         "1649"
40529       ],
40530       [
40531         "Tuvalu",
40532         "tv",
40533         "688"
40534       ],
40535       [
40536         "U.S. Virgin Islands",
40537         "vi",
40538         "1340"
40539       ],
40540       [
40541         "Uganda",
40542         "ug",
40543         "256"
40544       ],
40545       [
40546         "Ukraine (Україна)",
40547         "ua",
40548         "380"
40549       ],
40550       [
40551         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40552         "ae",
40553         "971"
40554       ],
40555       [
40556         "United Kingdom",
40557         "gb",
40558         "44",
40559         0
40560       ],
40561       [
40562         "United States",
40563         "us",
40564         "1",
40565         0
40566       ],
40567       [
40568         "Uruguay",
40569         "uy",
40570         "598"
40571       ],
40572       [
40573         "Uzbekistan (Oʻzbekiston)",
40574         "uz",
40575         "998"
40576       ],
40577       [
40578         "Vanuatu",
40579         "vu",
40580         "678"
40581       ],
40582       [
40583         "Vatican City (Città del Vaticano)",
40584         "va",
40585         "39",
40586         1
40587       ],
40588       [
40589         "Venezuela",
40590         "ve",
40591         "58"
40592       ],
40593       [
40594         "Vietnam (Việt Nam)",
40595         "vn",
40596         "84"
40597       ],
40598       [
40599         "Wallis and Futuna (Wallis-et-Futuna)",
40600         "wf",
40601         "681"
40602       ],
40603       [
40604         "Western Sahara (‫الصحراء الغربية‬‎)",
40605         "eh",
40606         "212",
40607         1
40608       ],
40609       [
40610         "Yemen (‫اليمن‬‎)",
40611         "ye",
40612         "967"
40613       ],
40614       [
40615         "Zambia",
40616         "zm",
40617         "260"
40618       ],
40619       [
40620         "Zimbabwe",
40621         "zw",
40622         "263"
40623       ],
40624       [
40625         "Åland Islands",
40626         "ax",
40627         "358",
40628         1
40629       ]
40630   ];
40631   
40632   return d;
40633 }/**
40634 *    This script refer to:
40635 *    Title: International Telephone Input
40636 *    Author: Jack O'Connor
40637 *    Code version:  v12.1.12
40638 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40639 **/
40640
40641 /**
40642  * @class Roo.bootstrap.PhoneInput
40643  * @extends Roo.bootstrap.TriggerField
40644  * An input with International dial-code selection
40645  
40646  * @cfg {String} defaultDialCode default '+852'
40647  * @cfg {Array} preferedCountries default []
40648   
40649  * @constructor
40650  * Create a new PhoneInput.
40651  * @param {Object} config Configuration options
40652  */
40653
40654 Roo.bootstrap.PhoneInput = function(config) {
40655     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40656 };
40657
40658 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40659         
40660         listWidth: undefined,
40661         
40662         selectedClass: 'active',
40663         
40664         invalidClass : "has-warning",
40665         
40666         validClass: 'has-success',
40667         
40668         allowed: '0123456789',
40669         
40670         max_length: 15,
40671         
40672         /**
40673          * @cfg {String} defaultDialCode The default dial code when initializing the input
40674          */
40675         defaultDialCode: '+852',
40676         
40677         /**
40678          * @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
40679          */
40680         preferedCountries: false,
40681         
40682         getAutoCreate : function()
40683         {
40684             var data = Roo.bootstrap.PhoneInputData();
40685             var align = this.labelAlign || this.parentLabelAlign();
40686             var id = Roo.id();
40687             
40688             this.allCountries = [];
40689             this.dialCodeMapping = [];
40690             
40691             for (var i = 0; i < data.length; i++) {
40692               var c = data[i];
40693               this.allCountries[i] = {
40694                 name: c[0],
40695                 iso2: c[1],
40696                 dialCode: c[2],
40697                 priority: c[3] || 0,
40698                 areaCodes: c[4] || null
40699               };
40700               this.dialCodeMapping[c[2]] = {
40701                   name: c[0],
40702                   iso2: c[1],
40703                   priority: c[3] || 0,
40704                   areaCodes: c[4] || null
40705               };
40706             }
40707             
40708             var cfg = {
40709                 cls: 'form-group',
40710                 cn: []
40711             };
40712             
40713             var input =  {
40714                 tag: 'input',
40715                 id : id,
40716                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40717                 maxlength: this.max_length,
40718                 cls : 'form-control tel-input',
40719                 autocomplete: 'new-password'
40720             };
40721             
40722             var hiddenInput = {
40723                 tag: 'input',
40724                 type: 'hidden',
40725                 cls: 'hidden-tel-input'
40726             };
40727             
40728             if (this.name) {
40729                 hiddenInput.name = this.name;
40730             }
40731             
40732             if (this.disabled) {
40733                 input.disabled = true;
40734             }
40735             
40736             var flag_container = {
40737                 tag: 'div',
40738                 cls: 'flag-box',
40739                 cn: [
40740                     {
40741                         tag: 'div',
40742                         cls: 'flag'
40743                     },
40744                     {
40745                         tag: 'div',
40746                         cls: 'caret'
40747                     }
40748                 ]
40749             };
40750             
40751             var box = {
40752                 tag: 'div',
40753                 cls: this.hasFeedback ? 'has-feedback' : '',
40754                 cn: [
40755                     hiddenInput,
40756                     input,
40757                     {
40758                         tag: 'input',
40759                         cls: 'dial-code-holder',
40760                         disabled: true
40761                     }
40762                 ]
40763             };
40764             
40765             var container = {
40766                 cls: 'roo-select2-container input-group',
40767                 cn: [
40768                     flag_container,
40769                     box
40770                 ]
40771             };
40772             
40773             if (this.fieldLabel.length) {
40774                 var indicator = {
40775                     tag: 'i',
40776                     tooltip: 'This field is required'
40777                 };
40778                 
40779                 var label = {
40780                     tag: 'label',
40781                     'for':  id,
40782                     cls: 'control-label',
40783                     cn: []
40784                 };
40785                 
40786                 var label_text = {
40787                     tag: 'span',
40788                     html: this.fieldLabel
40789                 };
40790                 
40791                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40792                 label.cn = [
40793                     indicator,
40794                     label_text
40795                 ];
40796                 
40797                 if(this.indicatorpos == 'right') {
40798                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40799                     label.cn = [
40800                         label_text,
40801                         indicator
40802                     ];
40803                 }
40804                 
40805                 if(align == 'left') {
40806                     container = {
40807                         tag: 'div',
40808                         cn: [
40809                             container
40810                         ]
40811                     };
40812                     
40813                     if(this.labelWidth > 12){
40814                         label.style = "width: " + this.labelWidth + 'px';
40815                     }
40816                     if(this.labelWidth < 13 && this.labelmd == 0){
40817                         this.labelmd = this.labelWidth;
40818                     }
40819                     if(this.labellg > 0){
40820                         label.cls += ' col-lg-' + this.labellg;
40821                         input.cls += ' col-lg-' + (12 - this.labellg);
40822                     }
40823                     if(this.labelmd > 0){
40824                         label.cls += ' col-md-' + this.labelmd;
40825                         container.cls += ' col-md-' + (12 - this.labelmd);
40826                     }
40827                     if(this.labelsm > 0){
40828                         label.cls += ' col-sm-' + this.labelsm;
40829                         container.cls += ' col-sm-' + (12 - this.labelsm);
40830                     }
40831                     if(this.labelxs > 0){
40832                         label.cls += ' col-xs-' + this.labelxs;
40833                         container.cls += ' col-xs-' + (12 - this.labelxs);
40834                     }
40835                 }
40836             }
40837             
40838             cfg.cn = [
40839                 label,
40840                 container
40841             ];
40842             
40843             var settings = this;
40844             
40845             ['xs','sm','md','lg'].map(function(size){
40846                 if (settings[size]) {
40847                     cfg.cls += ' col-' + size + '-' + settings[size];
40848                 }
40849             });
40850             
40851             this.store = new Roo.data.Store({
40852                 proxy : new Roo.data.MemoryProxy({}),
40853                 reader : new Roo.data.JsonReader({
40854                     fields : [
40855                         {
40856                             'name' : 'name',
40857                             'type' : 'string'
40858                         },
40859                         {
40860                             'name' : 'iso2',
40861                             'type' : 'string'
40862                         },
40863                         {
40864                             'name' : 'dialCode',
40865                             'type' : 'string'
40866                         },
40867                         {
40868                             'name' : 'priority',
40869                             'type' : 'string'
40870                         },
40871                         {
40872                             'name' : 'areaCodes',
40873                             'type' : 'string'
40874                         }
40875                     ]
40876                 })
40877             });
40878             
40879             if(!this.preferedCountries) {
40880                 this.preferedCountries = [
40881                     'hk',
40882                     'gb',
40883                     'us'
40884                 ];
40885             }
40886             
40887             var p = this.preferedCountries.reverse();
40888             
40889             if(p) {
40890                 for (var i = 0; i < p.length; i++) {
40891                     for (var j = 0; j < this.allCountries.length; j++) {
40892                         if(this.allCountries[j].iso2 == p[i]) {
40893                             var t = this.allCountries[j];
40894                             this.allCountries.splice(j,1);
40895                             this.allCountries.unshift(t);
40896                         }
40897                     } 
40898                 }
40899             }
40900             
40901             this.store.proxy.data = {
40902                 success: true,
40903                 data: this.allCountries
40904             };
40905             
40906             return cfg;
40907         },
40908         
40909         initEvents : function()
40910         {
40911             this.createList();
40912             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40913             
40914             this.indicator = this.indicatorEl();
40915             this.flag = this.flagEl();
40916             this.dialCodeHolder = this.dialCodeHolderEl();
40917             
40918             this.trigger = this.el.select('div.flag-box',true).first();
40919             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40920             
40921             var _this = this;
40922             
40923             (function(){
40924                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40925                 _this.list.setWidth(lw);
40926             }).defer(100);
40927             
40928             this.list.on('mouseover', this.onViewOver, this);
40929             this.list.on('mousemove', this.onViewMove, this);
40930             this.inputEl().on("keyup", this.onKeyUp, this);
40931             this.inputEl().on("keypress", this.onKeyPress, this);
40932             
40933             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40934
40935             this.view = new Roo.View(this.list, this.tpl, {
40936                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40937             });
40938             
40939             this.view.on('click', this.onViewClick, this);
40940             this.setValue(this.defaultDialCode);
40941         },
40942         
40943         onTriggerClick : function(e)
40944         {
40945             Roo.log('trigger click');
40946             if(this.disabled){
40947                 return;
40948             }
40949             
40950             if(this.isExpanded()){
40951                 this.collapse();
40952                 this.hasFocus = false;
40953             }else {
40954                 this.store.load({});
40955                 this.hasFocus = true;
40956                 this.expand();
40957             }
40958         },
40959         
40960         isExpanded : function()
40961         {
40962             return this.list.isVisible();
40963         },
40964         
40965         collapse : function()
40966         {
40967             if(!this.isExpanded()){
40968                 return;
40969             }
40970             this.list.hide();
40971             Roo.get(document).un('mousedown', this.collapseIf, this);
40972             Roo.get(document).un('mousewheel', this.collapseIf, this);
40973             this.fireEvent('collapse', this);
40974             this.validate();
40975         },
40976         
40977         expand : function()
40978         {
40979             Roo.log('expand');
40980
40981             if(this.isExpanded() || !this.hasFocus){
40982                 return;
40983             }
40984             
40985             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40986             this.list.setWidth(lw);
40987             
40988             this.list.show();
40989             this.restrictHeight();
40990             
40991             Roo.get(document).on('mousedown', this.collapseIf, this);
40992             Roo.get(document).on('mousewheel', this.collapseIf, this);
40993             
40994             this.fireEvent('expand', this);
40995         },
40996         
40997         restrictHeight : function()
40998         {
40999             this.list.alignTo(this.inputEl(), this.listAlign);
41000             this.list.alignTo(this.inputEl(), this.listAlign);
41001         },
41002         
41003         onViewOver : function(e, t)
41004         {
41005             if(this.inKeyMode){
41006                 return;
41007             }
41008             var item = this.view.findItemFromChild(t);
41009             
41010             if(item){
41011                 var index = this.view.indexOf(item);
41012                 this.select(index, false);
41013             }
41014         },
41015
41016         // private
41017         onViewClick : function(view, doFocus, el, e)
41018         {
41019             var index = this.view.getSelectedIndexes()[0];
41020             
41021             var r = this.store.getAt(index);
41022             
41023             if(r){
41024                 this.onSelect(r, index);
41025             }
41026             if(doFocus !== false && !this.blockFocus){
41027                 this.inputEl().focus();
41028             }
41029         },
41030         
41031         onViewMove : function(e, t)
41032         {
41033             this.inKeyMode = false;
41034         },
41035         
41036         select : function(index, scrollIntoView)
41037         {
41038             this.selectedIndex = index;
41039             this.view.select(index);
41040             if(scrollIntoView !== false){
41041                 var el = this.view.getNode(index);
41042                 if(el){
41043                     this.list.scrollChildIntoView(el, false);
41044                 }
41045             }
41046         },
41047         
41048         createList : function()
41049         {
41050             this.list = Roo.get(document.body).createChild({
41051                 tag: 'ul',
41052                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41053                 style: 'display:none'
41054             });
41055             
41056             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41057         },
41058         
41059         collapseIf : function(e)
41060         {
41061             var in_combo  = e.within(this.el);
41062             var in_list =  e.within(this.list);
41063             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41064             
41065             if (in_combo || in_list || is_list) {
41066                 return;
41067             }
41068             this.collapse();
41069         },
41070         
41071         onSelect : function(record, index)
41072         {
41073             if(this.fireEvent('beforeselect', this, record, index) !== false){
41074                 
41075                 this.setFlagClass(record.data.iso2);
41076                 this.setDialCode(record.data.dialCode);
41077                 this.hasFocus = false;
41078                 this.collapse();
41079                 this.fireEvent('select', this, record, index);
41080             }
41081         },
41082         
41083         flagEl : function()
41084         {
41085             var flag = this.el.select('div.flag',true).first();
41086             if(!flag){
41087                 return false;
41088             }
41089             return flag;
41090         },
41091         
41092         dialCodeHolderEl : function()
41093         {
41094             var d = this.el.select('input.dial-code-holder',true).first();
41095             if(!d){
41096                 return false;
41097             }
41098             return d;
41099         },
41100         
41101         setDialCode : function(v)
41102         {
41103             this.dialCodeHolder.dom.value = '+'+v;
41104         },
41105         
41106         setFlagClass : function(n)
41107         {
41108             this.flag.dom.className = 'flag '+n;
41109         },
41110         
41111         getValue : function()
41112         {
41113             var v = this.inputEl().getValue();
41114             if(this.dialCodeHolder) {
41115                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41116             }
41117             return v;
41118         },
41119         
41120         setValue : function(v)
41121         {
41122             var d = this.getDialCode(v);
41123             
41124             //invalid dial code
41125             if(v.length == 0 || !d || d.length == 0) {
41126                 if(this.rendered){
41127                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41128                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41129                 }
41130                 return;
41131             }
41132             
41133             //valid dial code
41134             this.setFlagClass(this.dialCodeMapping[d].iso2);
41135             this.setDialCode(d);
41136             this.inputEl().dom.value = v.replace('+'+d,'');
41137             this.hiddenEl().dom.value = this.getValue();
41138             
41139             this.validate();
41140         },
41141         
41142         getDialCode : function(v)
41143         {
41144             v = v ||  '';
41145             
41146             if (v.length == 0) {
41147                 return this.dialCodeHolder.dom.value;
41148             }
41149             
41150             var dialCode = "";
41151             if (v.charAt(0) != "+") {
41152                 return false;
41153             }
41154             var numericChars = "";
41155             for (var i = 1; i < v.length; i++) {
41156               var c = v.charAt(i);
41157               if (!isNaN(c)) {
41158                 numericChars += c;
41159                 if (this.dialCodeMapping[numericChars]) {
41160                   dialCode = v.substr(1, i);
41161                 }
41162                 if (numericChars.length == 4) {
41163                   break;
41164                 }
41165               }
41166             }
41167             return dialCode;
41168         },
41169         
41170         reset : function()
41171         {
41172             this.setValue(this.defaultDialCode);
41173             this.validate();
41174         },
41175         
41176         hiddenEl : function()
41177         {
41178             return this.el.select('input.hidden-tel-input',true).first();
41179         },
41180         
41181         // after setting val
41182         onKeyUp : function(e){
41183             this.setValue(this.getValue());
41184         },
41185         
41186         onKeyPress : function(e){
41187             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41188                 e.stopEvent();
41189             }
41190         }
41191         
41192 });
41193 /**
41194  * @class Roo.bootstrap.MoneyField
41195  * @extends Roo.bootstrap.ComboBox
41196  * Bootstrap MoneyField class
41197  * 
41198  * @constructor
41199  * Create a new MoneyField.
41200  * @param {Object} config Configuration options
41201  */
41202
41203 Roo.bootstrap.MoneyField = function(config) {
41204     
41205     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41206     
41207 };
41208
41209 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41210     
41211     /**
41212      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41213      */
41214     allowDecimals : true,
41215     /**
41216      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41217      */
41218     decimalSeparator : ".",
41219     /**
41220      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41221      */
41222     decimalPrecision : 0,
41223     /**
41224      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41225      */
41226     allowNegative : true,
41227     /**
41228      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41229      */
41230     allowZero: true,
41231     /**
41232      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41233      */
41234     minValue : Number.NEGATIVE_INFINITY,
41235     /**
41236      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41237      */
41238     maxValue : Number.MAX_VALUE,
41239     /**
41240      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41241      */
41242     minText : "The minimum value for this field is {0}",
41243     /**
41244      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41245      */
41246     maxText : "The maximum value for this field is {0}",
41247     /**
41248      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41249      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41250      */
41251     nanText : "{0} is not a valid number",
41252     /**
41253      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41254      */
41255     castInt : true,
41256     /**
41257      * @cfg {String} defaults currency of the MoneyField
41258      * value should be in lkey
41259      */
41260     defaultCurrency : false,
41261     /**
41262      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41263      */
41264     thousandsDelimiter : false,
41265     /**
41266      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41267      */
41268     max_length: false,
41269     
41270     inputlg : 9,
41271     inputmd : 9,
41272     inputsm : 9,
41273     inputxs : 6,
41274     
41275     store : false,
41276     
41277     getAutoCreate : function()
41278     {
41279         var align = this.labelAlign || this.parentLabelAlign();
41280         
41281         var id = Roo.id();
41282
41283         var cfg = {
41284             cls: 'form-group',
41285             cn: []
41286         };
41287
41288         var input =  {
41289             tag: 'input',
41290             id : id,
41291             cls : 'form-control roo-money-amount-input',
41292             autocomplete: 'new-password'
41293         };
41294         
41295         var hiddenInput = {
41296             tag: 'input',
41297             type: 'hidden',
41298             id: Roo.id(),
41299             cls: 'hidden-number-input'
41300         };
41301         
41302         if(this.max_length) {
41303             input.maxlength = this.max_length; 
41304         }
41305         
41306         if (this.name) {
41307             hiddenInput.name = this.name;
41308         }
41309
41310         if (this.disabled) {
41311             input.disabled = true;
41312         }
41313
41314         var clg = 12 - this.inputlg;
41315         var cmd = 12 - this.inputmd;
41316         var csm = 12 - this.inputsm;
41317         var cxs = 12 - this.inputxs;
41318         
41319         var container = {
41320             tag : 'div',
41321             cls : 'row roo-money-field',
41322             cn : [
41323                 {
41324                     tag : 'div',
41325                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41326                     cn : [
41327                         {
41328                             tag : 'div',
41329                             cls: 'roo-select2-container input-group',
41330                             cn: [
41331                                 {
41332                                     tag : 'input',
41333                                     cls : 'form-control roo-money-currency-input',
41334                                     autocomplete: 'new-password',
41335                                     readOnly : 1,
41336                                     name : this.currencyName
41337                                 },
41338                                 {
41339                                     tag :'span',
41340                                     cls : 'input-group-addon',
41341                                     cn : [
41342                                         {
41343                                             tag: 'span',
41344                                             cls: 'caret'
41345                                         }
41346                                     ]
41347                                 }
41348                             ]
41349                         }
41350                     ]
41351                 },
41352                 {
41353                     tag : 'div',
41354                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41355                     cn : [
41356                         {
41357                             tag: 'div',
41358                             cls: this.hasFeedback ? 'has-feedback' : '',
41359                             cn: [
41360                                 input
41361                             ]
41362                         }
41363                     ]
41364                 }
41365             ]
41366             
41367         };
41368         
41369         if (this.fieldLabel.length) {
41370             var indicator = {
41371                 tag: 'i',
41372                 tooltip: 'This field is required'
41373             };
41374
41375             var label = {
41376                 tag: 'label',
41377                 'for':  id,
41378                 cls: 'control-label',
41379                 cn: []
41380             };
41381
41382             var label_text = {
41383                 tag: 'span',
41384                 html: this.fieldLabel
41385             };
41386
41387             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41388             label.cn = [
41389                 indicator,
41390                 label_text
41391             ];
41392
41393             if(this.indicatorpos == 'right') {
41394                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41395                 label.cn = [
41396                     label_text,
41397                     indicator
41398                 ];
41399             }
41400
41401             if(align == 'left') {
41402                 container = {
41403                     tag: 'div',
41404                     cn: [
41405                         container
41406                     ]
41407                 };
41408
41409                 if(this.labelWidth > 12){
41410                     label.style = "width: " + this.labelWidth + 'px';
41411                 }
41412                 if(this.labelWidth < 13 && this.labelmd == 0){
41413                     this.labelmd = this.labelWidth;
41414                 }
41415                 if(this.labellg > 0){
41416                     label.cls += ' col-lg-' + this.labellg;
41417                     input.cls += ' col-lg-' + (12 - this.labellg);
41418                 }
41419                 if(this.labelmd > 0){
41420                     label.cls += ' col-md-' + this.labelmd;
41421                     container.cls += ' col-md-' + (12 - this.labelmd);
41422                 }
41423                 if(this.labelsm > 0){
41424                     label.cls += ' col-sm-' + this.labelsm;
41425                     container.cls += ' col-sm-' + (12 - this.labelsm);
41426                 }
41427                 if(this.labelxs > 0){
41428                     label.cls += ' col-xs-' + this.labelxs;
41429                     container.cls += ' col-xs-' + (12 - this.labelxs);
41430                 }
41431             }
41432         }
41433
41434         cfg.cn = [
41435             label,
41436             container,
41437             hiddenInput
41438         ];
41439         
41440         var settings = this;
41441
41442         ['xs','sm','md','lg'].map(function(size){
41443             if (settings[size]) {
41444                 cfg.cls += ' col-' + size + '-' + settings[size];
41445             }
41446         });
41447         
41448         return cfg;
41449     },
41450     
41451     initEvents : function()
41452     {
41453         this.indicator = this.indicatorEl();
41454         
41455         this.initCurrencyEvent();
41456         
41457         this.initNumberEvent();
41458     },
41459     
41460     initCurrencyEvent : function()
41461     {
41462         if (!this.store) {
41463             throw "can not find store for combo";
41464         }
41465         
41466         this.store = Roo.factory(this.store, Roo.data);
41467         this.store.parent = this;
41468         
41469         this.createList();
41470         
41471         this.triggerEl = this.el.select('.input-group-addon', true).first();
41472         
41473         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41474         
41475         var _this = this;
41476         
41477         (function(){
41478             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41479             _this.list.setWidth(lw);
41480         }).defer(100);
41481         
41482         this.list.on('mouseover', this.onViewOver, this);
41483         this.list.on('mousemove', this.onViewMove, this);
41484         this.list.on('scroll', this.onViewScroll, this);
41485         
41486         if(!this.tpl){
41487             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41488         }
41489         
41490         this.view = new Roo.View(this.list, this.tpl, {
41491             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41492         });
41493         
41494         this.view.on('click', this.onViewClick, this);
41495         
41496         this.store.on('beforeload', this.onBeforeLoad, this);
41497         this.store.on('load', this.onLoad, this);
41498         this.store.on('loadexception', this.onLoadException, this);
41499         
41500         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41501             "up" : function(e){
41502                 this.inKeyMode = true;
41503                 this.selectPrev();
41504             },
41505
41506             "down" : function(e){
41507                 if(!this.isExpanded()){
41508                     this.onTriggerClick();
41509                 }else{
41510                     this.inKeyMode = true;
41511                     this.selectNext();
41512                 }
41513             },
41514
41515             "enter" : function(e){
41516                 this.collapse();
41517                 
41518                 if(this.fireEvent("specialkey", this, e)){
41519                     this.onViewClick(false);
41520                 }
41521                 
41522                 return true;
41523             },
41524
41525             "esc" : function(e){
41526                 this.collapse();
41527             },
41528
41529             "tab" : function(e){
41530                 this.collapse();
41531                 
41532                 if(this.fireEvent("specialkey", this, e)){
41533                     this.onViewClick(false);
41534                 }
41535                 
41536                 return true;
41537             },
41538
41539             scope : this,
41540
41541             doRelay : function(foo, bar, hname){
41542                 if(hname == 'down' || this.scope.isExpanded()){
41543                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41544                 }
41545                 return true;
41546             },
41547
41548             forceKeyDown: true
41549         });
41550         
41551         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41552         
41553     },
41554     
41555     initNumberEvent : function(e)
41556     {
41557         this.inputEl().on("keydown" , this.fireKey,  this);
41558         this.inputEl().on("focus", this.onFocus,  this);
41559         this.inputEl().on("blur", this.onBlur,  this);
41560         
41561         this.inputEl().relayEvent('keyup', this);
41562         
41563         if(this.indicator){
41564             this.indicator.addClass('invisible');
41565         }
41566  
41567         this.originalValue = this.getValue();
41568         
41569         if(this.validationEvent == 'keyup'){
41570             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41571             this.inputEl().on('keyup', this.filterValidation, this);
41572         }
41573         else if(this.validationEvent !== false){
41574             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41575         }
41576         
41577         if(this.selectOnFocus){
41578             this.on("focus", this.preFocus, this);
41579             
41580         }
41581         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41582             this.inputEl().on("keypress", this.filterKeys, this);
41583         } else {
41584             this.inputEl().relayEvent('keypress', this);
41585         }
41586         
41587         var allowed = "0123456789";
41588         
41589         if(this.allowDecimals){
41590             allowed += this.decimalSeparator;
41591         }
41592         
41593         if(this.allowNegative){
41594             allowed += "-";
41595         }
41596         
41597         if(this.thousandsDelimiter) {
41598             allowed += ",";
41599         }
41600         
41601         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41602         
41603         var keyPress = function(e){
41604             
41605             var k = e.getKey();
41606             
41607             var c = e.getCharCode();
41608             
41609             if(
41610                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41611                     allowed.indexOf(String.fromCharCode(c)) === -1
41612             ){
41613                 e.stopEvent();
41614                 return;
41615             }
41616             
41617             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41618                 return;
41619             }
41620             
41621             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41622                 e.stopEvent();
41623             }
41624         };
41625         
41626         this.inputEl().on("keypress", keyPress, this);
41627         
41628     },
41629     
41630     onTriggerClick : function(e)
41631     {   
41632         if(this.disabled){
41633             return;
41634         }
41635         
41636         this.page = 0;
41637         this.loadNext = false;
41638         
41639         if(this.isExpanded()){
41640             this.collapse();
41641             return;
41642         }
41643         
41644         this.hasFocus = true;
41645         
41646         if(this.triggerAction == 'all') {
41647             this.doQuery(this.allQuery, true);
41648             return;
41649         }
41650         
41651         this.doQuery(this.getRawValue());
41652     },
41653     
41654     getCurrency : function()
41655     {   
41656         var v = this.currencyEl().getValue();
41657         
41658         return v;
41659     },
41660     
41661     restrictHeight : function()
41662     {
41663         this.list.alignTo(this.currencyEl(), this.listAlign);
41664         this.list.alignTo(this.currencyEl(), this.listAlign);
41665     },
41666     
41667     onViewClick : function(view, doFocus, el, e)
41668     {
41669         var index = this.view.getSelectedIndexes()[0];
41670         
41671         var r = this.store.getAt(index);
41672         
41673         if(r){
41674             this.onSelect(r, index);
41675         }
41676     },
41677     
41678     onSelect : function(record, index){
41679         
41680         if(this.fireEvent('beforeselect', this, record, index) !== false){
41681         
41682             this.setFromCurrencyData(index > -1 ? record.data : false);
41683             
41684             this.collapse();
41685             
41686             this.fireEvent('select', this, record, index);
41687         }
41688     },
41689     
41690     setFromCurrencyData : function(o)
41691     {
41692         var currency = '';
41693         
41694         this.lastCurrency = o;
41695         
41696         if (this.currencyField) {
41697             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41698         } else {
41699             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41700         }
41701         
41702         this.lastSelectionText = currency;
41703         
41704         //setting default currency
41705         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41706             this.setCurrency(this.defaultCurrency);
41707             return;
41708         }
41709         
41710         this.setCurrency(currency);
41711     },
41712     
41713     setFromData : function(o)
41714     {
41715         var c = {};
41716         
41717         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41718         
41719         this.setFromCurrencyData(c);
41720         
41721         var value = '';
41722         
41723         if (this.name) {
41724             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41725         } else {
41726             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41727         }
41728         
41729         this.setValue(value);
41730         
41731     },
41732     
41733     setCurrency : function(v)
41734     {   
41735         this.currencyValue = v;
41736         
41737         if(this.rendered){
41738             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41739             this.validate();
41740         }
41741     },
41742     
41743     setValue : function(v)
41744     {
41745         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41746         
41747         this.value = v;
41748         
41749         if(this.rendered){
41750             
41751             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41752             
41753             this.inputEl().dom.value = (v == '') ? '' :
41754                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41755             
41756             if(!this.allowZero && v === '0') {
41757                 this.hiddenEl().dom.value = '';
41758                 this.inputEl().dom.value = '';
41759             }
41760             
41761             this.validate();
41762         }
41763     },
41764     
41765     getRawValue : function()
41766     {
41767         var v = this.inputEl().getValue();
41768         
41769         return v;
41770     },
41771     
41772     getValue : function()
41773     {
41774         return this.fixPrecision(this.parseValue(this.getRawValue()));
41775     },
41776     
41777     parseValue : function(value)
41778     {
41779         if(this.thousandsDelimiter) {
41780             value += "";
41781             r = new RegExp(",", "g");
41782             value = value.replace(r, "");
41783         }
41784         
41785         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41786         return isNaN(value) ? '' : value;
41787         
41788     },
41789     
41790     fixPrecision : function(value)
41791     {
41792         if(this.thousandsDelimiter) {
41793             value += "";
41794             r = new RegExp(",", "g");
41795             value = value.replace(r, "");
41796         }
41797         
41798         var nan = isNaN(value);
41799         
41800         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41801             return nan ? '' : value;
41802         }
41803         return parseFloat(value).toFixed(this.decimalPrecision);
41804     },
41805     
41806     decimalPrecisionFcn : function(v)
41807     {
41808         return Math.floor(v);
41809     },
41810     
41811     validateValue : function(value)
41812     {
41813         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41814             return false;
41815         }
41816         
41817         var num = this.parseValue(value);
41818         
41819         if(isNaN(num)){
41820             this.markInvalid(String.format(this.nanText, value));
41821             return false;
41822         }
41823         
41824         if(num < this.minValue){
41825             this.markInvalid(String.format(this.minText, this.minValue));
41826             return false;
41827         }
41828         
41829         if(num > this.maxValue){
41830             this.markInvalid(String.format(this.maxText, this.maxValue));
41831             return false;
41832         }
41833         
41834         return true;
41835     },
41836     
41837     validate : function()
41838     {
41839         if(this.disabled || this.allowBlank){
41840             this.markValid();
41841             return true;
41842         }
41843         
41844         var currency = this.getCurrency();
41845         
41846         if(this.validateValue(this.getRawValue()) && currency.length){
41847             this.markValid();
41848             return true;
41849         }
41850         
41851         this.markInvalid();
41852         return false;
41853     },
41854     
41855     getName: function()
41856     {
41857         return this.name;
41858     },
41859     
41860     beforeBlur : function()
41861     {
41862         if(!this.castInt){
41863             return;
41864         }
41865         
41866         var v = this.parseValue(this.getRawValue());
41867         
41868         if(v || v == 0){
41869             this.setValue(v);
41870         }
41871     },
41872     
41873     onBlur : function()
41874     {
41875         this.beforeBlur();
41876         
41877         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41878             //this.el.removeClass(this.focusClass);
41879         }
41880         
41881         this.hasFocus = false;
41882         
41883         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41884             this.validate();
41885         }
41886         
41887         var v = this.getValue();
41888         
41889         if(String(v) !== String(this.startValue)){
41890             this.fireEvent('change', this, v, this.startValue);
41891         }
41892         
41893         this.fireEvent("blur", this);
41894     },
41895     
41896     inputEl : function()
41897     {
41898         return this.el.select('.roo-money-amount-input', true).first();
41899     },
41900     
41901     currencyEl : function()
41902     {
41903         return this.el.select('.roo-money-currency-input', true).first();
41904     },
41905     
41906     hiddenEl : function()
41907     {
41908         return this.el.select('input.hidden-number-input',true).first();
41909     }
41910     
41911 });/**
41912  * @class Roo.bootstrap.BezierSignature
41913  * @extends Roo.bootstrap.Component
41914  * Bootstrap BezierSignature class
41915  * This script refer to:
41916  *    Title: Signature Pad
41917  *    Author: szimek
41918  *    Availability: https://github.com/szimek/signature_pad
41919  *
41920  * @constructor
41921  * Create a new BezierSignature
41922  * @param {Object} config The config object
41923  */
41924
41925 Roo.bootstrap.BezierSignature = function(config){
41926     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41927     this.addEvents({
41928         "resize" : true
41929     });
41930 };
41931
41932 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41933 {
41934      
41935     curve_data: [],
41936     
41937     is_empty: true,
41938     
41939     mouse_btn_down: true,
41940     
41941     /**
41942      * @cfg {int} canvas height
41943      */
41944     canvas_height: '200px',
41945     
41946     /**
41947      * @cfg {float|function} Radius of a single dot.
41948      */ 
41949     dot_size: false,
41950     
41951     /**
41952      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41953      */
41954     min_width: 0.5,
41955     
41956     /**
41957      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41958      */
41959     max_width: 2.5,
41960     
41961     /**
41962      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41963      */
41964     throttle: 16,
41965     
41966     /**
41967      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41968      */
41969     min_distance: 5,
41970     
41971     /**
41972      * @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.
41973      */
41974     bg_color: 'rgba(0, 0, 0, 0)',
41975     
41976     /**
41977      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41978      */
41979     dot_color: 'black',
41980     
41981     /**
41982      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41983      */ 
41984     velocity_filter_weight: 0.7,
41985     
41986     /**
41987      * @cfg {function} Callback when stroke begin. 
41988      */
41989     onBegin: false,
41990     
41991     /**
41992      * @cfg {function} Callback when stroke end.
41993      */
41994     onEnd: false,
41995     
41996     getAutoCreate : function()
41997     {
41998         var cls = 'roo-signature column';
41999         
42000         if(this.cls){
42001             cls += ' ' + this.cls;
42002         }
42003         
42004         var col_sizes = [
42005             'lg',
42006             'md',
42007             'sm',
42008             'xs'
42009         ];
42010         
42011         for(var i = 0; i < col_sizes.length; i++) {
42012             if(this[col_sizes[i]]) {
42013                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42014             }
42015         }
42016         
42017         var cfg = {
42018             tag: 'div',
42019             cls: cls,
42020             cn: [
42021                 {
42022                     tag: 'div',
42023                     cls: 'roo-signature-body',
42024                     cn: [
42025                         {
42026                             tag: 'canvas',
42027                             cls: 'roo-signature-body-canvas',
42028                             height: this.canvas_height,
42029                             width: this.canvas_width
42030                         }
42031                     ]
42032                 },
42033                 {
42034                     tag: 'input',
42035                     type: 'file',
42036                     style: 'display: none'
42037                 }
42038             ]
42039         };
42040         
42041         return cfg;
42042     },
42043     
42044     initEvents: function() 
42045     {
42046         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42047         
42048         var canvas = this.canvasEl();
42049         
42050         // mouse && touch event swapping...
42051         canvas.dom.style.touchAction = 'none';
42052         canvas.dom.style.msTouchAction = 'none';
42053         
42054         this.mouse_btn_down = false;
42055         canvas.on('mousedown', this._handleMouseDown, this);
42056         canvas.on('mousemove', this._handleMouseMove, this);
42057         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42058         
42059         if (window.PointerEvent) {
42060             canvas.on('pointerdown', this._handleMouseDown, this);
42061             canvas.on('pointermove', this._handleMouseMove, this);
42062             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42063         }
42064         
42065         if ('ontouchstart' in window) {
42066             canvas.on('touchstart', this._handleTouchStart, this);
42067             canvas.on('touchmove', this._handleTouchMove, this);
42068             canvas.on('touchend', this._handleTouchEnd, this);
42069         }
42070         
42071         Roo.EventManager.onWindowResize(this.resize, this, true);
42072         
42073         // file input event
42074         this.fileEl().on('change', this.uploadImage, this);
42075         
42076         this.clear();
42077         
42078         this.resize();
42079     },
42080     
42081     resize: function(){
42082         
42083         var canvas = this.canvasEl().dom;
42084         var ctx = this.canvasElCtx();
42085         var img_data = false;
42086         
42087         if(canvas.width > 0) {
42088             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42089         }
42090         // setting canvas width will clean img data
42091         canvas.width = 0;
42092         
42093         var style = window.getComputedStyle ? 
42094             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42095             
42096         var padding_left = parseInt(style.paddingLeft) || 0;
42097         var padding_right = parseInt(style.paddingRight) || 0;
42098         
42099         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42100         
42101         if(img_data) {
42102             ctx.putImageData(img_data, 0, 0);
42103         }
42104     },
42105     
42106     _handleMouseDown: function(e)
42107     {
42108         if (e.browserEvent.which === 1) {
42109             this.mouse_btn_down = true;
42110             this.strokeBegin(e);
42111         }
42112     },
42113     
42114     _handleMouseMove: function (e)
42115     {
42116         if (this.mouse_btn_down) {
42117             this.strokeMoveUpdate(e);
42118         }
42119     },
42120     
42121     _handleMouseUp: function (e)
42122     {
42123         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42124             this.mouse_btn_down = false;
42125             this.strokeEnd(e);
42126         }
42127     },
42128     
42129     _handleTouchStart: function (e) {
42130         
42131         e.preventDefault();
42132         if (e.browserEvent.targetTouches.length === 1) {
42133             // var touch = e.browserEvent.changedTouches[0];
42134             // this.strokeBegin(touch);
42135             
42136              this.strokeBegin(e); // assume e catching the correct xy...
42137         }
42138     },
42139     
42140     _handleTouchMove: function (e) {
42141         e.preventDefault();
42142         // var touch = event.targetTouches[0];
42143         // _this._strokeMoveUpdate(touch);
42144         this.strokeMoveUpdate(e);
42145     },
42146     
42147     _handleTouchEnd: function (e) {
42148         var wasCanvasTouched = e.target === this.canvasEl().dom;
42149         if (wasCanvasTouched) {
42150             e.preventDefault();
42151             // var touch = event.changedTouches[0];
42152             // _this._strokeEnd(touch);
42153             this.strokeEnd(e);
42154         }
42155     },
42156     
42157     reset: function () {
42158         this._lastPoints = [];
42159         this._lastVelocity = 0;
42160         this._lastWidth = (this.min_width + this.max_width) / 2;
42161         this.canvasElCtx().fillStyle = this.dot_color;
42162     },
42163     
42164     strokeMoveUpdate: function(e)
42165     {
42166         this.strokeUpdate(e);
42167         
42168         if (this.throttle) {
42169             this.throttleStroke(this.strokeUpdate, this.throttle);
42170         }
42171         else {
42172             this.strokeUpdate(e);
42173         }
42174     },
42175     
42176     strokeBegin: function(e)
42177     {
42178         var newPointGroup = {
42179             color: this.dot_color,
42180             points: []
42181         };
42182         
42183         if (typeof this.onBegin === 'function') {
42184             this.onBegin(e);
42185         }
42186         
42187         this.curve_data.push(newPointGroup);
42188         this.reset();
42189         this.strokeUpdate(e);
42190     },
42191     
42192     strokeUpdate: function(e)
42193     {
42194         var rect = this.canvasEl().dom.getBoundingClientRect();
42195         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42196         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42197         var lastPoints = lastPointGroup.points;
42198         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42199         var isLastPointTooClose = lastPoint
42200             ? point.distanceTo(lastPoint) <= this.min_distance
42201             : false;
42202         var color = lastPointGroup.color;
42203         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42204             var curve = this.addPoint(point);
42205             if (!lastPoint) {
42206                 this.drawDot({color: color, point: point});
42207             }
42208             else if (curve) {
42209                 this.drawCurve({color: color, curve: curve});
42210             }
42211             lastPoints.push({
42212                 time: point.time,
42213                 x: point.x,
42214                 y: point.y
42215             });
42216         }
42217     },
42218     
42219     strokeEnd: function(e)
42220     {
42221         this.strokeUpdate(e);
42222         if (typeof this.onEnd === 'function') {
42223             this.onEnd(e);
42224         }
42225     },
42226     
42227     addPoint:  function (point) {
42228         var _lastPoints = this._lastPoints;
42229         _lastPoints.push(point);
42230         if (_lastPoints.length > 2) {
42231             if (_lastPoints.length === 3) {
42232                 _lastPoints.unshift(_lastPoints[0]);
42233             }
42234             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42235             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42236             _lastPoints.shift();
42237             return curve;
42238         }
42239         return null;
42240     },
42241     
42242     calculateCurveWidths: function (startPoint, endPoint) {
42243         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42244             (1 - this.velocity_filter_weight) * this._lastVelocity;
42245
42246         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42247         var widths = {
42248             end: newWidth,
42249             start: this._lastWidth
42250         };
42251         
42252         this._lastVelocity = velocity;
42253         this._lastWidth = newWidth;
42254         return widths;
42255     },
42256     
42257     drawDot: function (_a) {
42258         var color = _a.color, point = _a.point;
42259         var ctx = this.canvasElCtx();
42260         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42261         ctx.beginPath();
42262         this.drawCurveSegment(point.x, point.y, width);
42263         ctx.closePath();
42264         ctx.fillStyle = color;
42265         ctx.fill();
42266     },
42267     
42268     drawCurve: function (_a) {
42269         var color = _a.color, curve = _a.curve;
42270         var ctx = this.canvasElCtx();
42271         var widthDelta = curve.endWidth - curve.startWidth;
42272         var drawSteps = Math.floor(curve.length()) * 2;
42273         ctx.beginPath();
42274         ctx.fillStyle = color;
42275         for (var i = 0; i < drawSteps; i += 1) {
42276         var t = i / drawSteps;
42277         var tt = t * t;
42278         var ttt = tt * t;
42279         var u = 1 - t;
42280         var uu = u * u;
42281         var uuu = uu * u;
42282         var x = uuu * curve.startPoint.x;
42283         x += 3 * uu * t * curve.control1.x;
42284         x += 3 * u * tt * curve.control2.x;
42285         x += ttt * curve.endPoint.x;
42286         var y = uuu * curve.startPoint.y;
42287         y += 3 * uu * t * curve.control1.y;
42288         y += 3 * u * tt * curve.control2.y;
42289         y += ttt * curve.endPoint.y;
42290         var width = curve.startWidth + ttt * widthDelta;
42291         this.drawCurveSegment(x, y, width);
42292         }
42293         ctx.closePath();
42294         ctx.fill();
42295     },
42296     
42297     drawCurveSegment: function (x, y, width) {
42298         var ctx = this.canvasElCtx();
42299         ctx.moveTo(x, y);
42300         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42301         this.is_empty = false;
42302     },
42303     
42304     clear: function()
42305     {
42306         var ctx = this.canvasElCtx();
42307         var canvas = this.canvasEl().dom;
42308         ctx.fillStyle = this.bg_color;
42309         ctx.clearRect(0, 0, canvas.width, canvas.height);
42310         ctx.fillRect(0, 0, canvas.width, canvas.height);
42311         this.curve_data = [];
42312         this.reset();
42313         this.is_empty = true;
42314     },
42315     
42316     fileEl: function()
42317     {
42318         return  this.el.select('input',true).first();
42319     },
42320     
42321     canvasEl: function()
42322     {
42323         return this.el.select('canvas',true).first();
42324     },
42325     
42326     canvasElCtx: function()
42327     {
42328         return this.el.select('canvas',true).first().dom.getContext('2d');
42329     },
42330     
42331     getImage: function(type)
42332     {
42333         if(this.is_empty) {
42334             return false;
42335         }
42336         
42337         // encryption ?
42338         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42339     },
42340     
42341     drawFromImage: function(img_src)
42342     {
42343         var img = new Image();
42344         
42345         img.onload = function(){
42346             this.canvasElCtx().drawImage(img, 0, 0);
42347         }.bind(this);
42348         
42349         img.src = img_src;
42350         
42351         this.is_empty = false;
42352     },
42353     
42354     selectImage: function()
42355     {
42356         this.fileEl().dom.click();
42357     },
42358     
42359     uploadImage: function(e)
42360     {
42361         var reader = new FileReader();
42362         
42363         reader.onload = function(e){
42364             var img = new Image();
42365             img.onload = function(){
42366                 this.reset();
42367                 this.canvasElCtx().drawImage(img, 0, 0);
42368             }.bind(this);
42369             img.src = e.target.result;
42370         }.bind(this);
42371         
42372         reader.readAsDataURL(e.target.files[0]);
42373     },
42374     
42375     // Bezier Point Constructor
42376     Point: (function () {
42377         function Point(x, y, time) {
42378             this.x = x;
42379             this.y = y;
42380             this.time = time || Date.now();
42381         }
42382         Point.prototype.distanceTo = function (start) {
42383             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42384         };
42385         Point.prototype.equals = function (other) {
42386             return this.x === other.x && this.y === other.y && this.time === other.time;
42387         };
42388         Point.prototype.velocityFrom = function (start) {
42389             return this.time !== start.time
42390             ? this.distanceTo(start) / (this.time - start.time)
42391             : 0;
42392         };
42393         return Point;
42394     }()),
42395     
42396     
42397     // Bezier Constructor
42398     Bezier: (function () {
42399         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42400             this.startPoint = startPoint;
42401             this.control2 = control2;
42402             this.control1 = control1;
42403             this.endPoint = endPoint;
42404             this.startWidth = startWidth;
42405             this.endWidth = endWidth;
42406         }
42407         Bezier.fromPoints = function (points, widths, scope) {
42408             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42409             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42410             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42411         };
42412         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42413             var dx1 = s1.x - s2.x;
42414             var dy1 = s1.y - s2.y;
42415             var dx2 = s2.x - s3.x;
42416             var dy2 = s2.y - s3.y;
42417             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42418             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42419             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42420             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42421             var dxm = m1.x - m2.x;
42422             var dym = m1.y - m2.y;
42423             var k = l2 / (l1 + l2);
42424             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42425             var tx = s2.x - cm.x;
42426             var ty = s2.y - cm.y;
42427             return {
42428                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42429                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42430             };
42431         };
42432         Bezier.prototype.length = function () {
42433             var steps = 10;
42434             var length = 0;
42435             var px;
42436             var py;
42437             for (var i = 0; i <= steps; i += 1) {
42438                 var t = i / steps;
42439                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42440                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42441                 if (i > 0) {
42442                     var xdiff = cx - px;
42443                     var ydiff = cy - py;
42444                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42445                 }
42446                 px = cx;
42447                 py = cy;
42448             }
42449             return length;
42450         };
42451         Bezier.prototype.point = function (t, start, c1, c2, end) {
42452             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42453             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42454             + (3.0 * c2 * (1.0 - t) * t * t)
42455             + (end * t * t * t);
42456         };
42457         return Bezier;
42458     }()),
42459     
42460     throttleStroke: function(fn, wait) {
42461       if (wait === void 0) { wait = 250; }
42462       var previous = 0;
42463       var timeout = null;
42464       var result;
42465       var storedContext;
42466       var storedArgs;
42467       var later = function () {
42468           previous = Date.now();
42469           timeout = null;
42470           result = fn.apply(storedContext, storedArgs);
42471           if (!timeout) {
42472               storedContext = null;
42473               storedArgs = [];
42474           }
42475       };
42476       return function wrapper() {
42477           var args = [];
42478           for (var _i = 0; _i < arguments.length; _i++) {
42479               args[_i] = arguments[_i];
42480           }
42481           var now = Date.now();
42482           var remaining = wait - (now - previous);
42483           storedContext = this;
42484           storedArgs = args;
42485           if (remaining <= 0 || remaining > wait) {
42486               if (timeout) {
42487                   clearTimeout(timeout);
42488                   timeout = null;
42489               }
42490               previous = now;
42491               result = fn.apply(storedContext, storedArgs);
42492               if (!timeout) {
42493                   storedContext = null;
42494                   storedArgs = [];
42495               }
42496           }
42497           else if (!timeout) {
42498               timeout = window.setTimeout(later, remaining);
42499           }
42500           return result;
42501       };
42502   }
42503   
42504 });
42505
42506  
42507
42508