Roo/bootstrap/Navbar.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item dropdown-item'
2517             };
2518         }
2519         var ctag = {
2520             tag: 'span',
2521             html: 'Link'
2522         };
2523         
2524         var anc = {
2525             tag : 'a',
2526             href : '#',
2527             cn : [  ]
2528         };
2529         
2530         if (this.fa !== false) {
2531             anc.cn.push({
2532                 tag : 'i',
2533                 cls : 'fa fa-' + this.fa
2534             });
2535         }
2536         
2537         anc.cn.push(ctag);
2538         
2539         
2540         var cfg= {
2541             tag: 'li',
2542             cls: 'dropdown-menu-item dropdown-item',
2543             cn: [ anc ]
2544         };
2545         if (this.parent().type == 'treeview') {
2546             cfg.cls = 'treeview-menu';
2547         }
2548         if (this.active) {
2549             cfg.cls += ' active';
2550         }
2551         
2552         
2553         
2554         anc.href = this.href || cfg.cn[0].href ;
2555         ctag.html = this.html || cfg.cn[0].html ;
2556         return cfg;
2557     },
2558     
2559     initEvents: function()
2560     {
2561         if (this.parent().type == 'treeview') {
2562             this.el.select('a').on('click', this.onClick, this);
2563         }
2564         
2565         if (this.menu) {
2566             this.menu.parentType = this.xtype;
2567             this.menu.triggerEl = this.el;
2568             this.menu = this.addxtype(Roo.apply({}, this.menu));
2569         }
2570         
2571     },
2572     onClick : function(e)
2573     {
2574         Roo.log('item on click ');
2575         
2576         if(this.preventDefault){
2577             e.preventDefault();
2578         }
2579         //this.parent().hideMenuItems();
2580         
2581         this.fireEvent('click', this, e);
2582     },
2583     getEl : function()
2584     {
2585         return this.el;
2586     } 
2587 });
2588
2589  
2590
2591  /*
2592  * - LGPL
2593  *
2594  * menu separator
2595  * 
2596  */
2597
2598
2599 /**
2600  * @class Roo.bootstrap.MenuSeparator
2601  * @extends Roo.bootstrap.Component
2602  * Bootstrap MenuSeparator class
2603  * 
2604  * @constructor
2605  * Create a new MenuItem
2606  * @param {Object} config The config object
2607  */
2608
2609
2610 Roo.bootstrap.MenuSeparator = function(config){
2611     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2612 };
2613
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2615     
2616     getAutoCreate : function(){
2617         var cfg = {
2618             cls: 'divider',
2619             tag : 'li'
2620         };
2621         
2622         return cfg;
2623     }
2624    
2625 });
2626
2627  
2628
2629  
2630 /*
2631 * Licence: LGPL
2632 */
2633
2634 /**
2635  * @class Roo.bootstrap.Modal
2636  * @extends Roo.bootstrap.Component
2637  * Bootstrap Modal class
2638  * @cfg {String} title Title of dialog
2639  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2641  * @cfg {Boolean} specificTitle default false
2642  * @cfg {Array} buttons Array of buttons or standard button set..
2643  * @cfg {String} buttonPosition (left|right|center) default right
2644  * @cfg {Boolean} animate default true
2645  * @cfg {Boolean} allow_close default true
2646  * @cfg {Boolean} fitwindow default false
2647  * @cfg {String} size (sm|lg) default empty
2648  * @cfg {Number} max_width set the max width of modal
2649  *
2650  *
2651  * @constructor
2652  * Create a new Modal Dialog
2653  * @param {Object} config The config object
2654  */
2655
2656 Roo.bootstrap.Modal = function(config){
2657     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2658     this.addEvents({
2659         // raw events
2660         /**
2661          * @event btnclick
2662          * The raw btnclick event for the button
2663          * @param {Roo.EventObject} e
2664          */
2665         "btnclick" : true,
2666         /**
2667          * @event resize
2668          * Fire when dialog resize
2669          * @param {Roo.bootstrap.Modal} this
2670          * @param {Roo.EventObject} e
2671          */
2672         "resize" : true
2673     });
2674     this.buttons = this.buttons || [];
2675
2676     if (this.tmpl) {
2677         this.tmpl = Roo.factory(this.tmpl);
2678     }
2679
2680 };
2681
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2683
2684     title : 'test dialog',
2685
2686     buttons : false,
2687
2688     // set on load...
2689
2690     html: false,
2691
2692     tmp: false,
2693
2694     specificTitle: false,
2695
2696     buttonPosition: 'right',
2697
2698     allow_close : true,
2699
2700     animate : true,
2701
2702     fitwindow: false,
2703     
2704      // private
2705     dialogEl: false,
2706     bodyEl:  false,
2707     footerEl:  false,
2708     titleEl:  false,
2709     closeEl:  false,
2710
2711     size: '',
2712     
2713     max_width: 0,
2714     
2715     max_height: 0,
2716     
2717     fit_content: false,
2718
2719     onRender : function(ct, position)
2720     {
2721         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2722
2723         if(!this.el){
2724             var cfg = Roo.apply({},  this.getAutoCreate());
2725             cfg.id = Roo.id();
2726             //if(!cfg.name){
2727             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2728             //}
2729             //if (!cfg.name.length) {
2730             //    delete cfg.name;
2731            // }
2732             if (this.cls) {
2733                 cfg.cls += ' ' + this.cls;
2734             }
2735             if (this.style) {
2736                 cfg.style = this.style;
2737             }
2738             this.el = Roo.get(document.body).createChild(cfg, position);
2739         }
2740         //var type = this.el.dom.type;
2741
2742
2743         if(this.tabIndex !== undefined){
2744             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2745         }
2746
2747         this.dialogEl = this.el.select('.modal-dialog',true).first();
2748         this.bodyEl = this.el.select('.modal-body',true).first();
2749         this.closeEl = this.el.select('.modal-header .close', true).first();
2750         this.headerEl = this.el.select('.modal-header',true).first();
2751         this.titleEl = this.el.select('.modal-title',true).first();
2752         this.footerEl = this.el.select('.modal-footer',true).first();
2753
2754         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2755         
2756         //this.el.addClass("x-dlg-modal");
2757
2758         if (this.buttons.length) {
2759             Roo.each(this.buttons, function(bb) {
2760                 var b = Roo.apply({}, bb);
2761                 b.xns = b.xns || Roo.bootstrap;
2762                 b.xtype = b.xtype || 'Button';
2763                 if (typeof(b.listeners) == 'undefined') {
2764                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2765                 }
2766
2767                 var btn = Roo.factory(b);
2768
2769                 btn.render(this.el.select('.modal-footer div').first());
2770
2771             },this);
2772         }
2773         // render the children.
2774         var nitems = [];
2775
2776         if(typeof(this.items) != 'undefined'){
2777             var items = this.items;
2778             delete this.items;
2779
2780             for(var i =0;i < items.length;i++) {
2781                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2782             }
2783         }
2784
2785         this.items = nitems;
2786
2787         // where are these used - they used to be body/close/footer
2788
2789
2790         this.initEvents();
2791         //this.el.addClass([this.fieldClass, this.cls]);
2792
2793     },
2794
2795     getAutoCreate : function()
2796     {
2797         var bdy = {
2798                 cls : 'modal-body',
2799                 html : this.html || ''
2800         };
2801
2802         var title = {
2803             tag: 'h4',
2804             cls : 'modal-title',
2805             html : this.title
2806         };
2807
2808         if(this.specificTitle){
2809             title = this.title;
2810
2811         };
2812
2813         var header = [];
2814         if (this.allow_close && Roo.bootstrap.version == 3) {
2815             header.push({
2816                 tag: 'button',
2817                 cls : 'close',
2818                 html : '&times'
2819             });
2820         }
2821
2822         header.push(title);
2823
2824         if (this.allow_close && Roo.bootstrap.version == 4) {
2825             header.push({
2826                 tag: 'button',
2827                 cls : 'close',
2828                 html : '&times'
2829             });
2830         }
2831         
2832         var size = '';
2833
2834         if(this.size.length){
2835             size = 'modal-' + this.size;
2836         }
2837
2838         var modal = {
2839             cls: "modal",
2840              cn : [
2841                 {
2842                     cls: "modal-dialog " + size,
2843                     cn : [
2844                         {
2845                             cls : "modal-content",
2846                             cn : [
2847                                 {
2848                                     cls : 'modal-header',
2849                                     cn : header
2850                                 },
2851                                 bdy,
2852                                 {
2853                                     cls : 'modal-footer',
2854                                     cn : [
2855                                         {
2856                                             tag: 'div',
2857                                             cls: 'btn-' + this.buttonPosition
2858                                         }
2859                                     ]
2860
2861                                 }
2862
2863
2864                             ]
2865
2866                         }
2867                     ]
2868
2869                 }
2870             ]
2871         };
2872
2873         if(this.animate){
2874             modal.cls += ' fade';
2875         }
2876
2877         return modal;
2878
2879     },
2880     getChildContainer : function() {
2881
2882          return this.bodyEl;
2883
2884     },
2885     getButtonContainer : function() {
2886          return this.el.select('.modal-footer div',true).first();
2887
2888     },
2889     initEvents : function()
2890     {
2891         if (this.allow_close) {
2892             this.closeEl.on('click', this.hide, this);
2893         }
2894         Roo.EventManager.onWindowResize(this.resize, this, true);
2895
2896
2897     },
2898
2899     resize : function()
2900     {
2901         this.maskEl.setSize(
2902             Roo.lib.Dom.getViewWidth(true),
2903             Roo.lib.Dom.getViewHeight(true)
2904         );
2905         
2906         if (this.fitwindow) {
2907             this.setSize(
2908                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2910             );
2911             return;
2912         }
2913         
2914         if(this.max_width !== 0) {
2915             
2916             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2917             
2918             if(this.height) {
2919                 this.setSize(w, this.height);
2920                 return;
2921             }
2922             
2923             if(this.max_height) {
2924                 this.setSize(w,Math.min(
2925                     this.max_height,
2926                     Roo.lib.Dom.getViewportHeight(true) - 60
2927                 ));
2928                 
2929                 return;
2930             }
2931             
2932             if(!this.fit_content) {
2933                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2934                 return;
2935             }
2936             
2937             this.setSize(w, Math.min(
2938                 60 +
2939                 this.headerEl.getHeight() + 
2940                 this.footerEl.getHeight() + 
2941                 this.getChildHeight(this.bodyEl.dom.childNodes),
2942                 Roo.lib.Dom.getViewportHeight(true) - 60)
2943             );
2944         }
2945         
2946     },
2947
2948     setSize : function(w,h)
2949     {
2950         if (!w && !h) {
2951             return;
2952         }
2953         
2954         this.resizeTo(w,h);
2955     },
2956
2957     show : function() {
2958
2959         if (!this.rendered) {
2960             this.render();
2961         }
2962
2963         //this.el.setStyle('display', 'block');
2964         this.el.removeClass('hideing');
2965         this.el.dom.style.display='block';
2966         
2967         Roo.get(document.body).addClass('modal-open');
2968  
2969         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2970             var _this = this;
2971             (function(){
2972                 this.el.addClass('show');
2973                 this.el.addClass('in');
2974             }).defer(50, this);
2975         }else{
2976             this.el.addClass('show');
2977             this.el.addClass('in');
2978         }
2979
2980         // not sure how we can show data in here..
2981         //if (this.tmpl) {
2982         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2983         //}
2984
2985         Roo.get(document.body).addClass("x-body-masked");
2986         
2987         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2988         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989         this.maskEl.dom.style.display = 'block';
2990         this.maskEl.addClass('show');
2991         
2992         
2993         this.resize();
2994         
2995         this.fireEvent('show', this);
2996
2997         // set zindex here - otherwise it appears to be ignored...
2998         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2999
3000         (function () {
3001             this.items.forEach( function(e) {
3002                 e.layout ? e.layout() : false;
3003
3004             });
3005         }).defer(100,this);
3006
3007     },
3008     hide : function()
3009     {
3010         if(this.fireEvent("beforehide", this) !== false){
3011             
3012             this.maskEl.removeClass('show');
3013             
3014             this.maskEl.dom.style.display = '';
3015             Roo.get(document.body).removeClass("x-body-masked");
3016             this.el.removeClass('in');
3017             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3018
3019             if(this.animate){ // why
3020                 this.el.addClass('hideing');
3021                 this.el.removeClass('show');
3022                 (function(){
3023                     if (!this.el.hasClass('hideing')) {
3024                         return; // it's been shown again...
3025                     }
3026                     
3027                     this.el.dom.style.display='';
3028
3029                     Roo.get(document.body).removeClass('modal-open');
3030                     this.el.removeClass('hideing');
3031                 }).defer(150,this);
3032                 
3033             }else{
3034                 this.el.removeClass('show');
3035                 this.el.dom.style.display='';
3036                 Roo.get(document.body).removeClass('modal-open');
3037
3038             }
3039             this.fireEvent('hide', this);
3040         }
3041     },
3042     isVisible : function()
3043     {
3044         
3045         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3046         
3047     },
3048
3049     addButton : function(str, cb)
3050     {
3051
3052
3053         var b = Roo.apply({}, { html : str } );
3054         b.xns = b.xns || Roo.bootstrap;
3055         b.xtype = b.xtype || 'Button';
3056         if (typeof(b.listeners) == 'undefined') {
3057             b.listeners = { click : cb.createDelegate(this)  };
3058         }
3059
3060         var btn = Roo.factory(b);
3061
3062         btn.render(this.el.select('.modal-footer div').first());
3063
3064         return btn;
3065
3066     },
3067
3068     setDefaultButton : function(btn)
3069     {
3070         //this.el.select('.modal-footer').()
3071     },
3072     diff : false,
3073
3074     resizeTo: function(w,h)
3075     {
3076         // skip.. ?? why??
3077
3078         this.dialogEl.setWidth(w);
3079         if (this.diff === false) {
3080             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3081         }
3082
3083         this.bodyEl.setHeight(h - this.diff);
3084
3085         this.fireEvent('resize', this);
3086
3087     },
3088     setContentSize  : function(w, h)
3089     {
3090
3091     },
3092     onButtonClick: function(btn,e)
3093     {
3094         //Roo.log([a,b,c]);
3095         this.fireEvent('btnclick', btn.name, e);
3096     },
3097      /**
3098      * Set the title of the Dialog
3099      * @param {String} str new Title
3100      */
3101     setTitle: function(str) {
3102         this.titleEl.dom.innerHTML = str;
3103     },
3104     /**
3105      * Set the body of the Dialog
3106      * @param {String} str new Title
3107      */
3108     setBody: function(str) {
3109         this.bodyEl.dom.innerHTML = str;
3110     },
3111     /**
3112      * Set the body of the Dialog using the template
3113      * @param {Obj} data - apply this data to the template and replace the body contents.
3114      */
3115     applyBody: function(obj)
3116     {
3117         if (!this.tmpl) {
3118             Roo.log("Error - using apply Body without a template");
3119             //code
3120         }
3121         this.tmpl.overwrite(this.bodyEl, obj);
3122     },
3123     
3124     getChildHeight : function(child_nodes)
3125     {
3126         if(
3127             !child_nodes ||
3128             child_nodes.length == 0
3129         ) {
3130             return;
3131         }
3132         
3133         var child_height = 0;
3134         
3135         for(var i = 0; i < child_nodes.length; i++) {
3136             
3137             /*
3138             * for modal with tabs...
3139             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3140                 
3141                 var layout_childs = child_nodes[i].childNodes;
3142                 
3143                 for(var j = 0; j < layout_childs.length; j++) {
3144                     
3145                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3146                         
3147                         var layout_body_childs = layout_childs[j].childNodes;
3148                         
3149                         for(var k = 0; k < layout_body_childs.length; k++) {
3150                             
3151                             if(layout_body_childs[k].classList.contains('navbar')) {
3152                                 child_height += layout_body_childs[k].offsetHeight;
3153                                 continue;
3154                             }
3155                             
3156                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3157                                 
3158                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3159                                 
3160                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3161                                     
3162                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3164                                         continue;
3165                                     }
3166                                     
3167                                 }
3168                                 
3169                             }
3170                             
3171                         }
3172                     }
3173                 }
3174                 continue;
3175             }
3176             */
3177             
3178             child_height += child_nodes[i].offsetHeight;
3179             // Roo.log(child_nodes[i].offsetHeight);
3180         }
3181         
3182         return child_height;
3183     }
3184
3185 });
3186
3187
3188 Roo.apply(Roo.bootstrap.Modal,  {
3189     /**
3190          * Button config that displays a single OK button
3191          * @type Object
3192          */
3193         OK :  [{
3194             name : 'ok',
3195             weight : 'primary',
3196             html : 'OK'
3197         }],
3198         /**
3199          * Button config that displays Yes and No buttons
3200          * @type Object
3201          */
3202         YESNO : [
3203             {
3204                 name  : 'no',
3205                 html : 'No'
3206             },
3207             {
3208                 name  :'yes',
3209                 weight : 'primary',
3210                 html : 'Yes'
3211             }
3212         ],
3213
3214         /**
3215          * Button config that displays OK and Cancel buttons
3216          * @type Object
3217          */
3218         OKCANCEL : [
3219             {
3220                name : 'cancel',
3221                 html : 'Cancel'
3222             },
3223             {
3224                 name : 'ok',
3225                 weight : 'primary',
3226                 html : 'OK'
3227             }
3228         ],
3229         /**
3230          * Button config that displays Yes, No and Cancel buttons
3231          * @type Object
3232          */
3233         YESNOCANCEL : [
3234             {
3235                 name : 'yes',
3236                 weight : 'primary',
3237                 html : 'Yes'
3238             },
3239             {
3240                 name : 'no',
3241                 html : 'No'
3242             },
3243             {
3244                 name : 'cancel',
3245                 html : 'Cancel'
3246             }
3247         ],
3248         
3249         zIndex : 10001
3250 });
3251 /*
3252  * - LGPL
3253  *
3254  * messagebox - can be used as a replace
3255  * 
3256  */
3257 /**
3258  * @class Roo.MessageBox
3259  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3260  * Example usage:
3261  *<pre><code>
3262 // Basic alert:
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3264
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3267     if (btn == 'ok'){
3268         // process text value...
3269     }
3270 });
3271
3272 // Show a dialog using config options:
3273 Roo.Msg.show({
3274    title:'Save Changes?',
3275    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276    buttons: Roo.Msg.YESNOCANCEL,
3277    fn: processResult,
3278    animEl: 'elId'
3279 });
3280 </code></pre>
3281  * @singleton
3282  */
3283 Roo.bootstrap.MessageBox = function(){
3284     var dlg, opt, mask, waitTimer;
3285     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286     var buttons, activeTextEl, bwidth;
3287
3288     
3289     // private
3290     var handleButton = function(button){
3291         dlg.hide();
3292         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3293     };
3294
3295     // private
3296     var handleHide = function(){
3297         if(opt && opt.cls){
3298             dlg.el.removeClass(opt.cls);
3299         }
3300         //if(waitTimer){
3301         //    Roo.TaskMgr.stop(waitTimer);
3302         //    waitTimer = null;
3303         //}
3304     };
3305
3306     // private
3307     var updateButtons = function(b){
3308         var width = 0;
3309         if(!b){
3310             buttons["ok"].hide();
3311             buttons["cancel"].hide();
3312             buttons["yes"].hide();
3313             buttons["no"].hide();
3314             //dlg.footer.dom.style.display = 'none';
3315             return width;
3316         }
3317         dlg.footerEl.dom.style.display = '';
3318         for(var k in buttons){
3319             if(typeof buttons[k] != "function"){
3320                 if(b[k]){
3321                     buttons[k].show();
3322                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323                     width += buttons[k].el.getWidth()+15;
3324                 }else{
3325                     buttons[k].hide();
3326                 }
3327             }
3328         }
3329         return width;
3330     };
3331
3332     // private
3333     var handleEsc = function(d, k, e){
3334         if(opt && opt.closable !== false){
3335             dlg.hide();
3336         }
3337         if(e){
3338             e.stopEvent();
3339         }
3340     };
3341
3342     return {
3343         /**
3344          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345          * @return {Roo.BasicDialog} The BasicDialog element
3346          */
3347         getDialog : function(){
3348            if(!dlg){
3349                 dlg = new Roo.bootstrap.Modal( {
3350                     //draggable: true,
3351                     //resizable:false,
3352                     //constraintoviewport:false,
3353                     //fixedcenter:true,
3354                     //collapsible : false,
3355                     //shim:true,
3356                     //modal: true,
3357                 //    width: 'auto',
3358                   //  height:100,
3359                     //buttonAlign:"center",
3360                     closeClick : function(){
3361                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3362                             handleButton("no");
3363                         }else{
3364                             handleButton("cancel");
3365                         }
3366                     }
3367                 });
3368                 dlg.render();
3369                 dlg.on("hide", handleHide);
3370                 mask = dlg.mask;
3371                 //dlg.addKeyListener(27, handleEsc);
3372                 buttons = {};
3373                 this.buttons = buttons;
3374                 var bt = this.buttonText;
3375                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3379                 //Roo.log(buttons);
3380                 bodyEl = dlg.bodyEl.createChild({
3381
3382                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383                         '<textarea class="roo-mb-textarea"></textarea>' +
3384                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3385                 });
3386                 msgEl = bodyEl.dom.firstChild;
3387                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388                 textboxEl.enableDisplayMode();
3389                 textboxEl.addKeyListener([10,13], function(){
3390                     if(dlg.isVisible() && opt && opt.buttons){
3391                         if(opt.buttons.ok){
3392                             handleButton("ok");
3393                         }else if(opt.buttons.yes){
3394                             handleButton("yes");
3395                         }
3396                     }
3397                 });
3398                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399                 textareaEl.enableDisplayMode();
3400                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401                 progressEl.enableDisplayMode();
3402                 
3403                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404                 var pf = progressEl.dom.firstChild;
3405                 if (pf) {
3406                     pp = Roo.get(pf.firstChild);
3407                     pp.setHeight(pf.offsetHeight);
3408                 }
3409                 
3410             }
3411             return dlg;
3412         },
3413
3414         /**
3415          * Updates the message box body text
3416          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417          * the XHTML-compliant non-breaking space character '&amp;#160;')
3418          * @return {Roo.MessageBox} This message box
3419          */
3420         updateText : function(text)
3421         {
3422             if(!dlg.isVisible() && !opt.width){
3423                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3425             }
3426             msgEl.innerHTML = text || '&#160;';
3427       
3428             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3430             var w = Math.max(
3431                     Math.min(opt.width || cw , this.maxWidth), 
3432                     Math.max(opt.minWidth || this.minWidth, bwidth)
3433             );
3434             if(opt.prompt){
3435                 activeTextEl.setWidth(w);
3436             }
3437             if(dlg.isVisible()){
3438                 dlg.fixedcenter = false;
3439             }
3440             // to big, make it scroll. = But as usual stupid IE does not support
3441             // !important..
3442             
3443             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3446             } else {
3447                 bodyEl.dom.style.height = '';
3448                 bodyEl.dom.style.overflowY = '';
3449             }
3450             if (cw > w) {
3451                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3452             } else {
3453                 bodyEl.dom.style.overflowX = '';
3454             }
3455             
3456             dlg.setContentSize(w, bodyEl.getHeight());
3457             if(dlg.isVisible()){
3458                 dlg.fixedcenter = true;
3459             }
3460             return this;
3461         },
3462
3463         /**
3464          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3465          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468          * @return {Roo.MessageBox} This message box
3469          */
3470         updateProgress : function(value, text){
3471             if(text){
3472                 this.updateText(text);
3473             }
3474             
3475             if (pp) { // weird bug on my firefox - for some reason this is not defined
3476                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3478             }
3479             return this;
3480         },        
3481
3482         /**
3483          * Returns true if the message box is currently displayed
3484          * @return {Boolean} True if the message box is visible, else false
3485          */
3486         isVisible : function(){
3487             return dlg && dlg.isVisible();  
3488         },
3489
3490         /**
3491          * Hides the message box if it is displayed
3492          */
3493         hide : function(){
3494             if(this.isVisible()){
3495                 dlg.hide();
3496             }  
3497         },
3498
3499         /**
3500          * Displays a new message box, or reinitializes an existing message box, based on the config options
3501          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502          * The following config object properties are supported:
3503          * <pre>
3504 Property    Type             Description
3505 ----------  ---------------  ------------------------------------------------------------------------------------
3506 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3507                                    closes (defaults to undefined)
3508 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3511                                    progress and wait dialogs will ignore this property and always hide the
3512                                    close button as they can only be closed programmatically.
3513 cls               String           A custom CSS class to apply to the message box element
3514 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3515                                    displayed (defaults to 75)
3516 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3517                                    function will be btn (the name of the button that was clicked, if applicable,
3518                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3519                                    Progress and wait dialogs will ignore this option since they do not respond to
3520                                    user actions and can only be closed programmatically, so any required function
3521                                    should be called by the same code after it closes the dialog.
3522 icon              String           A CSS class that provides a background image to be used as an icon for
3523                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3525 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3526 modal             Boolean          False to allow user interaction with the page while the message box is
3527                                    displayed (defaults to true)
3528 msg               String           A string that will replace the existing message box body text (defaults
3529                                    to the XHTML-compliant non-breaking space character '&#160;')
3530 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3531 progress          Boolean          True to display a progress bar (defaults to false)
3532 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3535 title             String           The title text
3536 value             String           The string value to set into the active textbox element if displayed
3537 wait              Boolean          True to display a progress bar (defaults to false)
3538 width             Number           The width of the dialog in pixels
3539 </pre>
3540          *
3541          * Example usage:
3542          * <pre><code>
3543 Roo.Msg.show({
3544    title: 'Address',
3545    msg: 'Please enter your address:',
3546    width: 300,
3547    buttons: Roo.MessageBox.OKCANCEL,
3548    multiline: true,
3549    fn: saveAddress,
3550    animEl: 'addAddressBtn'
3551 });
3552 </code></pre>
3553          * @param {Object} config Configuration options
3554          * @return {Roo.MessageBox} This message box
3555          */
3556         show : function(options)
3557         {
3558             
3559             // this causes nightmares if you show one dialog after another
3560             // especially on callbacks..
3561              
3562             if(this.isVisible()){
3563                 
3564                 this.hide();
3565                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3567                 Roo.log("New Dialog Message:" +  options.msg )
3568                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3570                 
3571             }
3572             var d = this.getDialog();
3573             opt = options;
3574             d.setTitle(opt.title || "&#160;");
3575             d.closeEl.setDisplayed(opt.closable !== false);
3576             activeTextEl = textboxEl;
3577             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3578             if(opt.prompt){
3579                 if(opt.multiline){
3580                     textboxEl.hide();
3581                     textareaEl.show();
3582                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3583                         opt.multiline : this.defaultTextHeight);
3584                     activeTextEl = textareaEl;
3585                 }else{
3586                     textboxEl.show();
3587                     textareaEl.hide();
3588                 }
3589             }else{
3590                 textboxEl.hide();
3591                 textareaEl.hide();
3592             }
3593             progressEl.setDisplayed(opt.progress === true);
3594             this.updateProgress(0);
3595             activeTextEl.dom.value = opt.value || "";
3596             if(opt.prompt){
3597                 dlg.setDefaultButton(activeTextEl);
3598             }else{
3599                 var bs = opt.buttons;
3600                 var db = null;
3601                 if(bs && bs.ok){
3602                     db = buttons["ok"];
3603                 }else if(bs && bs.yes){
3604                     db = buttons["yes"];
3605                 }
3606                 dlg.setDefaultButton(db);
3607             }
3608             bwidth = updateButtons(opt.buttons);
3609             this.updateText(opt.msg);
3610             if(opt.cls){
3611                 d.el.addClass(opt.cls);
3612             }
3613             d.proxyDrag = opt.proxyDrag === true;
3614             d.modal = opt.modal !== false;
3615             d.mask = opt.modal !== false ? mask : false;
3616             if(!d.isVisible()){
3617                 // force it to the end of the z-index stack so it gets a cursor in FF
3618                 document.body.appendChild(dlg.el.dom);
3619                 d.animateTarget = null;
3620                 d.show(options.animEl);
3621             }
3622             return this;
3623         },
3624
3625         /**
3626          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3627          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628          * and closing the message box when the process is complete.
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @return {Roo.MessageBox} This message box
3632          */
3633         progress : function(title, msg){
3634             this.show({
3635                 title : title,
3636                 msg : msg,
3637                 buttons: false,
3638                 progress:true,
3639                 closable:false,
3640                 minWidth: this.minProgressWidth,
3641                 modal : true
3642             });
3643             return this;
3644         },
3645
3646         /**
3647          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648          * If a callback function is passed it will be called after the user clicks the button, and the
3649          * id of the button that was clicked will be passed as the only parameter to the callback
3650          * (could also be the top-right close button).
3651          * @param {String} title The title bar text
3652          * @param {String} msg The message box body text
3653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654          * @param {Object} scope (optional) The scope of the callback function
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         alert : function(title, msg, fn, scope)
3658         {
3659             this.show({
3660                 title : title,
3661                 msg : msg,
3662                 buttons: this.OK,
3663                 fn: fn,
3664                 closable : false,
3665                 scope : scope,
3666                 modal : true
3667             });
3668             return this;
3669         },
3670
3671         /**
3672          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3673          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674          * You are responsible for closing the message box when the process is complete.
3675          * @param {String} msg The message box body text
3676          * @param {String} title (optional) The title bar text
3677          * @return {Roo.MessageBox} This message box
3678          */
3679         wait : function(msg, title){
3680             this.show({
3681                 title : title,
3682                 msg : msg,
3683                 buttons: false,
3684                 closable:false,
3685                 progress:true,
3686                 modal:true,
3687                 width:300,
3688                 wait:true
3689             });
3690             waitTimer = Roo.TaskMgr.start({
3691                 run: function(i){
3692                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3693                 },
3694                 interval: 1000
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703          * @param {String} title The title bar text
3704          * @param {String} msg The message box body text
3705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706          * @param {Object} scope (optional) The scope of the callback function
3707          * @return {Roo.MessageBox} This message box
3708          */
3709         confirm : function(title, msg, fn, scope){
3710             this.show({
3711                 title : title,
3712                 msg : msg,
3713                 buttons: this.YESNO,
3714                 fn: fn,
3715                 scope : scope,
3716                 modal : true
3717             });
3718             return this;
3719         },
3720
3721         /**
3722          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3724          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725          * (could also be the top-right close button) and the text that was entered will be passed as the two
3726          * parameters to the callback.
3727          * @param {String} title The title bar text
3728          * @param {String} msg The message box body text
3729          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730          * @param {Object} scope (optional) The scope of the callback function
3731          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733          * @return {Roo.MessageBox} This message box
3734          */
3735         prompt : function(title, msg, fn, scope, multiline){
3736             this.show({
3737                 title : title,
3738                 msg : msg,
3739                 buttons: this.OKCANCEL,
3740                 fn: fn,
3741                 minWidth:250,
3742                 scope : scope,
3743                 prompt:true,
3744                 multiline: multiline,
3745                 modal : true
3746             });
3747             return this;
3748         },
3749
3750         /**
3751          * Button config that displays a single OK button
3752          * @type Object
3753          */
3754         OK : {ok:true},
3755         /**
3756          * Button config that displays Yes and No buttons
3757          * @type Object
3758          */
3759         YESNO : {yes:true, no:true},
3760         /**
3761          * Button config that displays OK and Cancel buttons
3762          * @type Object
3763          */
3764         OKCANCEL : {ok:true, cancel:true},
3765         /**
3766          * Button config that displays Yes, No and Cancel buttons
3767          * @type Object
3768          */
3769         YESNOCANCEL : {yes:true, no:true, cancel:true},
3770
3771         /**
3772          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3773          * @type Number
3774          */
3775         defaultTextHeight : 75,
3776         /**
3777          * The maximum width in pixels of the message box (defaults to 600)
3778          * @type Number
3779          */
3780         maxWidth : 600,
3781         /**
3782          * The minimum width in pixels of the message box (defaults to 100)
3783          * @type Number
3784          */
3785         minWidth : 100,
3786         /**
3787          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3788          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3789          * @type Number
3790          */
3791         minProgressWidth : 250,
3792         /**
3793          * An object containing the default button text strings that can be overriden for localized language support.
3794          * Supported properties are: ok, cancel, yes and no.
3795          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3796          * @type Object
3797          */
3798         buttonText : {
3799             ok : "OK",
3800             cancel : "Cancel",
3801             yes : "Yes",
3802             no : "No"
3803         }
3804     };
3805 }();
3806
3807 /**
3808  * Shorthand for {@link Roo.MessageBox}
3809  */
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3812 /*
3813  * - LGPL
3814  *
3815  * navbar
3816  * 
3817  */
3818
3819 /**
3820  * @class Roo.bootstrap.Navbar
3821  * @extends Roo.bootstrap.Component
3822  * Bootstrap Navbar class
3823
3824  * @constructor
3825  * Create a new Navbar
3826  * @param {Object} config The config object
3827  */
3828
3829
3830 Roo.bootstrap.Navbar = function(config){
3831     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3832     this.addEvents({
3833         // raw events
3834         /**
3835          * @event beforetoggle
3836          * Fire before toggle the menu
3837          * @param {Roo.EventObject} e
3838          */
3839         "beforetoggle" : true
3840     });
3841 };
3842
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3844     
3845     
3846    
3847     // private
3848     navItems : false,
3849     loadMask : false,
3850     
3851     
3852     getAutoCreate : function(){
3853         
3854         
3855         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3856         
3857     },
3858     
3859     initEvents :function ()
3860     {
3861         //Roo.log(this.el.select('.navbar-toggle',true));
3862         this.el.select('.navbar-toggle',true).on('click', function() {
3863             if(this.fireEvent('beforetoggle', this) !== false){
3864                 var ce = this.el.select('.navbar-collapse',true).first();
3865                 ce.toggleClass('in'); // old...
3866                 if (ce.hasClass('collapse')) {
3867                     // show it...
3868                     ce.removeClass('collapse show');
3869                     ce.setHeight(ce.getHeight()); // resize it ...
3870                     // now flag it as moving..
3871                     ce.addClass('collapsing');
3872                     
3873                     (function() { ce.removeClass('collapsing'); }).defer(100);
3874                 } else {
3875                     ce.addClass('collapsing');
3876                     ce.removeClass('show');
3877                     (function() {
3878                         ce.removeClass('collapsing');
3879                         ce.addClass('collapse');
3880                     }).defer(200);
3881                     
3882                 }
3883             }
3884             
3885         }, this);
3886         
3887         var mark = {
3888             tag: "div",
3889             cls:"x-dlg-mask"
3890         };
3891         
3892         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3893         
3894         var size = this.el.getSize();
3895         this.maskEl.setSize(size.width, size.height);
3896         this.maskEl.enableDisplayMode("block");
3897         this.maskEl.hide();
3898         
3899         if(this.loadMask){
3900             this.maskEl.show();
3901         }
3902     },
3903     
3904     
3905     getChildContainer : function()
3906     {
3907         if (this.el.select('.collapse').getCount()) {
3908             return this.el.select('.collapse',true).first();
3909         }
3910         
3911         return this.el;
3912     },
3913     
3914     mask : function()
3915     {
3916         this.maskEl.show();
3917     },
3918     
3919     unmask : function()
3920     {
3921         this.maskEl.hide();
3922     } 
3923     
3924     
3925     
3926     
3927 });
3928
3929
3930
3931  
3932
3933  /*
3934  * - LGPL
3935  *
3936  * navbar
3937  * 
3938  */
3939
3940 /**
3941  * @class Roo.bootstrap.NavSimplebar
3942  * @extends Roo.bootstrap.Navbar
3943  * Bootstrap Sidebar class
3944  *
3945  * @cfg {Boolean} inverse is inverted color
3946  * 
3947  * @cfg {String} type (nav | pills | tabs)
3948  * @cfg {Boolean} arrangement stacked | justified
3949  * @cfg {String} align (left | right) alignment
3950  * 
3951  * @cfg {Boolean} main (true|false) main nav bar? default false
3952  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3953  * 
3954  * @cfg {String} tag (header|footer|nav|div) default is nav 
3955
3956  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3957  * 
3958  * 
3959  * @constructor
3960  * Create a new Sidebar
3961  * @param {Object} config The config object
3962  */
3963
3964
3965 Roo.bootstrap.NavSimplebar = function(config){
3966     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3967 };
3968
3969 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3970     
3971     inverse: false,
3972     
3973     type: false,
3974     arrangement: '',
3975     align : false,
3976     
3977     weight : 'light',
3978     
3979     main : false,
3980     
3981     
3982     tag : false,
3983     
3984     
3985     getAutoCreate : function(){
3986         
3987         
3988         var cfg = {
3989             tag : this.tag || 'div',
3990             cls : 'navbar navbar-expand-lg'
3991         };
3992         if (['light','white'].indexOf(this.weight) > -1) {
3993             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3994         }
3995         cfg.cls += ' bg-' + this.weight;
3996         
3997           
3998         
3999         cfg.cn = [
4000             {
4001                 cls: 'nav',
4002                 tag : 'ul'
4003             }
4004         ];
4005         
4006          
4007         this.type = this.type || 'nav';
4008         if (['tabs','pills'].indexOf(this.type)!==-1) {
4009             cfg.cn[0].cls += ' nav-' + this.type
4010         
4011         
4012         } else {
4013             if (this.type!=='nav') {
4014                 Roo.log('nav type must be nav/tabs/pills')
4015             }
4016             cfg.cn[0].cls += ' navbar-nav'
4017         }
4018         
4019         
4020         
4021         
4022         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4023             cfg.cn[0].cls += ' nav-' + this.arrangement;
4024         }
4025         
4026         
4027         if (this.align === 'right') {
4028             cfg.cn[0].cls += ' navbar-right';
4029         }
4030         
4031         if (this.inverse) {
4032             cfg.cls += ' navbar-inverse';
4033             
4034         }
4035         
4036         
4037         return cfg;
4038     
4039         
4040     }
4041     
4042     
4043     
4044 });
4045
4046
4047
4048  
4049
4050  
4051        /*
4052  * - LGPL
4053  *
4054  * navbar
4055  * navbar-fixed-top
4056  * navbar-expand-md  fixed-top 
4057  */
4058
4059 /**
4060  * @class Roo.bootstrap.NavHeaderbar
4061  * @extends Roo.bootstrap.NavSimplebar
4062  * Bootstrap Sidebar class
4063  *
4064  * @cfg {String} brand what is brand
4065  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4066  * @cfg {String} brand_href href of the brand
4067  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4068  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4069  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4070  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4071  * 
4072  * @constructor
4073  * Create a new Sidebar
4074  * @param {Object} config The config object
4075  */
4076
4077
4078 Roo.bootstrap.NavHeaderbar = function(config){
4079     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4080       
4081 };
4082
4083 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4084     
4085     position: '',
4086     brand: '',
4087     brand_href: false,
4088     srButton : true,
4089     autohide : false,
4090     desktopCenter : false,
4091    
4092     
4093     getAutoCreate : function(){
4094         
4095         var   cfg = {
4096             tag: this.nav || 'nav',
4097             cls: 'navbar navbar-expand-md',
4098             role: 'navigation',
4099             cn: []
4100         };
4101         
4102         var cn = cfg.cn;
4103         if (this.desktopCenter) {
4104             cn.push({cls : 'container', cn : []});
4105             cn = cn[0].cn;
4106         }
4107         
4108         if(this.srButton){
4109             var btn = {
4110                 tag: 'button',
4111                 type: 'button',
4112                 cls: 'navbar-toggle navbar-toggler',
4113                 'data-toggle': 'collapse',
4114                 cn: [
4115                     {
4116                         tag: 'span',
4117                         cls: 'sr-only',
4118                         html: 'Toggle navigation'
4119                     },
4120                     {
4121                         tag: 'span',
4122                         cls: 'icon-bar navbar-toggler-icon'
4123                     },
4124                     {
4125                         tag: 'span',
4126                         cls: 'icon-bar'
4127                     },
4128                     {
4129                         tag: 'span',
4130                         cls: 'icon-bar'
4131                     }
4132                 ]
4133             };
4134             
4135             cn.push( Roo.bootstrap.version == 4 ? btn : {
4136                 tag: 'div',
4137                 cls: 'navbar-header',
4138                 cn: [
4139                     btn
4140                 ]
4141             });
4142         }
4143         
4144         cn.push({
4145             tag: 'div',
4146             cls: 'collapse navbar-collapse',
4147             cn : []
4148         });
4149         
4150         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4151         
4152         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4153             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4154             
4155             // tag can override this..
4156             
4157             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4158         }
4159         
4160         if (this.brand !== '') {
4161             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4162             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4163                 tag: 'a',
4164                 href: this.brand_href ? this.brand_href : '#',
4165                 cls: 'navbar-brand',
4166                 cn: [
4167                 this.brand
4168                 ]
4169             });
4170         }
4171         
4172         if(this.main){
4173             cfg.cls += ' main-nav';
4174         }
4175         
4176         
4177         return cfg;
4178
4179         
4180     },
4181     getHeaderChildContainer : function()
4182     {
4183         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4184             return this.el.select('.navbar-header',true).first();
4185         }
4186         
4187         return this.getChildContainer();
4188     },
4189     
4190     
4191     initEvents : function()
4192     {
4193         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4194         
4195         if (this.autohide) {
4196             
4197             var prevScroll = 0;
4198             var ft = this.el;
4199             
4200             Roo.get(document).on('scroll',function(e) {
4201                 var ns = Roo.get(document).getScroll().top;
4202                 var os = prevScroll;
4203                 prevScroll = ns;
4204                 
4205                 if(ns > os){
4206                     ft.removeClass('slideDown');
4207                     ft.addClass('slideUp');
4208                     return;
4209                 }
4210                 ft.removeClass('slideUp');
4211                 ft.addClass('slideDown');
4212                  
4213               
4214           },this);
4215         }
4216     }    
4217     
4218 });
4219
4220
4221
4222  
4223
4224  /*
4225  * - LGPL
4226  *
4227  * navbar
4228  * 
4229  */
4230
4231 /**
4232  * @class Roo.bootstrap.NavSidebar
4233  * @extends Roo.bootstrap.Navbar
4234  * Bootstrap Sidebar class
4235  * 
4236  * @constructor
4237  * Create a new Sidebar
4238  * @param {Object} config The config object
4239  */
4240
4241
4242 Roo.bootstrap.NavSidebar = function(config){
4243     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4244 };
4245
4246 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4247     
4248     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4249     
4250     getAutoCreate : function(){
4251         
4252         
4253         return  {
4254             tag: 'div',
4255             cls: 'sidebar sidebar-nav'
4256         };
4257     
4258         
4259     }
4260     
4261     
4262     
4263 });
4264
4265
4266
4267  
4268
4269  /*
4270  * - LGPL
4271  *
4272  * nav group
4273  * 
4274  */
4275
4276 /**
4277  * @class Roo.bootstrap.NavGroup
4278  * @extends Roo.bootstrap.Component
4279  * Bootstrap NavGroup class
4280  * @cfg {String} align (left|right)
4281  * @cfg {Boolean} inverse
4282  * @cfg {String} type (nav|pills|tab) default nav
4283  * @cfg {String} navId - reference Id for navbar.
4284
4285  * 
4286  * @constructor
4287  * Create a new nav group
4288  * @param {Object} config The config object
4289  */
4290
4291 Roo.bootstrap.NavGroup = function(config){
4292     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4293     this.navItems = [];
4294    
4295     Roo.bootstrap.NavGroup.register(this);
4296      this.addEvents({
4297         /**
4298              * @event changed
4299              * Fires when the active item changes
4300              * @param {Roo.bootstrap.NavGroup} this
4301              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4302              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4303          */
4304         'changed': true
4305      });
4306     
4307 };
4308
4309 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4310     
4311     align: '',
4312     inverse: false,
4313     form: false,
4314     type: 'nav',
4315     navId : '',
4316     // private
4317     
4318     navItems : false, 
4319     
4320     getAutoCreate : function()
4321     {
4322         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4323         
4324         cfg = {
4325             tag : 'ul',
4326             cls: 'nav' 
4327         };
4328         
4329         if (['tabs','pills'].indexOf(this.type)!==-1) {
4330             cfg.cls += ' nav-' + this.type
4331         } else {
4332             if (this.type!=='nav') {
4333                 Roo.log('nav type must be nav/tabs/pills')
4334             }
4335             cfg.cls += ' navbar-nav'
4336         }
4337         
4338         if (this.parent() && this.parent().sidebar) {
4339             cfg = {
4340                 tag: 'ul',
4341                 cls: 'dashboard-menu sidebar-menu'
4342             };
4343             
4344             return cfg;
4345         }
4346         
4347         if (this.form === true) {
4348             cfg = {
4349                 tag: 'form',
4350                 cls: 'navbar-form'
4351             };
4352             
4353             if (this.align === 'right') {
4354                 cfg.cls += ' navbar-right ml-md-auto';
4355             } else {
4356                 cfg.cls += ' navbar-left';
4357             }
4358         }
4359         
4360         if (this.align === 'right') {
4361             cfg.cls += ' navbar-right ml-md-auto';
4362         } else {
4363             cfg.cls += ' mr-auto';
4364         }
4365         
4366         if (this.inverse) {
4367             cfg.cls += ' navbar-inverse';
4368             
4369         }
4370         
4371         
4372         return cfg;
4373     },
4374     /**
4375     * sets the active Navigation item
4376     * @param {Roo.bootstrap.NavItem} the new current navitem
4377     */
4378     setActiveItem : function(item)
4379     {
4380         var prev = false;
4381         Roo.each(this.navItems, function(v){
4382             if (v == item) {
4383                 return ;
4384             }
4385             if (v.isActive()) {
4386                 v.setActive(false, true);
4387                 prev = v;
4388                 
4389             }
4390             
4391         });
4392
4393         item.setActive(true, true);
4394         this.fireEvent('changed', this, item, prev);
4395         
4396         
4397     },
4398     /**
4399     * gets the active Navigation item
4400     * @return {Roo.bootstrap.NavItem} the current navitem
4401     */
4402     getActive : function()
4403     {
4404         
4405         var prev = false;
4406         Roo.each(this.navItems, function(v){
4407             
4408             if (v.isActive()) {
4409                 prev = v;
4410                 
4411             }
4412             
4413         });
4414         return prev;
4415     },
4416     
4417     indexOfNav : function()
4418     {
4419         
4420         var prev = false;
4421         Roo.each(this.navItems, function(v,i){
4422             
4423             if (v.isActive()) {
4424                 prev = i;
4425                 
4426             }
4427             
4428         });
4429         return prev;
4430     },
4431     /**
4432     * adds a Navigation item
4433     * @param {Roo.bootstrap.NavItem} the navitem to add
4434     */
4435     addItem : function(cfg)
4436     {
4437         var cn = new Roo.bootstrap.NavItem(cfg);
4438         this.register(cn);
4439         cn.parentId = this.id;
4440         cn.onRender(this.el, null);
4441         return cn;
4442     },
4443     /**
4444     * register a Navigation item
4445     * @param {Roo.bootstrap.NavItem} the navitem to add
4446     */
4447     register : function(item)
4448     {
4449         this.navItems.push( item);
4450         item.navId = this.navId;
4451     
4452     },
4453     
4454     /**
4455     * clear all the Navigation item
4456     */
4457    
4458     clearAll : function()
4459     {
4460         this.navItems = [];
4461         this.el.dom.innerHTML = '';
4462     },
4463     
4464     getNavItem: function(tabId)
4465     {
4466         var ret = false;
4467         Roo.each(this.navItems, function(e) {
4468             if (e.tabId == tabId) {
4469                ret =  e;
4470                return false;
4471             }
4472             return true;
4473             
4474         });
4475         return ret;
4476     },
4477     
4478     setActiveNext : function()
4479     {
4480         var i = this.indexOfNav(this.getActive());
4481         if (i > this.navItems.length) {
4482             return;
4483         }
4484         this.setActiveItem(this.navItems[i+1]);
4485     },
4486     setActivePrev : function()
4487     {
4488         var i = this.indexOfNav(this.getActive());
4489         if (i  < 1) {
4490             return;
4491         }
4492         this.setActiveItem(this.navItems[i-1]);
4493     },
4494     clearWasActive : function(except) {
4495         Roo.each(this.navItems, function(e) {
4496             if (e.tabId != except.tabId && e.was_active) {
4497                e.was_active = false;
4498                return false;
4499             }
4500             return true;
4501             
4502         });
4503     },
4504     getWasActive : function ()
4505     {
4506         var r = false;
4507         Roo.each(this.navItems, function(e) {
4508             if (e.was_active) {
4509                r = e;
4510                return false;
4511             }
4512             return true;
4513             
4514         });
4515         return r;
4516     }
4517     
4518     
4519 });
4520
4521  
4522 Roo.apply(Roo.bootstrap.NavGroup, {
4523     
4524     groups: {},
4525      /**
4526     * register a Navigation Group
4527     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4528     */
4529     register : function(navgrp)
4530     {
4531         this.groups[navgrp.navId] = navgrp;
4532         
4533     },
4534     /**
4535     * fetch a Navigation Group based on the navigation ID
4536     * @param {string} the navgroup to add
4537     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4538     */
4539     get: function(navId) {
4540         if (typeof(this.groups[navId]) == 'undefined') {
4541             return false;
4542             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4543         }
4544         return this.groups[navId] ;
4545     }
4546     
4547     
4548     
4549 });
4550
4551  /*
4552  * - LGPL
4553  *
4554  * row
4555  * 
4556  */
4557
4558 /**
4559  * @class Roo.bootstrap.NavItem
4560  * @extends Roo.bootstrap.Component
4561  * Bootstrap Navbar.NavItem class
4562  * @cfg {String} href  link to
4563  * @cfg {String} html content of button
4564  * @cfg {String} badge text inside badge
4565  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4566  * @cfg {String} glyphicon DEPRICATED - use fa
4567  * @cfg {String} icon DEPRICATED - use fa
4568  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4569  * @cfg {Boolean} active Is item active
4570  * @cfg {Boolean} disabled Is item disabled
4571  
4572  * @cfg {Boolean} preventDefault (true | false) default false
4573  * @cfg {String} tabId the tab that this item activates.
4574  * @cfg {String} tagtype (a|span) render as a href or span?
4575  * @cfg {Boolean} animateRef (true|false) link to element default false  
4576   
4577  * @constructor
4578  * Create a new Navbar Item
4579  * @param {Object} config The config object
4580  */
4581 Roo.bootstrap.NavItem = function(config){
4582     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4583     this.addEvents({
4584         // raw events
4585         /**
4586          * @event click
4587          * The raw click event for the entire grid.
4588          * @param {Roo.EventObject} e
4589          */
4590         "click" : true,
4591          /**
4592             * @event changed
4593             * Fires when the active item active state changes
4594             * @param {Roo.bootstrap.NavItem} this
4595             * @param {boolean} state the new state
4596              
4597          */
4598         'changed': true,
4599         /**
4600             * @event scrollto
4601             * Fires when scroll to element
4602             * @param {Roo.bootstrap.NavItem} this
4603             * @param {Object} options
4604             * @param {Roo.EventObject} e
4605              
4606          */
4607         'scrollto': true
4608     });
4609    
4610 };
4611
4612 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4613     
4614     href: false,
4615     html: '',
4616     badge: '',
4617     icon: false,
4618     fa : false,
4619     glyphicon: false,
4620     active: false,
4621     preventDefault : false,
4622     tabId : false,
4623     tagtype : 'a',
4624     disabled : false,
4625     animateRef : false,
4626     was_active : false,
4627     
4628     getAutoCreate : function(){
4629          
4630         var cfg = {
4631             tag: 'li',
4632             cls: 'nav-item'
4633             
4634         };
4635         
4636         if (this.active) {
4637             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4638         }
4639         if (this.disabled) {
4640             cfg.cls += ' disabled';
4641         }
4642         
4643         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4644             cfg.cn = [
4645                 {
4646                     tag: this.tagtype,
4647                     href : this.href || "#",
4648                     html: this.html || ''
4649                 }
4650             ];
4651             if (this.tagtype == 'a') {
4652                 cfg.cn[0].cls = 'nav-link';
4653             }
4654             if (this.icon) {
4655                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4656             }
4657             if (this.fa) {
4658                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4659             }
4660             if(this.glyphicon) {
4661                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4662             }
4663             
4664             if (this.menu) {
4665                 
4666                 cfg.cn[0].html += " <span class='caret'></span>";
4667              
4668             }
4669             
4670             if (this.badge !== '') {
4671                  
4672                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4673             }
4674         }
4675         
4676         
4677         
4678         return cfg;
4679     },
4680     initEvents: function() 
4681     {
4682         if (typeof (this.menu) != 'undefined') {
4683             this.menu.parentType = this.xtype;
4684             this.menu.triggerEl = this.el;
4685             this.menu = this.addxtype(Roo.apply({}, this.menu));
4686         }
4687         
4688         this.el.select('a',true).on('click', this.onClick, this);
4689         
4690         if(this.tagtype == 'span'){
4691             this.el.select('span',true).on('click', this.onClick, this);
4692         }
4693        
4694         // at this point parent should be available..
4695         this.parent().register(this);
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if (e.getTarget('.dropdown-menu-item')) {
4701             // did you click on a menu itemm.... - then don't trigger onclick..
4702             return;
4703         }
4704         
4705         if(
4706                 this.preventDefault || 
4707                 this.href == '#' 
4708         ){
4709             Roo.log("NavItem - prevent Default?");
4710             e.preventDefault();
4711         }
4712         
4713         if (this.disabled) {
4714             return;
4715         }
4716         
4717         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4718         if (tg && tg.transition) {
4719             Roo.log("waiting for the transitionend");
4720             return;
4721         }
4722         
4723         
4724         
4725         //Roo.log("fire event clicked");
4726         if(this.fireEvent('click', this, e) === false){
4727             return;
4728         };
4729         
4730         if(this.tagtype == 'span'){
4731             return;
4732         }
4733         
4734         //Roo.log(this.href);
4735         var ael = this.el.select('a',true).first();
4736         //Roo.log(ael);
4737         
4738         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4739             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4740             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4741                 return; // ignore... - it's a 'hash' to another page.
4742             }
4743             Roo.log("NavItem - prevent Default?");
4744             e.preventDefault();
4745             this.scrollToElement(e);
4746         }
4747         
4748         
4749         var p =  this.parent();
4750    
4751         if (['tabs','pills'].indexOf(p.type)!==-1) {
4752             if (typeof(p.setActiveItem) !== 'undefined') {
4753                 p.setActiveItem(this);
4754             }
4755         }
4756         
4757         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4758         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4759             // remove the collapsed menu expand...
4760             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4761         }
4762     },
4763     
4764     isActive: function () {
4765         return this.active
4766     },
4767     setActive : function(state, fire, is_was_active)
4768     {
4769         if (this.active && !state && this.navId) {
4770             this.was_active = true;
4771             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4772             if (nv) {
4773                 nv.clearWasActive(this);
4774             }
4775             
4776         }
4777         this.active = state;
4778         
4779         if (!state ) {
4780             this.el.removeClass('active');
4781         } else if (!this.el.hasClass('active')) {
4782             this.el.addClass('active');
4783         }
4784         if (fire) {
4785             this.fireEvent('changed', this, state);
4786         }
4787         
4788         // show a panel if it's registered and related..
4789         
4790         if (!this.navId || !this.tabId || !state || is_was_active) {
4791             return;
4792         }
4793         
4794         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4795         if (!tg) {
4796             return;
4797         }
4798         var pan = tg.getPanelByName(this.tabId);
4799         if (!pan) {
4800             return;
4801         }
4802         // if we can not flip to new panel - go back to old nav highlight..
4803         if (false == tg.showPanel(pan)) {
4804             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4805             if (nv) {
4806                 var onav = nv.getWasActive();
4807                 if (onav) {
4808                     onav.setActive(true, false, true);
4809                 }
4810             }
4811             
4812         }
4813         
4814         
4815         
4816     },
4817      // this should not be here...
4818     setDisabled : function(state)
4819     {
4820         this.disabled = state;
4821         if (!state ) {
4822             this.el.removeClass('disabled');
4823         } else if (!this.el.hasClass('disabled')) {
4824             this.el.addClass('disabled');
4825         }
4826         
4827     },
4828     
4829     /**
4830      * Fetch the element to display the tooltip on.
4831      * @return {Roo.Element} defaults to this.el
4832      */
4833     tooltipEl : function()
4834     {
4835         return this.el.select('' + this.tagtype + '', true).first();
4836     },
4837     
4838     scrollToElement : function(e)
4839     {
4840         var c = document.body;
4841         
4842         /*
4843          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4844          */
4845         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4846             c = document.documentElement;
4847         }
4848         
4849         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4850         
4851         if(!target){
4852             return;
4853         }
4854
4855         var o = target.calcOffsetsTo(c);
4856         
4857         var options = {
4858             target : target,
4859             value : o[1]
4860         };
4861         
4862         this.fireEvent('scrollto', this, options, e);
4863         
4864         Roo.get(c).scrollTo('top', options.value, true);
4865         
4866         return;
4867     }
4868 });
4869  
4870
4871  /*
4872  * - LGPL
4873  *
4874  * sidebar item
4875  *
4876  *  li
4877  *    <span> icon </span>
4878  *    <span> text </span>
4879  *    <span>badge </span>
4880  */
4881
4882 /**
4883  * @class Roo.bootstrap.NavSidebarItem
4884  * @extends Roo.bootstrap.NavItem
4885  * Bootstrap Navbar.NavSidebarItem class
4886  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4887  * {Boolean} open is the menu open
4888  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4889  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4890  * {String} buttonSize (sm|md|lg)the extra classes for the button
4891  * {Boolean} showArrow show arrow next to the text (default true)
4892  * @constructor
4893  * Create a new Navbar Button
4894  * @param {Object} config The config object
4895  */
4896 Roo.bootstrap.NavSidebarItem = function(config){
4897     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4898     this.addEvents({
4899         // raw events
4900         /**
4901          * @event click
4902          * The raw click event for the entire grid.
4903          * @param {Roo.EventObject} e
4904          */
4905         "click" : true,
4906          /**
4907             * @event changed
4908             * Fires when the active item active state changes
4909             * @param {Roo.bootstrap.NavSidebarItem} this
4910             * @param {boolean} state the new state
4911              
4912          */
4913         'changed': true
4914     });
4915    
4916 };
4917
4918 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4919     
4920     badgeWeight : 'default',
4921     
4922     open: false,
4923     
4924     buttonView : false,
4925     
4926     buttonWeight : 'default',
4927     
4928     buttonSize : 'md',
4929     
4930     showArrow : true,
4931     
4932     getAutoCreate : function(){
4933         
4934         
4935         var a = {
4936                 tag: 'a',
4937                 href : this.href || '#',
4938                 cls: '',
4939                 html : '',
4940                 cn : []
4941         };
4942         
4943         if(this.buttonView){
4944             a = {
4945                 tag: 'button',
4946                 href : this.href || '#',
4947                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4948                 html : this.html,
4949                 cn : []
4950             };
4951         }
4952         
4953         var cfg = {
4954             tag: 'li',
4955             cls: '',
4956             cn: [ a ]
4957         };
4958         
4959         if (this.active) {
4960             cfg.cls += ' active';
4961         }
4962         
4963         if (this.disabled) {
4964             cfg.cls += ' disabled';
4965         }
4966         if (this.open) {
4967             cfg.cls += ' open x-open';
4968         }
4969         // left icon..
4970         if (this.glyphicon || this.icon) {
4971             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4972             a.cn.push({ tag : 'i', cls : c }) ;
4973         }
4974         
4975         if(!this.buttonView){
4976             var span = {
4977                 tag: 'span',
4978                 html : this.html || ''
4979             };
4980
4981             a.cn.push(span);
4982             
4983         }
4984         
4985         if (this.badge !== '') {
4986             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4987         }
4988         
4989         if (this.menu) {
4990             
4991             if(this.showArrow){
4992                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4993             }
4994             
4995             a.cls += ' dropdown-toggle treeview' ;
4996         }
4997         
4998         return cfg;
4999     },
5000     
5001     initEvents : function()
5002     { 
5003         if (typeof (this.menu) != 'undefined') {
5004             this.menu.parentType = this.xtype;
5005             this.menu.triggerEl = this.el;
5006             this.menu = this.addxtype(Roo.apply({}, this.menu));
5007         }
5008         
5009         this.el.on('click', this.onClick, this);
5010         
5011         if(this.badge !== ''){
5012             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5013         }
5014         
5015     },
5016     
5017     onClick : function(e)
5018     {
5019         if(this.disabled){
5020             e.preventDefault();
5021             return;
5022         }
5023         
5024         if(this.preventDefault){
5025             e.preventDefault();
5026         }
5027         
5028         this.fireEvent('click', this);
5029     },
5030     
5031     disable : function()
5032     {
5033         this.setDisabled(true);
5034     },
5035     
5036     enable : function()
5037     {
5038         this.setDisabled(false);
5039     },
5040     
5041     setDisabled : function(state)
5042     {
5043         if(this.disabled == state){
5044             return;
5045         }
5046         
5047         this.disabled = state;
5048         
5049         if (state) {
5050             this.el.addClass('disabled');
5051             return;
5052         }
5053         
5054         this.el.removeClass('disabled');
5055         
5056         return;
5057     },
5058     
5059     setActive : function(state)
5060     {
5061         if(this.active == state){
5062             return;
5063         }
5064         
5065         this.active = state;
5066         
5067         if (state) {
5068             this.el.addClass('active');
5069             return;
5070         }
5071         
5072         this.el.removeClass('active');
5073         
5074         return;
5075     },
5076     
5077     isActive: function () 
5078     {
5079         return this.active;
5080     },
5081     
5082     setBadge : function(str)
5083     {
5084         if(!this.badgeEl){
5085             return;
5086         }
5087         
5088         this.badgeEl.dom.innerHTML = str;
5089     }
5090     
5091    
5092      
5093  
5094 });
5095  
5096
5097  /*
5098  * - LGPL
5099  *
5100  * row
5101  * 
5102  */
5103
5104 /**
5105  * @class Roo.bootstrap.Row
5106  * @extends Roo.bootstrap.Component
5107  * Bootstrap Row class (contains columns...)
5108  * 
5109  * @constructor
5110  * Create a new Row
5111  * @param {Object} config The config object
5112  */
5113
5114 Roo.bootstrap.Row = function(config){
5115     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5116 };
5117
5118 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5119     
5120     getAutoCreate : function(){
5121        return {
5122             cls: 'row clearfix'
5123        };
5124     }
5125     
5126     
5127 });
5128
5129  
5130
5131  /*
5132  * - LGPL
5133  *
5134  * element
5135  * 
5136  */
5137
5138 /**
5139  * @class Roo.bootstrap.Element
5140  * @extends Roo.bootstrap.Component
5141  * Bootstrap Element class
5142  * @cfg {String} html contents of the element
5143  * @cfg {String} tag tag of the element
5144  * @cfg {String} cls class of the element
5145  * @cfg {Boolean} preventDefault (true|false) default false
5146  * @cfg {Boolean} clickable (true|false) default false
5147  * 
5148  * @constructor
5149  * Create a new Element
5150  * @param {Object} config The config object
5151  */
5152
5153 Roo.bootstrap.Element = function(config){
5154     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5155     
5156     this.addEvents({
5157         // raw events
5158         /**
5159          * @event click
5160          * When a element is chick
5161          * @param {Roo.bootstrap.Element} this
5162          * @param {Roo.EventObject} e
5163          */
5164         "click" : true
5165     });
5166 };
5167
5168 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5169     
5170     tag: 'div',
5171     cls: '',
5172     html: '',
5173     preventDefault: false, 
5174     clickable: false,
5175     
5176     getAutoCreate : function(){
5177         
5178         var cfg = {
5179             tag: this.tag,
5180             // cls: this.cls, double assign in parent class Component.js :: onRender
5181             html: this.html
5182         };
5183         
5184         return cfg;
5185     },
5186     
5187     initEvents: function() 
5188     {
5189         Roo.bootstrap.Element.superclass.initEvents.call(this);
5190         
5191         if(this.clickable){
5192             this.el.on('click', this.onClick, this);
5193         }
5194         
5195     },
5196     
5197     onClick : function(e)
5198     {
5199         if(this.preventDefault){
5200             e.preventDefault();
5201         }
5202         
5203         this.fireEvent('click', this, e);
5204     },
5205     
5206     getValue : function()
5207     {
5208         return this.el.dom.innerHTML;
5209     },
5210     
5211     setValue : function(value)
5212     {
5213         this.el.dom.innerHTML = value;
5214     }
5215    
5216 });
5217
5218  
5219
5220  /*
5221  * - LGPL
5222  *
5223  * pagination
5224  * 
5225  */
5226
5227 /**
5228  * @class Roo.bootstrap.Pagination
5229  * @extends Roo.bootstrap.Component
5230  * Bootstrap Pagination class
5231  * @cfg {String} size xs | sm | md | lg
5232  * @cfg {Boolean} inverse false | true
5233  * 
5234  * @constructor
5235  * Create a new Pagination
5236  * @param {Object} config The config object
5237  */
5238
5239 Roo.bootstrap.Pagination = function(config){
5240     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5241 };
5242
5243 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5244     
5245     cls: false,
5246     size: false,
5247     inverse: false,
5248     
5249     getAutoCreate : function(){
5250         var cfg = {
5251             tag: 'ul',
5252                 cls: 'pagination'
5253         };
5254         if (this.inverse) {
5255             cfg.cls += ' inverse';
5256         }
5257         if (this.html) {
5258             cfg.html=this.html;
5259         }
5260         if (this.cls) {
5261             cfg.cls += " " + this.cls;
5262         }
5263         return cfg;
5264     }
5265    
5266 });
5267
5268  
5269
5270  /*
5271  * - LGPL
5272  *
5273  * Pagination item
5274  * 
5275  */
5276
5277
5278 /**
5279  * @class Roo.bootstrap.PaginationItem
5280  * @extends Roo.bootstrap.Component
5281  * Bootstrap PaginationItem class
5282  * @cfg {String} html text
5283  * @cfg {String} href the link
5284  * @cfg {Boolean} preventDefault (true | false) default true
5285  * @cfg {Boolean} active (true | false) default false
5286  * @cfg {Boolean} disabled default false
5287  * 
5288  * 
5289  * @constructor
5290  * Create a new PaginationItem
5291  * @param {Object} config The config object
5292  */
5293
5294
5295 Roo.bootstrap.PaginationItem = function(config){
5296     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5297     this.addEvents({
5298         // raw events
5299         /**
5300          * @event click
5301          * The raw click event for the entire grid.
5302          * @param {Roo.EventObject} e
5303          */
5304         "click" : true
5305     });
5306 };
5307
5308 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5309     
5310     href : false,
5311     html : false,
5312     preventDefault: true,
5313     active : false,
5314     cls : false,
5315     disabled: false,
5316     
5317     getAutoCreate : function(){
5318         var cfg= {
5319             tag: 'li',
5320             cn: [
5321                 {
5322                     tag : 'a',
5323                     href : this.href ? this.href : '#',
5324                     html : this.html ? this.html : ''
5325                 }
5326             ]
5327         };
5328         
5329         if(this.cls){
5330             cfg.cls = this.cls;
5331         }
5332         
5333         if(this.disabled){
5334             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5335         }
5336         
5337         if(this.active){
5338             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5339         }
5340         
5341         return cfg;
5342     },
5343     
5344     initEvents: function() {
5345         
5346         this.el.on('click', this.onClick, this);
5347         
5348     },
5349     onClick : function(e)
5350     {
5351         Roo.log('PaginationItem on click ');
5352         if(this.preventDefault){
5353             e.preventDefault();
5354         }
5355         
5356         if(this.disabled){
5357             return;
5358         }
5359         
5360         this.fireEvent('click', this, e);
5361     }
5362    
5363 });
5364
5365  
5366
5367  /*
5368  * - LGPL
5369  *
5370  * slider
5371  * 
5372  */
5373
5374
5375 /**
5376  * @class Roo.bootstrap.Slider
5377  * @extends Roo.bootstrap.Component
5378  * Bootstrap Slider class
5379  *    
5380  * @constructor
5381  * Create a new Slider
5382  * @param {Object} config The config object
5383  */
5384
5385 Roo.bootstrap.Slider = function(config){
5386     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5387 };
5388
5389 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5390     
5391     getAutoCreate : function(){
5392         
5393         var cfg = {
5394             tag: 'div',
5395             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5396             cn: [
5397                 {
5398                     tag: 'a',
5399                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5400                 }
5401             ]
5402         };
5403         
5404         return cfg;
5405     }
5406    
5407 });
5408
5409  /*
5410  * Based on:
5411  * Ext JS Library 1.1.1
5412  * Copyright(c) 2006-2007, Ext JS, LLC.
5413  *
5414  * Originally Released Under LGPL - original licence link has changed is not relivant.
5415  *
5416  * Fork - LGPL
5417  * <script type="text/javascript">
5418  */
5419  
5420
5421 /**
5422  * @class Roo.grid.ColumnModel
5423  * @extends Roo.util.Observable
5424  * This is the default implementation of a ColumnModel used by the Grid. It defines
5425  * the columns in the grid.
5426  * <br>Usage:<br>
5427  <pre><code>
5428  var colModel = new Roo.grid.ColumnModel([
5429         {header: "Ticker", width: 60, sortable: true, locked: true},
5430         {header: "Company Name", width: 150, sortable: true},
5431         {header: "Market Cap.", width: 100, sortable: true},
5432         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5433         {header: "Employees", width: 100, sortable: true, resizable: false}
5434  ]);
5435  </code></pre>
5436  * <p>
5437  
5438  * The config options listed for this class are options which may appear in each
5439  * individual column definition.
5440  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5441  * @constructor
5442  * @param {Object} config An Array of column config objects. See this class's
5443  * config objects for details.
5444 */
5445 Roo.grid.ColumnModel = function(config){
5446         /**
5447      * The config passed into the constructor
5448      */
5449     this.config = config;
5450     this.lookup = {};
5451
5452     // if no id, create one
5453     // if the column does not have a dataIndex mapping,
5454     // map it to the order it is in the config
5455     for(var i = 0, len = config.length; i < len; i++){
5456         var c = config[i];
5457         if(typeof c.dataIndex == "undefined"){
5458             c.dataIndex = i;
5459         }
5460         if(typeof c.renderer == "string"){
5461             c.renderer = Roo.util.Format[c.renderer];
5462         }
5463         if(typeof c.id == "undefined"){
5464             c.id = Roo.id();
5465         }
5466         if(c.editor && c.editor.xtype){
5467             c.editor  = Roo.factory(c.editor, Roo.grid);
5468         }
5469         if(c.editor && c.editor.isFormField){
5470             c.editor = new Roo.grid.GridEditor(c.editor);
5471         }
5472         this.lookup[c.id] = c;
5473     }
5474
5475     /**
5476      * The width of columns which have no width specified (defaults to 100)
5477      * @type Number
5478      */
5479     this.defaultWidth = 100;
5480
5481     /**
5482      * Default sortable of columns which have no sortable specified (defaults to false)
5483      * @type Boolean
5484      */
5485     this.defaultSortable = false;
5486
5487     this.addEvents({
5488         /**
5489              * @event widthchange
5490              * Fires when the width of a column changes.
5491              * @param {ColumnModel} this
5492              * @param {Number} columnIndex The column index
5493              * @param {Number} newWidth The new width
5494              */
5495             "widthchange": true,
5496         /**
5497              * @event headerchange
5498              * Fires when the text of a header changes.
5499              * @param {ColumnModel} this
5500              * @param {Number} columnIndex The column index
5501              * @param {Number} newText The new header text
5502              */
5503             "headerchange": true,
5504         /**
5505              * @event hiddenchange
5506              * Fires when a column is hidden or "unhidden".
5507              * @param {ColumnModel} this
5508              * @param {Number} columnIndex The column index
5509              * @param {Boolean} hidden true if hidden, false otherwise
5510              */
5511             "hiddenchange": true,
5512             /**
5513          * @event columnmoved
5514          * Fires when a column is moved.
5515          * @param {ColumnModel} this
5516          * @param {Number} oldIndex
5517          * @param {Number} newIndex
5518          */
5519         "columnmoved" : true,
5520         /**
5521          * @event columlockchange
5522          * Fires when a column's locked state is changed
5523          * @param {ColumnModel} this
5524          * @param {Number} colIndex
5525          * @param {Boolean} locked true if locked
5526          */
5527         "columnlockchange" : true
5528     });
5529     Roo.grid.ColumnModel.superclass.constructor.call(this);
5530 };
5531 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5532     /**
5533      * @cfg {String} header The header text to display in the Grid view.
5534      */
5535     /**
5536      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5537      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5538      * specified, the column's index is used as an index into the Record's data Array.
5539      */
5540     /**
5541      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5542      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5543      */
5544     /**
5545      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5546      * Defaults to the value of the {@link #defaultSortable} property.
5547      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5548      */
5549     /**
5550      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5551      */
5552     /**
5553      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5554      */
5555     /**
5556      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5557      */
5558     /**
5559      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5560      */
5561     /**
5562      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5563      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5564      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5565      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5566      */
5567        /**
5568      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5569      */
5570     /**
5571      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5572      */
5573     /**
5574      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5575      */
5576     /**
5577      * @cfg {String} cursor (Optional)
5578      */
5579     /**
5580      * @cfg {String} tooltip (Optional)
5581      */
5582     /**
5583      * @cfg {Number} xs (Optional)
5584      */
5585     /**
5586      * @cfg {Number} sm (Optional)
5587      */
5588     /**
5589      * @cfg {Number} md (Optional)
5590      */
5591     /**
5592      * @cfg {Number} lg (Optional)
5593      */
5594     /**
5595      * Returns the id of the column at the specified index.
5596      * @param {Number} index The column index
5597      * @return {String} the id
5598      */
5599     getColumnId : function(index){
5600         return this.config[index].id;
5601     },
5602
5603     /**
5604      * Returns the column for a specified id.
5605      * @param {String} id The column id
5606      * @return {Object} the column
5607      */
5608     getColumnById : function(id){
5609         return this.lookup[id];
5610     },
5611
5612     
5613     /**
5614      * Returns the column for a specified dataIndex.
5615      * @param {String} dataIndex The column dataIndex
5616      * @return {Object|Boolean} the column or false if not found
5617      */
5618     getColumnByDataIndex: function(dataIndex){
5619         var index = this.findColumnIndex(dataIndex);
5620         return index > -1 ? this.config[index] : false;
5621     },
5622     
5623     /**
5624      * Returns the index for a specified column id.
5625      * @param {String} id The column id
5626      * @return {Number} the index, or -1 if not found
5627      */
5628     getIndexById : function(id){
5629         for(var i = 0, len = this.config.length; i < len; i++){
5630             if(this.config[i].id == id){
5631                 return i;
5632             }
5633         }
5634         return -1;
5635     },
5636     
5637     /**
5638      * Returns the index for a specified column dataIndex.
5639      * @param {String} dataIndex The column dataIndex
5640      * @return {Number} the index, or -1 if not found
5641      */
5642     
5643     findColumnIndex : function(dataIndex){
5644         for(var i = 0, len = this.config.length; i < len; i++){
5645             if(this.config[i].dataIndex == dataIndex){
5646                 return i;
5647             }
5648         }
5649         return -1;
5650     },
5651     
5652     
5653     moveColumn : function(oldIndex, newIndex){
5654         var c = this.config[oldIndex];
5655         this.config.splice(oldIndex, 1);
5656         this.config.splice(newIndex, 0, c);
5657         this.dataMap = null;
5658         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5659     },
5660
5661     isLocked : function(colIndex){
5662         return this.config[colIndex].locked === true;
5663     },
5664
5665     setLocked : function(colIndex, value, suppressEvent){
5666         if(this.isLocked(colIndex) == value){
5667             return;
5668         }
5669         this.config[colIndex].locked = value;
5670         if(!suppressEvent){
5671             this.fireEvent("columnlockchange", this, colIndex, value);
5672         }
5673     },
5674
5675     getTotalLockedWidth : function(){
5676         var totalWidth = 0;
5677         for(var i = 0; i < this.config.length; i++){
5678             if(this.isLocked(i) && !this.isHidden(i)){
5679                 this.totalWidth += this.getColumnWidth(i);
5680             }
5681         }
5682         return totalWidth;
5683     },
5684
5685     getLockedCount : function(){
5686         for(var i = 0, len = this.config.length; i < len; i++){
5687             if(!this.isLocked(i)){
5688                 return i;
5689             }
5690         }
5691         
5692         return this.config.length;
5693     },
5694
5695     /**
5696      * Returns the number of columns.
5697      * @return {Number}
5698      */
5699     getColumnCount : function(visibleOnly){
5700         if(visibleOnly === true){
5701             var c = 0;
5702             for(var i = 0, len = this.config.length; i < len; i++){
5703                 if(!this.isHidden(i)){
5704                     c++;
5705                 }
5706             }
5707             return c;
5708         }
5709         return this.config.length;
5710     },
5711
5712     /**
5713      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5714      * @param {Function} fn
5715      * @param {Object} scope (optional)
5716      * @return {Array} result
5717      */
5718     getColumnsBy : function(fn, scope){
5719         var r = [];
5720         for(var i = 0, len = this.config.length; i < len; i++){
5721             var c = this.config[i];
5722             if(fn.call(scope||this, c, i) === true){
5723                 r[r.length] = c;
5724             }
5725         }
5726         return r;
5727     },
5728
5729     /**
5730      * Returns true if the specified column is sortable.
5731      * @param {Number} col The column index
5732      * @return {Boolean}
5733      */
5734     isSortable : function(col){
5735         if(typeof this.config[col].sortable == "undefined"){
5736             return this.defaultSortable;
5737         }
5738         return this.config[col].sortable;
5739     },
5740
5741     /**
5742      * Returns the rendering (formatting) function defined for the column.
5743      * @param {Number} col The column index.
5744      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5745      */
5746     getRenderer : function(col){
5747         if(!this.config[col].renderer){
5748             return Roo.grid.ColumnModel.defaultRenderer;
5749         }
5750         return this.config[col].renderer;
5751     },
5752
5753     /**
5754      * Sets the rendering (formatting) function for a column.
5755      * @param {Number} col The column index
5756      * @param {Function} fn The function to use to process the cell's raw data
5757      * to return HTML markup for the grid view. The render function is called with
5758      * the following parameters:<ul>
5759      * <li>Data value.</li>
5760      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5761      * <li>css A CSS style string to apply to the table cell.</li>
5762      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5763      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5764      * <li>Row index</li>
5765      * <li>Column index</li>
5766      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5767      */
5768     setRenderer : function(col, fn){
5769         this.config[col].renderer = fn;
5770     },
5771
5772     /**
5773      * Returns the width for the specified column.
5774      * @param {Number} col The column index
5775      * @return {Number}
5776      */
5777     getColumnWidth : function(col){
5778         return this.config[col].width * 1 || this.defaultWidth;
5779     },
5780
5781     /**
5782      * Sets the width for a column.
5783      * @param {Number} col The column index
5784      * @param {Number} width The new width
5785      */
5786     setColumnWidth : function(col, width, suppressEvent){
5787         this.config[col].width = width;
5788         this.totalWidth = null;
5789         if(!suppressEvent){
5790              this.fireEvent("widthchange", this, col, width);
5791         }
5792     },
5793
5794     /**
5795      * Returns the total width of all columns.
5796      * @param {Boolean} includeHidden True to include hidden column widths
5797      * @return {Number}
5798      */
5799     getTotalWidth : function(includeHidden){
5800         if(!this.totalWidth){
5801             this.totalWidth = 0;
5802             for(var i = 0, len = this.config.length; i < len; i++){
5803                 if(includeHidden || !this.isHidden(i)){
5804                     this.totalWidth += this.getColumnWidth(i);
5805                 }
5806             }
5807         }
5808         return this.totalWidth;
5809     },
5810
5811     /**
5812      * Returns the header for the specified column.
5813      * @param {Number} col The column index
5814      * @return {String}
5815      */
5816     getColumnHeader : function(col){
5817         return this.config[col].header;
5818     },
5819
5820     /**
5821      * Sets the header for a column.
5822      * @param {Number} col The column index
5823      * @param {String} header The new header
5824      */
5825     setColumnHeader : function(col, header){
5826         this.config[col].header = header;
5827         this.fireEvent("headerchange", this, col, header);
5828     },
5829
5830     /**
5831      * Returns the tooltip for the specified column.
5832      * @param {Number} col The column index
5833      * @return {String}
5834      */
5835     getColumnTooltip : function(col){
5836             return this.config[col].tooltip;
5837     },
5838     /**
5839      * Sets the tooltip for a column.
5840      * @param {Number} col The column index
5841      * @param {String} tooltip The new tooltip
5842      */
5843     setColumnTooltip : function(col, tooltip){
5844             this.config[col].tooltip = tooltip;
5845     },
5846
5847     /**
5848      * Returns the dataIndex for the specified column.
5849      * @param {Number} col The column index
5850      * @return {Number}
5851      */
5852     getDataIndex : function(col){
5853         return this.config[col].dataIndex;
5854     },
5855
5856     /**
5857      * Sets the dataIndex for a column.
5858      * @param {Number} col The column index
5859      * @param {Number} dataIndex The new dataIndex
5860      */
5861     setDataIndex : function(col, dataIndex){
5862         this.config[col].dataIndex = dataIndex;
5863     },
5864
5865     
5866     
5867     /**
5868      * Returns true if the cell is editable.
5869      * @param {Number} colIndex The column index
5870      * @param {Number} rowIndex The row index - this is nto actually used..?
5871      * @return {Boolean}
5872      */
5873     isCellEditable : function(colIndex, rowIndex){
5874         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5875     },
5876
5877     /**
5878      * Returns the editor defined for the cell/column.
5879      * return false or null to disable editing.
5880      * @param {Number} colIndex The column index
5881      * @param {Number} rowIndex The row index
5882      * @return {Object}
5883      */
5884     getCellEditor : function(colIndex, rowIndex){
5885         return this.config[colIndex].editor;
5886     },
5887
5888     /**
5889      * Sets if a column is editable.
5890      * @param {Number} col The column index
5891      * @param {Boolean} editable True if the column is editable
5892      */
5893     setEditable : function(col, editable){
5894         this.config[col].editable = editable;
5895     },
5896
5897
5898     /**
5899      * Returns true if the column is hidden.
5900      * @param {Number} colIndex The column index
5901      * @return {Boolean}
5902      */
5903     isHidden : function(colIndex){
5904         return this.config[colIndex].hidden;
5905     },
5906
5907
5908     /**
5909      * Returns true if the column width cannot be changed
5910      */
5911     isFixed : function(colIndex){
5912         return this.config[colIndex].fixed;
5913     },
5914
5915     /**
5916      * Returns true if the column can be resized
5917      * @return {Boolean}
5918      */
5919     isResizable : function(colIndex){
5920         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5921     },
5922     /**
5923      * Sets if a column is hidden.
5924      * @param {Number} colIndex The column index
5925      * @param {Boolean} hidden True if the column is hidden
5926      */
5927     setHidden : function(colIndex, hidden){
5928         this.config[colIndex].hidden = hidden;
5929         this.totalWidth = null;
5930         this.fireEvent("hiddenchange", this, colIndex, hidden);
5931     },
5932
5933     /**
5934      * Sets the editor for a column.
5935      * @param {Number} col The column index
5936      * @param {Object} editor The editor object
5937      */
5938     setEditor : function(col, editor){
5939         this.config[col].editor = editor;
5940     }
5941 });
5942
5943 Roo.grid.ColumnModel.defaultRenderer = function(value)
5944 {
5945     if(typeof value == "object") {
5946         return value;
5947     }
5948         if(typeof value == "string" && value.length < 1){
5949             return "&#160;";
5950         }
5951     
5952         return String.format("{0}", value);
5953 };
5954
5955 // Alias for backwards compatibility
5956 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5957 /*
5958  * Based on:
5959  * Ext JS Library 1.1.1
5960  * Copyright(c) 2006-2007, Ext JS, LLC.
5961  *
5962  * Originally Released Under LGPL - original licence link has changed is not relivant.
5963  *
5964  * Fork - LGPL
5965  * <script type="text/javascript">
5966  */
5967  
5968 /**
5969  * @class Roo.LoadMask
5970  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5971  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5972  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5973  * element's UpdateManager load indicator and will be destroyed after the initial load.
5974  * @constructor
5975  * Create a new LoadMask
5976  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5977  * @param {Object} config The config object
5978  */
5979 Roo.LoadMask = function(el, config){
5980     this.el = Roo.get(el);
5981     Roo.apply(this, config);
5982     if(this.store){
5983         this.store.on('beforeload', this.onBeforeLoad, this);
5984         this.store.on('load', this.onLoad, this);
5985         this.store.on('loadexception', this.onLoadException, this);
5986         this.removeMask = false;
5987     }else{
5988         var um = this.el.getUpdateManager();
5989         um.showLoadIndicator = false; // disable the default indicator
5990         um.on('beforeupdate', this.onBeforeLoad, this);
5991         um.on('update', this.onLoad, this);
5992         um.on('failure', this.onLoad, this);
5993         this.removeMask = true;
5994     }
5995 };
5996
5997 Roo.LoadMask.prototype = {
5998     /**
5999      * @cfg {Boolean} removeMask
6000      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6001      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6002      */
6003     /**
6004      * @cfg {String} msg
6005      * The text to display in a centered loading message box (defaults to 'Loading...')
6006      */
6007     msg : 'Loading...',
6008     /**
6009      * @cfg {String} msgCls
6010      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6011      */
6012     msgCls : 'x-mask-loading',
6013
6014     /**
6015      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6016      * @type Boolean
6017      */
6018     disabled: false,
6019
6020     /**
6021      * Disables the mask to prevent it from being displayed
6022      */
6023     disable : function(){
6024        this.disabled = true;
6025     },
6026
6027     /**
6028      * Enables the mask so that it can be displayed
6029      */
6030     enable : function(){
6031         this.disabled = false;
6032     },
6033     
6034     onLoadException : function()
6035     {
6036         Roo.log(arguments);
6037         
6038         if (typeof(arguments[3]) != 'undefined') {
6039             Roo.MessageBox.alert("Error loading",arguments[3]);
6040         } 
6041         /*
6042         try {
6043             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6044                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6045             }   
6046         } catch(e) {
6047             
6048         }
6049         */
6050     
6051         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6052     },
6053     // private
6054     onLoad : function()
6055     {
6056         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6057     },
6058
6059     // private
6060     onBeforeLoad : function(){
6061         if(!this.disabled){
6062             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6063         }
6064     },
6065
6066     // private
6067     destroy : function(){
6068         if(this.store){
6069             this.store.un('beforeload', this.onBeforeLoad, this);
6070             this.store.un('load', this.onLoad, this);
6071             this.store.un('loadexception', this.onLoadException, this);
6072         }else{
6073             var um = this.el.getUpdateManager();
6074             um.un('beforeupdate', this.onBeforeLoad, this);
6075             um.un('update', this.onLoad, this);
6076             um.un('failure', this.onLoad, this);
6077         }
6078     }
6079 };/*
6080  * - LGPL
6081  *
6082  * table
6083  * 
6084  */
6085
6086 /**
6087  * @class Roo.bootstrap.Table
6088  * @extends Roo.bootstrap.Component
6089  * Bootstrap Table class
6090  * @cfg {String} cls table class
6091  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6092  * @cfg {String} bgcolor Specifies the background color for a table
6093  * @cfg {Number} border Specifies whether the table cells should have borders or not
6094  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6095  * @cfg {Number} cellspacing Specifies the space between cells
6096  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6097  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6098  * @cfg {String} sortable Specifies that the table should be sortable
6099  * @cfg {String} summary Specifies a summary of the content of a table
6100  * @cfg {Number} width Specifies the width of a table
6101  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6102  * 
6103  * @cfg {boolean} striped Should the rows be alternative striped
6104  * @cfg {boolean} bordered Add borders to the table
6105  * @cfg {boolean} hover Add hover highlighting
6106  * @cfg {boolean} condensed Format condensed
6107  * @cfg {boolean} responsive Format condensed
6108  * @cfg {Boolean} loadMask (true|false) default false
6109  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6110  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6111  * @cfg {Boolean} rowSelection (true|false) default false
6112  * @cfg {Boolean} cellSelection (true|false) default false
6113  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6114  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6115  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6116  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6117  
6118  * 
6119  * @constructor
6120  * Create a new Table
6121  * @param {Object} config The config object
6122  */
6123
6124 Roo.bootstrap.Table = function(config){
6125     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6126     
6127   
6128     
6129     // BC...
6130     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6131     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6132     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6133     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6134     
6135     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6136     if (this.sm) {
6137         this.sm.grid = this;
6138         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6139         this.sm = this.selModel;
6140         this.sm.xmodule = this.xmodule || false;
6141     }
6142     
6143     if (this.cm && typeof(this.cm.config) == 'undefined') {
6144         this.colModel = new Roo.grid.ColumnModel(this.cm);
6145         this.cm = this.colModel;
6146         this.cm.xmodule = this.xmodule || false;
6147     }
6148     if (this.store) {
6149         this.store= Roo.factory(this.store, Roo.data);
6150         this.ds = this.store;
6151         this.ds.xmodule = this.xmodule || false;
6152          
6153     }
6154     if (this.footer && this.store) {
6155         this.footer.dataSource = this.ds;
6156         this.footer = Roo.factory(this.footer);
6157     }
6158     
6159     /** @private */
6160     this.addEvents({
6161         /**
6162          * @event cellclick
6163          * Fires when a cell is clicked
6164          * @param {Roo.bootstrap.Table} this
6165          * @param {Roo.Element} el
6166          * @param {Number} rowIndex
6167          * @param {Number} columnIndex
6168          * @param {Roo.EventObject} e
6169          */
6170         "cellclick" : true,
6171         /**
6172          * @event celldblclick
6173          * Fires when a cell is double clicked
6174          * @param {Roo.bootstrap.Table} this
6175          * @param {Roo.Element} el
6176          * @param {Number} rowIndex
6177          * @param {Number} columnIndex
6178          * @param {Roo.EventObject} e
6179          */
6180         "celldblclick" : true,
6181         /**
6182          * @event rowclick
6183          * Fires when a row is clicked
6184          * @param {Roo.bootstrap.Table} this
6185          * @param {Roo.Element} el
6186          * @param {Number} rowIndex
6187          * @param {Roo.EventObject} e
6188          */
6189         "rowclick" : true,
6190         /**
6191          * @event rowdblclick
6192          * Fires when a row is double clicked
6193          * @param {Roo.bootstrap.Table} this
6194          * @param {Roo.Element} el
6195          * @param {Number} rowIndex
6196          * @param {Roo.EventObject} e
6197          */
6198         "rowdblclick" : true,
6199         /**
6200          * @event mouseover
6201          * Fires when a mouseover occur
6202          * @param {Roo.bootstrap.Table} this
6203          * @param {Roo.Element} el
6204          * @param {Number} rowIndex
6205          * @param {Number} columnIndex
6206          * @param {Roo.EventObject} e
6207          */
6208         "mouseover" : true,
6209         /**
6210          * @event mouseout
6211          * Fires when a mouseout occur
6212          * @param {Roo.bootstrap.Table} this
6213          * @param {Roo.Element} el
6214          * @param {Number} rowIndex
6215          * @param {Number} columnIndex
6216          * @param {Roo.EventObject} e
6217          */
6218         "mouseout" : true,
6219         /**
6220          * @event rowclass
6221          * Fires when a row is rendered, so you can change add a style to it.
6222          * @param {Roo.bootstrap.Table} this
6223          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6224          */
6225         'rowclass' : true,
6226           /**
6227          * @event rowsrendered
6228          * Fires when all the  rows have been rendered
6229          * @param {Roo.bootstrap.Table} this
6230          */
6231         'rowsrendered' : true,
6232         /**
6233          * @event contextmenu
6234          * The raw contextmenu event for the entire grid.
6235          * @param {Roo.EventObject} e
6236          */
6237         "contextmenu" : true,
6238         /**
6239          * @event rowcontextmenu
6240          * Fires when a row is right clicked
6241          * @param {Roo.bootstrap.Table} this
6242          * @param {Number} rowIndex
6243          * @param {Roo.EventObject} e
6244          */
6245         "rowcontextmenu" : true,
6246         /**
6247          * @event cellcontextmenu
6248          * Fires when a cell is right clicked
6249          * @param {Roo.bootstrap.Table} this
6250          * @param {Number} rowIndex
6251          * @param {Number} cellIndex
6252          * @param {Roo.EventObject} e
6253          */
6254          "cellcontextmenu" : true,
6255          /**
6256          * @event headercontextmenu
6257          * Fires when a header is right clicked
6258          * @param {Roo.bootstrap.Table} this
6259          * @param {Number} columnIndex
6260          * @param {Roo.EventObject} e
6261          */
6262         "headercontextmenu" : true
6263     });
6264 };
6265
6266 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6267     
6268     cls: false,
6269     align: false,
6270     bgcolor: false,
6271     border: false,
6272     cellpadding: false,
6273     cellspacing: false,
6274     frame: false,
6275     rules: false,
6276     sortable: false,
6277     summary: false,
6278     width: false,
6279     striped : false,
6280     scrollBody : false,
6281     bordered: false,
6282     hover:  false,
6283     condensed : false,
6284     responsive : false,
6285     sm : false,
6286     cm : false,
6287     store : false,
6288     loadMask : false,
6289     footerShow : true,
6290     headerShow : true,
6291   
6292     rowSelection : false,
6293     cellSelection : false,
6294     layout : false,
6295     
6296     // Roo.Element - the tbody
6297     mainBody: false,
6298     // Roo.Element - thead element
6299     mainHead: false,
6300     
6301     container: false, // used by gridpanel...
6302     
6303     lazyLoad : false,
6304     
6305     CSS : Roo.util.CSS,
6306     
6307     auto_hide_footer : false,
6308     
6309     getAutoCreate : function()
6310     {
6311         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6312         
6313         cfg = {
6314             tag: 'table',
6315             cls : 'table',
6316             cn : []
6317         };
6318         if (this.scrollBody) {
6319             cfg.cls += ' table-body-fixed';
6320         }    
6321         if (this.striped) {
6322             cfg.cls += ' table-striped';
6323         }
6324         
6325         if (this.hover) {
6326             cfg.cls += ' table-hover';
6327         }
6328         if (this.bordered) {
6329             cfg.cls += ' table-bordered';
6330         }
6331         if (this.condensed) {
6332             cfg.cls += ' table-condensed';
6333         }
6334         if (this.responsive) {
6335             cfg.cls += ' table-responsive';
6336         }
6337         
6338         if (this.cls) {
6339             cfg.cls+=  ' ' +this.cls;
6340         }
6341         
6342         // this lot should be simplifed...
6343         var _t = this;
6344         var cp = [
6345             'align',
6346             'bgcolor',
6347             'border',
6348             'cellpadding',
6349             'cellspacing',
6350             'frame',
6351             'rules',
6352             'sortable',
6353             'summary',
6354             'width'
6355         ].forEach(function(k) {
6356             if (_t[k]) {
6357                 cfg[k] = _t[k];
6358             }
6359         });
6360         
6361         
6362         if (this.layout) {
6363             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6364         }
6365         
6366         if(this.store || this.cm){
6367             if(this.headerShow){
6368                 cfg.cn.push(this.renderHeader());
6369             }
6370             
6371             cfg.cn.push(this.renderBody());
6372             
6373             if(this.footerShow){
6374                 cfg.cn.push(this.renderFooter());
6375             }
6376             // where does this come from?
6377             //cfg.cls+=  ' TableGrid';
6378         }
6379         
6380         return { cn : [ cfg ] };
6381     },
6382     
6383     initEvents : function()
6384     {   
6385         if(!this.store || !this.cm){
6386             return;
6387         }
6388         if (this.selModel) {
6389             this.selModel.initEvents();
6390         }
6391         
6392         
6393         //Roo.log('initEvents with ds!!!!');
6394         
6395         this.mainBody = this.el.select('tbody', true).first();
6396         this.mainHead = this.el.select('thead', true).first();
6397         this.mainFoot = this.el.select('tfoot', true).first();
6398         
6399         
6400         
6401         var _this = this;
6402         
6403         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6404             e.on('click', _this.sort, _this);
6405         });
6406         
6407         this.mainBody.on("click", this.onClick, this);
6408         this.mainBody.on("dblclick", this.onDblClick, this);
6409         
6410         // why is this done????? = it breaks dialogs??
6411         //this.parent().el.setStyle('position', 'relative');
6412         
6413         
6414         if (this.footer) {
6415             this.footer.parentId = this.id;
6416             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6417             
6418             if(this.lazyLoad){
6419                 this.el.select('tfoot tr td').first().addClass('hide');
6420             }
6421         } 
6422         
6423         if(this.loadMask) {
6424             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6425         }
6426         
6427         this.store.on('load', this.onLoad, this);
6428         this.store.on('beforeload', this.onBeforeLoad, this);
6429         this.store.on('update', this.onUpdate, this);
6430         this.store.on('add', this.onAdd, this);
6431         this.store.on("clear", this.clear, this);
6432         
6433         this.el.on("contextmenu", this.onContextMenu, this);
6434         
6435         this.mainBody.on('scroll', this.onBodyScroll, this);
6436         
6437         this.cm.on("headerchange", this.onHeaderChange, this);
6438         
6439         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6440         
6441     },
6442     
6443     onContextMenu : function(e, t)
6444     {
6445         this.processEvent("contextmenu", e);
6446     },
6447     
6448     processEvent : function(name, e)
6449     {
6450         if (name != 'touchstart' ) {
6451             this.fireEvent(name, e);    
6452         }
6453         
6454         var t = e.getTarget();
6455         
6456         var cell = Roo.get(t);
6457         
6458         if(!cell){
6459             return;
6460         }
6461         
6462         if(cell.findParent('tfoot', false, true)){
6463             return;
6464         }
6465         
6466         if(cell.findParent('thead', false, true)){
6467             
6468             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6469                 cell = Roo.get(t).findParent('th', false, true);
6470                 if (!cell) {
6471                     Roo.log("failed to find th in thead?");
6472                     Roo.log(e.getTarget());
6473                     return;
6474                 }
6475             }
6476             
6477             var cellIndex = cell.dom.cellIndex;
6478             
6479             var ename = name == 'touchstart' ? 'click' : name;
6480             this.fireEvent("header" + ename, this, cellIndex, e);
6481             
6482             return;
6483         }
6484         
6485         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6486             cell = Roo.get(t).findParent('td', false, true);
6487             if (!cell) {
6488                 Roo.log("failed to find th in tbody?");
6489                 Roo.log(e.getTarget());
6490                 return;
6491             }
6492         }
6493         
6494         var row = cell.findParent('tr', false, true);
6495         var cellIndex = cell.dom.cellIndex;
6496         var rowIndex = row.dom.rowIndex - 1;
6497         
6498         if(row !== false){
6499             
6500             this.fireEvent("row" + name, this, rowIndex, e);
6501             
6502             if(cell !== false){
6503             
6504                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6505             }
6506         }
6507         
6508     },
6509     
6510     onMouseover : function(e, el)
6511     {
6512         var cell = Roo.get(el);
6513         
6514         if(!cell){
6515             return;
6516         }
6517         
6518         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6519             cell = cell.findParent('td', false, true);
6520         }
6521         
6522         var row = cell.findParent('tr', false, true);
6523         var cellIndex = cell.dom.cellIndex;
6524         var rowIndex = row.dom.rowIndex - 1; // start from 0
6525         
6526         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6527         
6528     },
6529     
6530     onMouseout : function(e, el)
6531     {
6532         var cell = Roo.get(el);
6533         
6534         if(!cell){
6535             return;
6536         }
6537         
6538         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6539             cell = cell.findParent('td', false, true);
6540         }
6541         
6542         var row = cell.findParent('tr', false, true);
6543         var cellIndex = cell.dom.cellIndex;
6544         var rowIndex = row.dom.rowIndex - 1; // start from 0
6545         
6546         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6547         
6548     },
6549     
6550     onClick : function(e, el)
6551     {
6552         var cell = Roo.get(el);
6553         
6554         if(!cell || (!this.cellSelection && !this.rowSelection)){
6555             return;
6556         }
6557         
6558         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6559             cell = cell.findParent('td', false, true);
6560         }
6561         
6562         if(!cell || typeof(cell) == 'undefined'){
6563             return;
6564         }
6565         
6566         var row = cell.findParent('tr', false, true);
6567         
6568         if(!row || typeof(row) == 'undefined'){
6569             return;
6570         }
6571         
6572         var cellIndex = cell.dom.cellIndex;
6573         var rowIndex = this.getRowIndex(row);
6574         
6575         // why??? - should these not be based on SelectionModel?
6576         if(this.cellSelection){
6577             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6578         }
6579         
6580         if(this.rowSelection){
6581             this.fireEvent('rowclick', this, row, rowIndex, e);
6582         }
6583         
6584         
6585     },
6586         
6587     onDblClick : function(e,el)
6588     {
6589         var cell = Roo.get(el);
6590         
6591         if(!cell || (!this.cellSelection && !this.rowSelection)){
6592             return;
6593         }
6594         
6595         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6596             cell = cell.findParent('td', false, true);
6597         }
6598         
6599         if(!cell || typeof(cell) == 'undefined'){
6600             return;
6601         }
6602         
6603         var row = cell.findParent('tr', false, true);
6604         
6605         if(!row || typeof(row) == 'undefined'){
6606             return;
6607         }
6608         
6609         var cellIndex = cell.dom.cellIndex;
6610         var rowIndex = this.getRowIndex(row);
6611         
6612         if(this.cellSelection){
6613             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6614         }
6615         
6616         if(this.rowSelection){
6617             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6618         }
6619     },
6620     
6621     sort : function(e,el)
6622     {
6623         var col = Roo.get(el);
6624         
6625         if(!col.hasClass('sortable')){
6626             return;
6627         }
6628         
6629         var sort = col.attr('sort');
6630         var dir = 'ASC';
6631         
6632         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6633             dir = 'DESC';
6634         }
6635         
6636         this.store.sortInfo = {field : sort, direction : dir};
6637         
6638         if (this.footer) {
6639             Roo.log("calling footer first");
6640             this.footer.onClick('first');
6641         } else {
6642         
6643             this.store.load({ params : { start : 0 } });
6644         }
6645     },
6646     
6647     renderHeader : function()
6648     {
6649         var header = {
6650             tag: 'thead',
6651             cn : []
6652         };
6653         
6654         var cm = this.cm;
6655         this.totalWidth = 0;
6656         
6657         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6658             
6659             var config = cm.config[i];
6660             
6661             var c = {
6662                 tag: 'th',
6663                 cls : 'x-hcol-' + i,
6664                 style : '',
6665                 html: cm.getColumnHeader(i)
6666             };
6667             
6668             var hh = '';
6669             
6670             if(typeof(config.sortable) != 'undefined' && config.sortable){
6671                 c.cls = 'sortable';
6672                 c.html = '<i class="glyphicon"></i>' + c.html;
6673             }
6674             
6675             if(typeof(config.lgHeader) != 'undefined'){
6676                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6677             }
6678             
6679             if(typeof(config.mdHeader) != 'undefined'){
6680                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6681             }
6682             
6683             if(typeof(config.smHeader) != 'undefined'){
6684                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6685             }
6686             
6687             if(typeof(config.xsHeader) != 'undefined'){
6688                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6689             }
6690             
6691             if(hh.length){
6692                 c.html = hh;
6693             }
6694             
6695             if(typeof(config.tooltip) != 'undefined'){
6696                 c.tooltip = config.tooltip;
6697             }
6698             
6699             if(typeof(config.colspan) != 'undefined'){
6700                 c.colspan = config.colspan;
6701             }
6702             
6703             if(typeof(config.hidden) != 'undefined' && config.hidden){
6704                 c.style += ' display:none;';
6705             }
6706             
6707             if(typeof(config.dataIndex) != 'undefined'){
6708                 c.sort = config.dataIndex;
6709             }
6710             
6711            
6712             
6713             if(typeof(config.align) != 'undefined' && config.align.length){
6714                 c.style += ' text-align:' + config.align + ';';
6715             }
6716             
6717             if(typeof(config.width) != 'undefined'){
6718                 c.style += ' width:' + config.width + 'px;';
6719                 this.totalWidth += config.width;
6720             } else {
6721                 this.totalWidth += 100; // assume minimum of 100 per column?
6722             }
6723             
6724             if(typeof(config.cls) != 'undefined'){
6725                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6726             }
6727             
6728             ['xs','sm','md','lg'].map(function(size){
6729                 
6730                 if(typeof(config[size]) == 'undefined'){
6731                     return;
6732                 }
6733                 
6734                 if (!config[size]) { // 0 = hidden
6735                     c.cls += ' hidden-' + size;
6736                     return;
6737                 }
6738                 
6739                 c.cls += ' col-' + size + '-' + config[size];
6740
6741             });
6742             
6743             header.cn.push(c)
6744         }
6745         
6746         return header;
6747     },
6748     
6749     renderBody : function()
6750     {
6751         var body = {
6752             tag: 'tbody',
6753             cn : [
6754                 {
6755                     tag: 'tr',
6756                     cn : [
6757                         {
6758                             tag : 'td',
6759                             colspan :  this.cm.getColumnCount()
6760                         }
6761                     ]
6762                 }
6763             ]
6764         };
6765         
6766         return body;
6767     },
6768     
6769     renderFooter : function()
6770     {
6771         var footer = {
6772             tag: 'tfoot',
6773             cn : [
6774                 {
6775                     tag: 'tr',
6776                     cn : [
6777                         {
6778                             tag : 'td',
6779                             colspan :  this.cm.getColumnCount()
6780                         }
6781                     ]
6782                 }
6783             ]
6784         };
6785         
6786         return footer;
6787     },
6788     
6789     
6790     
6791     onLoad : function()
6792     {
6793 //        Roo.log('ds onload');
6794         this.clear();
6795         
6796         var _this = this;
6797         var cm = this.cm;
6798         var ds = this.store;
6799         
6800         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6801             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6802             if (_this.store.sortInfo) {
6803                     
6804                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6805                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6806                 }
6807                 
6808                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6809                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6810                 }
6811             }
6812         });
6813         
6814         var tbody =  this.mainBody;
6815               
6816         if(ds.getCount() > 0){
6817             ds.data.each(function(d,rowIndex){
6818                 var row =  this.renderRow(cm, ds, rowIndex);
6819                 
6820                 tbody.createChild(row);
6821                 
6822                 var _this = this;
6823                 
6824                 if(row.cellObjects.length){
6825                     Roo.each(row.cellObjects, function(r){
6826                         _this.renderCellObject(r);
6827                     })
6828                 }
6829                 
6830             }, this);
6831         }
6832         
6833         var tfoot = this.el.select('tfoot', true).first();
6834         
6835         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6836             
6837             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6838             
6839             var total = this.ds.getTotalCount();
6840             
6841             if(this.footer.pageSize < total){
6842                 this.mainFoot.show();
6843             }
6844         }
6845         
6846         Roo.each(this.el.select('tbody td', true).elements, function(e){
6847             e.on('mouseover', _this.onMouseover, _this);
6848         });
6849         
6850         Roo.each(this.el.select('tbody td', true).elements, function(e){
6851             e.on('mouseout', _this.onMouseout, _this);
6852         });
6853         this.fireEvent('rowsrendered', this);
6854         
6855         this.autoSize();
6856     },
6857     
6858     
6859     onUpdate : function(ds,record)
6860     {
6861         this.refreshRow(record);
6862         this.autoSize();
6863     },
6864     
6865     onRemove : function(ds, record, index, isUpdate){
6866         if(isUpdate !== true){
6867             this.fireEvent("beforerowremoved", this, index, record);
6868         }
6869         var bt = this.mainBody.dom;
6870         
6871         var rows = this.el.select('tbody > tr', true).elements;
6872         
6873         if(typeof(rows[index]) != 'undefined'){
6874             bt.removeChild(rows[index].dom);
6875         }
6876         
6877 //        if(bt.rows[index]){
6878 //            bt.removeChild(bt.rows[index]);
6879 //        }
6880         
6881         if(isUpdate !== true){
6882             //this.stripeRows(index);
6883             //this.syncRowHeights(index, index);
6884             //this.layout();
6885             this.fireEvent("rowremoved", this, index, record);
6886         }
6887     },
6888     
6889     onAdd : function(ds, records, rowIndex)
6890     {
6891         //Roo.log('on Add called');
6892         // - note this does not handle multiple adding very well..
6893         var bt = this.mainBody.dom;
6894         for (var i =0 ; i < records.length;i++) {
6895             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6896             //Roo.log(records[i]);
6897             //Roo.log(this.store.getAt(rowIndex+i));
6898             this.insertRow(this.store, rowIndex + i, false);
6899             return;
6900         }
6901         
6902     },
6903     
6904     
6905     refreshRow : function(record){
6906         var ds = this.store, index;
6907         if(typeof record == 'number'){
6908             index = record;
6909             record = ds.getAt(index);
6910         }else{
6911             index = ds.indexOf(record);
6912         }
6913         this.insertRow(ds, index, true);
6914         this.autoSize();
6915         this.onRemove(ds, record, index+1, true);
6916         this.autoSize();
6917         //this.syncRowHeights(index, index);
6918         //this.layout();
6919         this.fireEvent("rowupdated", this, index, record);
6920     },
6921     
6922     insertRow : function(dm, rowIndex, isUpdate){
6923         
6924         if(!isUpdate){
6925             this.fireEvent("beforerowsinserted", this, rowIndex);
6926         }
6927             //var s = this.getScrollState();
6928         var row = this.renderRow(this.cm, this.store, rowIndex);
6929         // insert before rowIndex..
6930         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6931         
6932         var _this = this;
6933                 
6934         if(row.cellObjects.length){
6935             Roo.each(row.cellObjects, function(r){
6936                 _this.renderCellObject(r);
6937             })
6938         }
6939             
6940         if(!isUpdate){
6941             this.fireEvent("rowsinserted", this, rowIndex);
6942             //this.syncRowHeights(firstRow, lastRow);
6943             //this.stripeRows(firstRow);
6944             //this.layout();
6945         }
6946         
6947     },
6948     
6949     
6950     getRowDom : function(rowIndex)
6951     {
6952         var rows = this.el.select('tbody > tr', true).elements;
6953         
6954         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6955         
6956     },
6957     // returns the object tree for a tr..
6958   
6959     
6960     renderRow : function(cm, ds, rowIndex) 
6961     {
6962         var d = ds.getAt(rowIndex);
6963         
6964         var row = {
6965             tag : 'tr',
6966             cls : 'x-row-' + rowIndex,
6967             cn : []
6968         };
6969             
6970         var cellObjects = [];
6971         
6972         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6973             var config = cm.config[i];
6974             
6975             var renderer = cm.getRenderer(i);
6976             var value = '';
6977             var id = false;
6978             
6979             if(typeof(renderer) !== 'undefined'){
6980                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6981             }
6982             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6983             // and are rendered into the cells after the row is rendered - using the id for the element.
6984             
6985             if(typeof(value) === 'object'){
6986                 id = Roo.id();
6987                 cellObjects.push({
6988                     container : id,
6989                     cfg : value 
6990                 })
6991             }
6992             
6993             var rowcfg = {
6994                 record: d,
6995                 rowIndex : rowIndex,
6996                 colIndex : i,
6997                 rowClass : ''
6998             };
6999
7000             this.fireEvent('rowclass', this, rowcfg);
7001             
7002             var td = {
7003                 tag: 'td',
7004                 cls : rowcfg.rowClass + ' x-col-' + i,
7005                 style: '',
7006                 html: (typeof(value) === 'object') ? '' : value
7007             };
7008             
7009             if (id) {
7010                 td.id = id;
7011             }
7012             
7013             if(typeof(config.colspan) != 'undefined'){
7014                 td.colspan = config.colspan;
7015             }
7016             
7017             if(typeof(config.hidden) != 'undefined' && config.hidden){
7018                 td.style += ' display:none;';
7019             }
7020             
7021             if(typeof(config.align) != 'undefined' && config.align.length){
7022                 td.style += ' text-align:' + config.align + ';';
7023             }
7024             if(typeof(config.valign) != 'undefined' && config.valign.length){
7025                 td.style += ' vertical-align:' + config.valign + ';';
7026             }
7027             
7028             if(typeof(config.width) != 'undefined'){
7029                 td.style += ' width:' +  config.width + 'px;';
7030             }
7031             
7032             if(typeof(config.cursor) != 'undefined'){
7033                 td.style += ' cursor:' +  config.cursor + ';';
7034             }
7035             
7036             if(typeof(config.cls) != 'undefined'){
7037                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7038             }
7039             
7040             ['xs','sm','md','lg'].map(function(size){
7041                 
7042                 if(typeof(config[size]) == 'undefined'){
7043                     return;
7044                 }
7045                 
7046                 if (!config[size]) { // 0 = hidden
7047                     td.cls += ' hidden-' + size;
7048                     return;
7049                 }
7050                 
7051                 td.cls += ' col-' + size + '-' + config[size];
7052
7053             });
7054             
7055             row.cn.push(td);
7056            
7057         }
7058         
7059         row.cellObjects = cellObjects;
7060         
7061         return row;
7062           
7063     },
7064     
7065     
7066     
7067     onBeforeLoad : function()
7068     {
7069         
7070     },
7071      /**
7072      * Remove all rows
7073      */
7074     clear : function()
7075     {
7076         this.el.select('tbody', true).first().dom.innerHTML = '';
7077     },
7078     /**
7079      * Show or hide a row.
7080      * @param {Number} rowIndex to show or hide
7081      * @param {Boolean} state hide
7082      */
7083     setRowVisibility : function(rowIndex, state)
7084     {
7085         var bt = this.mainBody.dom;
7086         
7087         var rows = this.el.select('tbody > tr', true).elements;
7088         
7089         if(typeof(rows[rowIndex]) == 'undefined'){
7090             return;
7091         }
7092         rows[rowIndex].dom.style.display = state ? '' : 'none';
7093     },
7094     
7095     
7096     getSelectionModel : function(){
7097         if(!this.selModel){
7098             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7099         }
7100         return this.selModel;
7101     },
7102     /*
7103      * Render the Roo.bootstrap object from renderder
7104      */
7105     renderCellObject : function(r)
7106     {
7107         var _this = this;
7108         
7109         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7110         
7111         var t = r.cfg.render(r.container);
7112         
7113         if(r.cfg.cn){
7114             Roo.each(r.cfg.cn, function(c){
7115                 var child = {
7116                     container: t.getChildContainer(),
7117                     cfg: c
7118                 };
7119                 _this.renderCellObject(child);
7120             })
7121         }
7122     },
7123     
7124     getRowIndex : function(row)
7125     {
7126         var rowIndex = -1;
7127         
7128         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7129             if(el != row){
7130                 return;
7131             }
7132             
7133             rowIndex = index;
7134         });
7135         
7136         return rowIndex;
7137     },
7138      /**
7139      * Returns the grid's underlying element = used by panel.Grid
7140      * @return {Element} The element
7141      */
7142     getGridEl : function(){
7143         return this.el;
7144     },
7145      /**
7146      * Forces a resize - used by panel.Grid
7147      * @return {Element} The element
7148      */
7149     autoSize : function()
7150     {
7151         //var ctr = Roo.get(this.container.dom.parentElement);
7152         var ctr = Roo.get(this.el.dom);
7153         
7154         var thd = this.getGridEl().select('thead',true).first();
7155         var tbd = this.getGridEl().select('tbody', true).first();
7156         var tfd = this.getGridEl().select('tfoot', true).first();
7157         
7158         var cw = ctr.getWidth();
7159         
7160         if (tbd) {
7161             
7162             tbd.setSize(ctr.getWidth(),
7163                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7164             );
7165             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7166             cw -= barsize;
7167         }
7168         cw = Math.max(cw, this.totalWidth);
7169         this.getGridEl().select('tr',true).setWidth(cw);
7170         // resize 'expandable coloumn?
7171         
7172         return; // we doe not have a view in this design..
7173         
7174     },
7175     onBodyScroll: function()
7176     {
7177         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7178         if(this.mainHead){
7179             this.mainHead.setStyle({
7180                 'position' : 'relative',
7181                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7182             });
7183         }
7184         
7185         if(this.lazyLoad){
7186             
7187             var scrollHeight = this.mainBody.dom.scrollHeight;
7188             
7189             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7190             
7191             var height = this.mainBody.getHeight();
7192             
7193             if(scrollHeight - height == scrollTop) {
7194                 
7195                 var total = this.ds.getTotalCount();
7196                 
7197                 if(this.footer.cursor + this.footer.pageSize < total){
7198                     
7199                     this.footer.ds.load({
7200                         params : {
7201                             start : this.footer.cursor + this.footer.pageSize,
7202                             limit : this.footer.pageSize
7203                         },
7204                         add : true
7205                     });
7206                 }
7207             }
7208             
7209         }
7210     },
7211     
7212     onHeaderChange : function()
7213     {
7214         var header = this.renderHeader();
7215         var table = this.el.select('table', true).first();
7216         
7217         this.mainHead.remove();
7218         this.mainHead = table.createChild(header, this.mainBody, false);
7219     },
7220     
7221     onHiddenChange : function(colModel, colIndex, hidden)
7222     {
7223         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7224         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7225         
7226         this.CSS.updateRule(thSelector, "display", "");
7227         this.CSS.updateRule(tdSelector, "display", "");
7228         
7229         if(hidden){
7230             this.CSS.updateRule(thSelector, "display", "none");
7231             this.CSS.updateRule(tdSelector, "display", "none");
7232         }
7233         
7234         this.onHeaderChange();
7235         this.onLoad();
7236     },
7237     
7238     setColumnWidth: function(col_index, width)
7239     {
7240         // width = "md-2 xs-2..."
7241         if(!this.colModel.config[col_index]) {
7242             return;
7243         }
7244         
7245         var w = width.split(" ");
7246         
7247         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7248         
7249         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7250         
7251         
7252         for(var j = 0; j < w.length; j++) {
7253             
7254             if(!w[j]) {
7255                 continue;
7256             }
7257             
7258             var size_cls = w[j].split("-");
7259             
7260             if(!Number.isInteger(size_cls[1] * 1)) {
7261                 continue;
7262             }
7263             
7264             if(!this.colModel.config[col_index][size_cls[0]]) {
7265                 continue;
7266             }
7267             
7268             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7269                 continue;
7270             }
7271             
7272             h_row[0].classList.replace(
7273                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7274                 "col-"+size_cls[0]+"-"+size_cls[1]
7275             );
7276             
7277             for(var i = 0; i < rows.length; i++) {
7278                 
7279                 var size_cls = w[j].split("-");
7280                 
7281                 if(!Number.isInteger(size_cls[1] * 1)) {
7282                     continue;
7283                 }
7284                 
7285                 if(!this.colModel.config[col_index][size_cls[0]]) {
7286                     continue;
7287                 }
7288                 
7289                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7290                     continue;
7291                 }
7292                 
7293                 rows[i].classList.replace(
7294                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7295                     "col-"+size_cls[0]+"-"+size_cls[1]
7296                 );
7297             }
7298             
7299             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7300         }
7301     }
7302 });
7303
7304  
7305
7306  /*
7307  * - LGPL
7308  *
7309  * table cell
7310  * 
7311  */
7312
7313 /**
7314  * @class Roo.bootstrap.TableCell
7315  * @extends Roo.bootstrap.Component
7316  * Bootstrap TableCell class
7317  * @cfg {String} html cell contain text
7318  * @cfg {String} cls cell class
7319  * @cfg {String} tag cell tag (td|th) default td
7320  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7321  * @cfg {String} align Aligns the content in a cell
7322  * @cfg {String} axis Categorizes cells
7323  * @cfg {String} bgcolor Specifies the background color of a cell
7324  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7325  * @cfg {Number} colspan Specifies the number of columns a cell should span
7326  * @cfg {String} headers Specifies one or more header cells a cell is related to
7327  * @cfg {Number} height Sets the height of a cell
7328  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7329  * @cfg {Number} rowspan Sets the number of rows a cell should span
7330  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7331  * @cfg {String} valign Vertical aligns the content in a cell
7332  * @cfg {Number} width Specifies the width of a cell
7333  * 
7334  * @constructor
7335  * Create a new TableCell
7336  * @param {Object} config The config object
7337  */
7338
7339 Roo.bootstrap.TableCell = function(config){
7340     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7341 };
7342
7343 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7344     
7345     html: false,
7346     cls: false,
7347     tag: false,
7348     abbr: false,
7349     align: false,
7350     axis: false,
7351     bgcolor: false,
7352     charoff: false,
7353     colspan: false,
7354     headers: false,
7355     height: false,
7356     nowrap: false,
7357     rowspan: false,
7358     scope: false,
7359     valign: false,
7360     width: false,
7361     
7362     
7363     getAutoCreate : function(){
7364         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7365         
7366         cfg = {
7367             tag: 'td'
7368         };
7369         
7370         if(this.tag){
7371             cfg.tag = this.tag;
7372         }
7373         
7374         if (this.html) {
7375             cfg.html=this.html
7376         }
7377         if (this.cls) {
7378             cfg.cls=this.cls
7379         }
7380         if (this.abbr) {
7381             cfg.abbr=this.abbr
7382         }
7383         if (this.align) {
7384             cfg.align=this.align
7385         }
7386         if (this.axis) {
7387             cfg.axis=this.axis
7388         }
7389         if (this.bgcolor) {
7390             cfg.bgcolor=this.bgcolor
7391         }
7392         if (this.charoff) {
7393             cfg.charoff=this.charoff
7394         }
7395         if (this.colspan) {
7396             cfg.colspan=this.colspan
7397         }
7398         if (this.headers) {
7399             cfg.headers=this.headers
7400         }
7401         if (this.height) {
7402             cfg.height=this.height
7403         }
7404         if (this.nowrap) {
7405             cfg.nowrap=this.nowrap
7406         }
7407         if (this.rowspan) {
7408             cfg.rowspan=this.rowspan
7409         }
7410         if (this.scope) {
7411             cfg.scope=this.scope
7412         }
7413         if (this.valign) {
7414             cfg.valign=this.valign
7415         }
7416         if (this.width) {
7417             cfg.width=this.width
7418         }
7419         
7420         
7421         return cfg;
7422     }
7423    
7424 });
7425
7426  
7427
7428  /*
7429  * - LGPL
7430  *
7431  * table row
7432  * 
7433  */
7434
7435 /**
7436  * @class Roo.bootstrap.TableRow
7437  * @extends Roo.bootstrap.Component
7438  * Bootstrap TableRow class
7439  * @cfg {String} cls row class
7440  * @cfg {String} align Aligns the content in a table row
7441  * @cfg {String} bgcolor Specifies a background color for a table row
7442  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7443  * @cfg {String} valign Vertical aligns the content in a table row
7444  * 
7445  * @constructor
7446  * Create a new TableRow
7447  * @param {Object} config The config object
7448  */
7449
7450 Roo.bootstrap.TableRow = function(config){
7451     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7452 };
7453
7454 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7455     
7456     cls: false,
7457     align: false,
7458     bgcolor: false,
7459     charoff: false,
7460     valign: false,
7461     
7462     getAutoCreate : function(){
7463         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7464         
7465         cfg = {
7466             tag: 'tr'
7467         };
7468             
7469         if(this.cls){
7470             cfg.cls = this.cls;
7471         }
7472         if(this.align){
7473             cfg.align = this.align;
7474         }
7475         if(this.bgcolor){
7476             cfg.bgcolor = this.bgcolor;
7477         }
7478         if(this.charoff){
7479             cfg.charoff = this.charoff;
7480         }
7481         if(this.valign){
7482             cfg.valign = this.valign;
7483         }
7484         
7485         return cfg;
7486     }
7487    
7488 });
7489
7490  
7491
7492  /*
7493  * - LGPL
7494  *
7495  * table body
7496  * 
7497  */
7498
7499 /**
7500  * @class Roo.bootstrap.TableBody
7501  * @extends Roo.bootstrap.Component
7502  * Bootstrap TableBody class
7503  * @cfg {String} cls element class
7504  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7505  * @cfg {String} align Aligns the content inside the element
7506  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7507  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7508  * 
7509  * @constructor
7510  * Create a new TableBody
7511  * @param {Object} config The config object
7512  */
7513
7514 Roo.bootstrap.TableBody = function(config){
7515     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7516 };
7517
7518 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7519     
7520     cls: false,
7521     tag: false,
7522     align: false,
7523     charoff: false,
7524     valign: false,
7525     
7526     getAutoCreate : function(){
7527         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7528         
7529         cfg = {
7530             tag: 'tbody'
7531         };
7532             
7533         if (this.cls) {
7534             cfg.cls=this.cls
7535         }
7536         if(this.tag){
7537             cfg.tag = this.tag;
7538         }
7539         
7540         if(this.align){
7541             cfg.align = this.align;
7542         }
7543         if(this.charoff){
7544             cfg.charoff = this.charoff;
7545         }
7546         if(this.valign){
7547             cfg.valign = this.valign;
7548         }
7549         
7550         return cfg;
7551     }
7552     
7553     
7554 //    initEvents : function()
7555 //    {
7556 //        
7557 //        if(!this.store){
7558 //            return;
7559 //        }
7560 //        
7561 //        this.store = Roo.factory(this.store, Roo.data);
7562 //        this.store.on('load', this.onLoad, this);
7563 //        
7564 //        this.store.load();
7565 //        
7566 //    },
7567 //    
7568 //    onLoad: function () 
7569 //    {   
7570 //        this.fireEvent('load', this);
7571 //    }
7572 //    
7573 //   
7574 });
7575
7576  
7577
7578  /*
7579  * Based on:
7580  * Ext JS Library 1.1.1
7581  * Copyright(c) 2006-2007, Ext JS, LLC.
7582  *
7583  * Originally Released Under LGPL - original licence link has changed is not relivant.
7584  *
7585  * Fork - LGPL
7586  * <script type="text/javascript">
7587  */
7588
7589 // as we use this in bootstrap.
7590 Roo.namespace('Roo.form');
7591  /**
7592  * @class Roo.form.Action
7593  * Internal Class used to handle form actions
7594  * @constructor
7595  * @param {Roo.form.BasicForm} el The form element or its id
7596  * @param {Object} config Configuration options
7597  */
7598
7599  
7600  
7601 // define the action interface
7602 Roo.form.Action = function(form, options){
7603     this.form = form;
7604     this.options = options || {};
7605 };
7606 /**
7607  * Client Validation Failed
7608  * @const 
7609  */
7610 Roo.form.Action.CLIENT_INVALID = 'client';
7611 /**
7612  * Server Validation Failed
7613  * @const 
7614  */
7615 Roo.form.Action.SERVER_INVALID = 'server';
7616  /**
7617  * Connect to Server Failed
7618  * @const 
7619  */
7620 Roo.form.Action.CONNECT_FAILURE = 'connect';
7621 /**
7622  * Reading Data from Server Failed
7623  * @const 
7624  */
7625 Roo.form.Action.LOAD_FAILURE = 'load';
7626
7627 Roo.form.Action.prototype = {
7628     type : 'default',
7629     failureType : undefined,
7630     response : undefined,
7631     result : undefined,
7632
7633     // interface method
7634     run : function(options){
7635
7636     },
7637
7638     // interface method
7639     success : function(response){
7640
7641     },
7642
7643     // interface method
7644     handleResponse : function(response){
7645
7646     },
7647
7648     // default connection failure
7649     failure : function(response){
7650         
7651         this.response = response;
7652         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7653         this.form.afterAction(this, false);
7654     },
7655
7656     processResponse : function(response){
7657         this.response = response;
7658         if(!response.responseText){
7659             return true;
7660         }
7661         this.result = this.handleResponse(response);
7662         return this.result;
7663     },
7664
7665     // utility functions used internally
7666     getUrl : function(appendParams){
7667         var url = this.options.url || this.form.url || this.form.el.dom.action;
7668         if(appendParams){
7669             var p = this.getParams();
7670             if(p){
7671                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7672             }
7673         }
7674         return url;
7675     },
7676
7677     getMethod : function(){
7678         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7679     },
7680
7681     getParams : function(){
7682         var bp = this.form.baseParams;
7683         var p = this.options.params;
7684         if(p){
7685             if(typeof p == "object"){
7686                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7687             }else if(typeof p == 'string' && bp){
7688                 p += '&' + Roo.urlEncode(bp);
7689             }
7690         }else if(bp){
7691             p = Roo.urlEncode(bp);
7692         }
7693         return p;
7694     },
7695
7696     createCallback : function(){
7697         return {
7698             success: this.success,
7699             failure: this.failure,
7700             scope: this,
7701             timeout: (this.form.timeout*1000),
7702             upload: this.form.fileUpload ? this.success : undefined
7703         };
7704     }
7705 };
7706
7707 Roo.form.Action.Submit = function(form, options){
7708     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7709 };
7710
7711 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7712     type : 'submit',
7713
7714     haveProgress : false,
7715     uploadComplete : false,
7716     
7717     // uploadProgress indicator.
7718     uploadProgress : function()
7719     {
7720         if (!this.form.progressUrl) {
7721             return;
7722         }
7723         
7724         if (!this.haveProgress) {
7725             Roo.MessageBox.progress("Uploading", "Uploading");
7726         }
7727         if (this.uploadComplete) {
7728            Roo.MessageBox.hide();
7729            return;
7730         }
7731         
7732         this.haveProgress = true;
7733    
7734         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7735         
7736         var c = new Roo.data.Connection();
7737         c.request({
7738             url : this.form.progressUrl,
7739             params: {
7740                 id : uid
7741             },
7742             method: 'GET',
7743             success : function(req){
7744                //console.log(data);
7745                 var rdata = false;
7746                 var edata;
7747                 try  {
7748                    rdata = Roo.decode(req.responseText)
7749                 } catch (e) {
7750                     Roo.log("Invalid data from server..");
7751                     Roo.log(edata);
7752                     return;
7753                 }
7754                 if (!rdata || !rdata.success) {
7755                     Roo.log(rdata);
7756                     Roo.MessageBox.alert(Roo.encode(rdata));
7757                     return;
7758                 }
7759                 var data = rdata.data;
7760                 
7761                 if (this.uploadComplete) {
7762                    Roo.MessageBox.hide();
7763                    return;
7764                 }
7765                    
7766                 if (data){
7767                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7768                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7769                     );
7770                 }
7771                 this.uploadProgress.defer(2000,this);
7772             },
7773        
7774             failure: function(data) {
7775                 Roo.log('progress url failed ');
7776                 Roo.log(data);
7777             },
7778             scope : this
7779         });
7780            
7781     },
7782     
7783     
7784     run : function()
7785     {
7786         // run get Values on the form, so it syncs any secondary forms.
7787         this.form.getValues();
7788         
7789         var o = this.options;
7790         var method = this.getMethod();
7791         var isPost = method == 'POST';
7792         if(o.clientValidation === false || this.form.isValid()){
7793             
7794             if (this.form.progressUrl) {
7795                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7796                     (new Date() * 1) + '' + Math.random());
7797                     
7798             } 
7799             
7800             
7801             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7802                 form:this.form.el.dom,
7803                 url:this.getUrl(!isPost),
7804                 method: method,
7805                 params:isPost ? this.getParams() : null,
7806                 isUpload: this.form.fileUpload
7807             }));
7808             
7809             this.uploadProgress();
7810
7811         }else if (o.clientValidation !== false){ // client validation failed
7812             this.failureType = Roo.form.Action.CLIENT_INVALID;
7813             this.form.afterAction(this, false);
7814         }
7815     },
7816
7817     success : function(response)
7818     {
7819         this.uploadComplete= true;
7820         if (this.haveProgress) {
7821             Roo.MessageBox.hide();
7822         }
7823         
7824         
7825         var result = this.processResponse(response);
7826         if(result === true || result.success){
7827             this.form.afterAction(this, true);
7828             return;
7829         }
7830         if(result.errors){
7831             this.form.markInvalid(result.errors);
7832             this.failureType = Roo.form.Action.SERVER_INVALID;
7833         }
7834         this.form.afterAction(this, false);
7835     },
7836     failure : function(response)
7837     {
7838         this.uploadComplete= true;
7839         if (this.haveProgress) {
7840             Roo.MessageBox.hide();
7841         }
7842         
7843         this.response = response;
7844         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7845         this.form.afterAction(this, false);
7846     },
7847     
7848     handleResponse : function(response){
7849         if(this.form.errorReader){
7850             var rs = this.form.errorReader.read(response);
7851             var errors = [];
7852             if(rs.records){
7853                 for(var i = 0, len = rs.records.length; i < len; i++) {
7854                     var r = rs.records[i];
7855                     errors[i] = r.data;
7856                 }
7857             }
7858             if(errors.length < 1){
7859                 errors = null;
7860             }
7861             return {
7862                 success : rs.success,
7863                 errors : errors
7864             };
7865         }
7866         var ret = false;
7867         try {
7868             ret = Roo.decode(response.responseText);
7869         } catch (e) {
7870             ret = {
7871                 success: false,
7872                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7873                 errors : []
7874             };
7875         }
7876         return ret;
7877         
7878     }
7879 });
7880
7881
7882 Roo.form.Action.Load = function(form, options){
7883     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7884     this.reader = this.form.reader;
7885 };
7886
7887 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7888     type : 'load',
7889
7890     run : function(){
7891         
7892         Roo.Ajax.request(Roo.apply(
7893                 this.createCallback(), {
7894                     method:this.getMethod(),
7895                     url:this.getUrl(false),
7896                     params:this.getParams()
7897         }));
7898     },
7899
7900     success : function(response){
7901         
7902         var result = this.processResponse(response);
7903         if(result === true || !result.success || !result.data){
7904             this.failureType = Roo.form.Action.LOAD_FAILURE;
7905             this.form.afterAction(this, false);
7906             return;
7907         }
7908         this.form.clearInvalid();
7909         this.form.setValues(result.data);
7910         this.form.afterAction(this, true);
7911     },
7912
7913     handleResponse : function(response){
7914         if(this.form.reader){
7915             var rs = this.form.reader.read(response);
7916             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7917             return {
7918                 success : rs.success,
7919                 data : data
7920             };
7921         }
7922         return Roo.decode(response.responseText);
7923     }
7924 });
7925
7926 Roo.form.Action.ACTION_TYPES = {
7927     'load' : Roo.form.Action.Load,
7928     'submit' : Roo.form.Action.Submit
7929 };/*
7930  * - LGPL
7931  *
7932  * form
7933  *
7934  */
7935
7936 /**
7937  * @class Roo.bootstrap.Form
7938  * @extends Roo.bootstrap.Component
7939  * Bootstrap Form class
7940  * @cfg {String} method  GET | POST (default POST)
7941  * @cfg {String} labelAlign top | left (default top)
7942  * @cfg {String} align left  | right - for navbars
7943  * @cfg {Boolean} loadMask load mask when submit (default true)
7944
7945  *
7946  * @constructor
7947  * Create a new Form
7948  * @param {Object} config The config object
7949  */
7950
7951
7952 Roo.bootstrap.Form = function(config){
7953     
7954     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7955     
7956     Roo.bootstrap.Form.popover.apply();
7957     
7958     this.addEvents({
7959         /**
7960          * @event clientvalidation
7961          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7962          * @param {Form} this
7963          * @param {Boolean} valid true if the form has passed client-side validation
7964          */
7965         clientvalidation: true,
7966         /**
7967          * @event beforeaction
7968          * Fires before any action is performed. Return false to cancel the action.
7969          * @param {Form} this
7970          * @param {Action} action The action to be performed
7971          */
7972         beforeaction: true,
7973         /**
7974          * @event actionfailed
7975          * Fires when an action fails.
7976          * @param {Form} this
7977          * @param {Action} action The action that failed
7978          */
7979         actionfailed : true,
7980         /**
7981          * @event actioncomplete
7982          * Fires when an action is completed.
7983          * @param {Form} this
7984          * @param {Action} action The action that completed
7985          */
7986         actioncomplete : true
7987     });
7988 };
7989
7990 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7991
7992      /**
7993      * @cfg {String} method
7994      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7995      */
7996     method : 'POST',
7997     /**
7998      * @cfg {String} url
7999      * The URL to use for form actions if one isn't supplied in the action options.
8000      */
8001     /**
8002      * @cfg {Boolean} fileUpload
8003      * Set to true if this form is a file upload.
8004      */
8005
8006     /**
8007      * @cfg {Object} baseParams
8008      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8009      */
8010
8011     /**
8012      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8013      */
8014     timeout: 30,
8015     /**
8016      * @cfg {Sting} align (left|right) for navbar forms
8017      */
8018     align : 'left',
8019
8020     // private
8021     activeAction : null,
8022
8023     /**
8024      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8025      * element by passing it or its id or mask the form itself by passing in true.
8026      * @type Mixed
8027      */
8028     waitMsgTarget : false,
8029
8030     loadMask : true,
8031     
8032     /**
8033      * @cfg {Boolean} errorMask (true|false) default false
8034      */
8035     errorMask : false,
8036     
8037     /**
8038      * @cfg {Number} maskOffset Default 100
8039      */
8040     maskOffset : 100,
8041     
8042     /**
8043      * @cfg {Boolean} maskBody
8044      */
8045     maskBody : false,
8046
8047     getAutoCreate : function(){
8048
8049         var cfg = {
8050             tag: 'form',
8051             method : this.method || 'POST',
8052             id : this.id || Roo.id(),
8053             cls : ''
8054         };
8055         if (this.parent().xtype.match(/^Nav/)) {
8056             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8057
8058         }
8059
8060         if (this.labelAlign == 'left' ) {
8061             cfg.cls += ' form-horizontal';
8062         }
8063
8064
8065         return cfg;
8066     },
8067     initEvents : function()
8068     {
8069         this.el.on('submit', this.onSubmit, this);
8070         // this was added as random key presses on the form where triggering form submit.
8071         this.el.on('keypress', function(e) {
8072             if (e.getCharCode() != 13) {
8073                 return true;
8074             }
8075             // we might need to allow it for textareas.. and some other items.
8076             // check e.getTarget().
8077
8078             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8079                 return true;
8080             }
8081
8082             Roo.log("keypress blocked");
8083
8084             e.preventDefault();
8085             return false;
8086         });
8087         
8088     },
8089     // private
8090     onSubmit : function(e){
8091         e.stopEvent();
8092     },
8093
8094      /**
8095      * Returns true if client-side validation on the form is successful.
8096      * @return Boolean
8097      */
8098     isValid : function(){
8099         var items = this.getItems();
8100         var valid = true;
8101         var target = false;
8102         
8103         items.each(function(f){
8104             
8105             if(f.validate()){
8106                 return;
8107             }
8108             
8109             Roo.log('invalid field: ' + f.name);
8110             
8111             valid = false;
8112
8113             if(!target && f.el.isVisible(true)){
8114                 target = f;
8115             }
8116            
8117         });
8118         
8119         if(this.errorMask && !valid){
8120             Roo.bootstrap.Form.popover.mask(this, target);
8121         }
8122         
8123         return valid;
8124     },
8125     
8126     /**
8127      * Returns true if any fields in this form have changed since their original load.
8128      * @return Boolean
8129      */
8130     isDirty : function(){
8131         var dirty = false;
8132         var items = this.getItems();
8133         items.each(function(f){
8134            if(f.isDirty()){
8135                dirty = true;
8136                return false;
8137            }
8138            return true;
8139         });
8140         return dirty;
8141     },
8142      /**
8143      * Performs a predefined action (submit or load) or custom actions you define on this form.
8144      * @param {String} actionName The name of the action type
8145      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8146      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8147      * accept other config options):
8148      * <pre>
8149 Property          Type             Description
8150 ----------------  ---------------  ----------------------------------------------------------------------------------
8151 url               String           The url for the action (defaults to the form's url)
8152 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8153 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8154 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8155                                    validate the form on the client (defaults to false)
8156      * </pre>
8157      * @return {BasicForm} this
8158      */
8159     doAction : function(action, options){
8160         if(typeof action == 'string'){
8161             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8162         }
8163         if(this.fireEvent('beforeaction', this, action) !== false){
8164             this.beforeAction(action);
8165             action.run.defer(100, action);
8166         }
8167         return this;
8168     },
8169
8170     // private
8171     beforeAction : function(action){
8172         var o = action.options;
8173         
8174         if(this.loadMask){
8175             
8176             if(this.maskBody){
8177                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8178             } else {
8179                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8180             }
8181         }
8182         // not really supported yet.. ??
8183
8184         //if(this.waitMsgTarget === true){
8185         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8186         //}else if(this.waitMsgTarget){
8187         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8188         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8189         //}else {
8190         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8191        // }
8192
8193     },
8194
8195     // private
8196     afterAction : function(action, success){
8197         this.activeAction = null;
8198         var o = action.options;
8199
8200         if(this.loadMask){
8201             
8202             if(this.maskBody){
8203                 Roo.get(document.body).unmask();
8204             } else {
8205                 this.el.unmask();
8206             }
8207         }
8208         
8209         //if(this.waitMsgTarget === true){
8210 //            this.el.unmask();
8211         //}else if(this.waitMsgTarget){
8212         //    this.waitMsgTarget.unmask();
8213         //}else{
8214         //    Roo.MessageBox.updateProgress(1);
8215         //    Roo.MessageBox.hide();
8216        // }
8217         //
8218         if(success){
8219             if(o.reset){
8220                 this.reset();
8221             }
8222             Roo.callback(o.success, o.scope, [this, action]);
8223             this.fireEvent('actioncomplete', this, action);
8224
8225         }else{
8226
8227             // failure condition..
8228             // we have a scenario where updates need confirming.
8229             // eg. if a locking scenario exists..
8230             // we look for { errors : { needs_confirm : true }} in the response.
8231             if (
8232                 (typeof(action.result) != 'undefined')  &&
8233                 (typeof(action.result.errors) != 'undefined')  &&
8234                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8235            ){
8236                 var _t = this;
8237                 Roo.log("not supported yet");
8238                  /*
8239
8240                 Roo.MessageBox.confirm(
8241                     "Change requires confirmation",
8242                     action.result.errorMsg,
8243                     function(r) {
8244                         if (r != 'yes') {
8245                             return;
8246                         }
8247                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8248                     }
8249
8250                 );
8251                 */
8252
8253
8254                 return;
8255             }
8256
8257             Roo.callback(o.failure, o.scope, [this, action]);
8258             // show an error message if no failed handler is set..
8259             if (!this.hasListener('actionfailed')) {
8260                 Roo.log("need to add dialog support");
8261                 /*
8262                 Roo.MessageBox.alert("Error",
8263                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8264                         action.result.errorMsg :
8265                         "Saving Failed, please check your entries or try again"
8266                 );
8267                 */
8268             }
8269
8270             this.fireEvent('actionfailed', this, action);
8271         }
8272
8273     },
8274     /**
8275      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8276      * @param {String} id The value to search for
8277      * @return Field
8278      */
8279     findField : function(id){
8280         var items = this.getItems();
8281         var field = items.get(id);
8282         if(!field){
8283              items.each(function(f){
8284                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8285                     field = f;
8286                     return false;
8287                 }
8288                 return true;
8289             });
8290         }
8291         return field || null;
8292     },
8293      /**
8294      * Mark fields in this form invalid in bulk.
8295      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8296      * @return {BasicForm} this
8297      */
8298     markInvalid : function(errors){
8299         if(errors instanceof Array){
8300             for(var i = 0, len = errors.length; i < len; i++){
8301                 var fieldError = errors[i];
8302                 var f = this.findField(fieldError.id);
8303                 if(f){
8304                     f.markInvalid(fieldError.msg);
8305                 }
8306             }
8307         }else{
8308             var field, id;
8309             for(id in errors){
8310                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8311                     field.markInvalid(errors[id]);
8312                 }
8313             }
8314         }
8315         //Roo.each(this.childForms || [], function (f) {
8316         //    f.markInvalid(errors);
8317         //});
8318
8319         return this;
8320     },
8321
8322     /**
8323      * Set values for fields in this form in bulk.
8324      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8325      * @return {BasicForm} this
8326      */
8327     setValues : function(values){
8328         if(values instanceof Array){ // array of objects
8329             for(var i = 0, len = values.length; i < len; i++){
8330                 var v = values[i];
8331                 var f = this.findField(v.id);
8332                 if(f){
8333                     f.setValue(v.value);
8334                     if(this.trackResetOnLoad){
8335                         f.originalValue = f.getValue();
8336                     }
8337                 }
8338             }
8339         }else{ // object hash
8340             var field, id;
8341             for(id in values){
8342                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8343
8344                     if (field.setFromData &&
8345                         field.valueField &&
8346                         field.displayField &&
8347                         // combos' with local stores can
8348                         // be queried via setValue()
8349                         // to set their value..
8350                         (field.store && !field.store.isLocal)
8351                         ) {
8352                         // it's a combo
8353                         var sd = { };
8354                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8355                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8356                         field.setFromData(sd);
8357
8358                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8359                         
8360                         field.setFromData(values);
8361                         
8362                     } else {
8363                         field.setValue(values[id]);
8364                     }
8365
8366
8367                     if(this.trackResetOnLoad){
8368                         field.originalValue = field.getValue();
8369                     }
8370                 }
8371             }
8372         }
8373
8374         //Roo.each(this.childForms || [], function (f) {
8375         //    f.setValues(values);
8376         //});
8377
8378         return this;
8379     },
8380
8381     /**
8382      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8383      * they are returned as an array.
8384      * @param {Boolean} asString
8385      * @return {Object}
8386      */
8387     getValues : function(asString){
8388         //if (this.childForms) {
8389             // copy values from the child forms
8390         //    Roo.each(this.childForms, function (f) {
8391         //        this.setValues(f.getValues());
8392         //    }, this);
8393         //}
8394
8395
8396
8397         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8398         if(asString === true){
8399             return fs;
8400         }
8401         return Roo.urlDecode(fs);
8402     },
8403
8404     /**
8405      * Returns the fields in this form as an object with key/value pairs.
8406      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8407      * @return {Object}
8408      */
8409     getFieldValues : function(with_hidden)
8410     {
8411         var items = this.getItems();
8412         var ret = {};
8413         items.each(function(f){
8414             
8415             if (!f.getName()) {
8416                 return;
8417             }
8418             
8419             var v = f.getValue();
8420             
8421             if (f.inputType =='radio') {
8422                 if (typeof(ret[f.getName()]) == 'undefined') {
8423                     ret[f.getName()] = ''; // empty..
8424                 }
8425
8426                 if (!f.el.dom.checked) {
8427                     return;
8428
8429                 }
8430                 v = f.el.dom.value;
8431
8432             }
8433             
8434             if(f.xtype == 'MoneyField'){
8435                 ret[f.currencyName] = f.getCurrency();
8436             }
8437
8438             // not sure if this supported any more..
8439             if ((typeof(v) == 'object') && f.getRawValue) {
8440                 v = f.getRawValue() ; // dates..
8441             }
8442             // combo boxes where name != hiddenName...
8443             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8444                 ret[f.name] = f.getRawValue();
8445             }
8446             ret[f.getName()] = v;
8447         });
8448
8449         return ret;
8450     },
8451
8452     /**
8453      * Clears all invalid messages in this form.
8454      * @return {BasicForm} this
8455      */
8456     clearInvalid : function(){
8457         var items = this.getItems();
8458
8459         items.each(function(f){
8460            f.clearInvalid();
8461         });
8462
8463         return this;
8464     },
8465
8466     /**
8467      * Resets this form.
8468      * @return {BasicForm} this
8469      */
8470     reset : function(){
8471         var items = this.getItems();
8472         items.each(function(f){
8473             f.reset();
8474         });
8475
8476         Roo.each(this.childForms || [], function (f) {
8477             f.reset();
8478         });
8479
8480
8481         return this;
8482     },
8483     
8484     getItems : function()
8485     {
8486         var r=new Roo.util.MixedCollection(false, function(o){
8487             return o.id || (o.id = Roo.id());
8488         });
8489         var iter = function(el) {
8490             if (el.inputEl) {
8491                 r.add(el);
8492             }
8493             if (!el.items) {
8494                 return;
8495             }
8496             Roo.each(el.items,function(e) {
8497                 iter(e);
8498             });
8499         };
8500
8501         iter(this);
8502         return r;
8503     },
8504     
8505     hideFields : function(items)
8506     {
8507         Roo.each(items, function(i){
8508             
8509             var f = this.findField(i);
8510             
8511             if(!f){
8512                 return;
8513             }
8514             
8515             f.hide();
8516             
8517         }, this);
8518     },
8519     
8520     showFields : function(items)
8521     {
8522         Roo.each(items, function(i){
8523             
8524             var f = this.findField(i);
8525             
8526             if(!f){
8527                 return;
8528             }
8529             
8530             f.show();
8531             
8532         }, this);
8533     }
8534
8535 });
8536
8537 Roo.apply(Roo.bootstrap.Form, {
8538     
8539     popover : {
8540         
8541         padding : 5,
8542         
8543         isApplied : false,
8544         
8545         isMasked : false,
8546         
8547         form : false,
8548         
8549         target : false,
8550         
8551         toolTip : false,
8552         
8553         intervalID : false,
8554         
8555         maskEl : false,
8556         
8557         apply : function()
8558         {
8559             if(this.isApplied){
8560                 return;
8561             }
8562             
8563             this.maskEl = {
8564                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8565                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8566                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8567                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8568             };
8569             
8570             this.maskEl.top.enableDisplayMode("block");
8571             this.maskEl.left.enableDisplayMode("block");
8572             this.maskEl.bottom.enableDisplayMode("block");
8573             this.maskEl.right.enableDisplayMode("block");
8574             
8575             this.toolTip = new Roo.bootstrap.Tooltip({
8576                 cls : 'roo-form-error-popover',
8577                 alignment : {
8578                     'left' : ['r-l', [-2,0], 'right'],
8579                     'right' : ['l-r', [2,0], 'left'],
8580                     'bottom' : ['tl-bl', [0,2], 'top'],
8581                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8582                 }
8583             });
8584             
8585             this.toolTip.render(Roo.get(document.body));
8586
8587             this.toolTip.el.enableDisplayMode("block");
8588             
8589             Roo.get(document.body).on('click', function(){
8590                 this.unmask();
8591             }, this);
8592             
8593             Roo.get(document.body).on('touchstart', function(){
8594                 this.unmask();
8595             }, this);
8596             
8597             this.isApplied = true
8598         },
8599         
8600         mask : function(form, target)
8601         {
8602             this.form = form;
8603             
8604             this.target = target;
8605             
8606             if(!this.form.errorMask || !target.el){
8607                 return;
8608             }
8609             
8610             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8611             
8612             Roo.log(scrollable);
8613             
8614             var ot = this.target.el.calcOffsetsTo(scrollable);
8615             
8616             var scrollTo = ot[1] - this.form.maskOffset;
8617             
8618             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8619             
8620             scrollable.scrollTo('top', scrollTo);
8621             
8622             var box = this.target.el.getBox();
8623             Roo.log(box);
8624             var zIndex = Roo.bootstrap.Modal.zIndex++;
8625
8626             
8627             this.maskEl.top.setStyle('position', 'absolute');
8628             this.maskEl.top.setStyle('z-index', zIndex);
8629             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8630             this.maskEl.top.setLeft(0);
8631             this.maskEl.top.setTop(0);
8632             this.maskEl.top.show();
8633             
8634             this.maskEl.left.setStyle('position', 'absolute');
8635             this.maskEl.left.setStyle('z-index', zIndex);
8636             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8637             this.maskEl.left.setLeft(0);
8638             this.maskEl.left.setTop(box.y - this.padding);
8639             this.maskEl.left.show();
8640
8641             this.maskEl.bottom.setStyle('position', 'absolute');
8642             this.maskEl.bottom.setStyle('z-index', zIndex);
8643             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8644             this.maskEl.bottom.setLeft(0);
8645             this.maskEl.bottom.setTop(box.bottom + this.padding);
8646             this.maskEl.bottom.show();
8647
8648             this.maskEl.right.setStyle('position', 'absolute');
8649             this.maskEl.right.setStyle('z-index', zIndex);
8650             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8651             this.maskEl.right.setLeft(box.right + this.padding);
8652             this.maskEl.right.setTop(box.y - this.padding);
8653             this.maskEl.right.show();
8654
8655             this.toolTip.bindEl = this.target.el;
8656
8657             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8658
8659             var tip = this.target.blankText;
8660
8661             if(this.target.getValue() !== '' ) {
8662                 
8663                 if (this.target.invalidText.length) {
8664                     tip = this.target.invalidText;
8665                 } else if (this.target.regexText.length){
8666                     tip = this.target.regexText;
8667                 }
8668             }
8669
8670             this.toolTip.show(tip);
8671
8672             this.intervalID = window.setInterval(function() {
8673                 Roo.bootstrap.Form.popover.unmask();
8674             }, 10000);
8675
8676             window.onwheel = function(){ return false;};
8677             
8678             (function(){ this.isMasked = true; }).defer(500, this);
8679             
8680         },
8681         
8682         unmask : function()
8683         {
8684             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8685                 return;
8686             }
8687             
8688             this.maskEl.top.setStyle('position', 'absolute');
8689             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8690             this.maskEl.top.hide();
8691
8692             this.maskEl.left.setStyle('position', 'absolute');
8693             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8694             this.maskEl.left.hide();
8695
8696             this.maskEl.bottom.setStyle('position', 'absolute');
8697             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8698             this.maskEl.bottom.hide();
8699
8700             this.maskEl.right.setStyle('position', 'absolute');
8701             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8702             this.maskEl.right.hide();
8703             
8704             this.toolTip.hide();
8705             
8706             this.toolTip.el.hide();
8707             
8708             window.onwheel = function(){ return true;};
8709             
8710             if(this.intervalID){
8711                 window.clearInterval(this.intervalID);
8712                 this.intervalID = false;
8713             }
8714             
8715             this.isMasked = false;
8716             
8717         }
8718         
8719     }
8720     
8721 });
8722
8723 /*
8724  * Based on:
8725  * Ext JS Library 1.1.1
8726  * Copyright(c) 2006-2007, Ext JS, LLC.
8727  *
8728  * Originally Released Under LGPL - original licence link has changed is not relivant.
8729  *
8730  * Fork - LGPL
8731  * <script type="text/javascript">
8732  */
8733 /**
8734  * @class Roo.form.VTypes
8735  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8736  * @singleton
8737  */
8738 Roo.form.VTypes = function(){
8739     // closure these in so they are only created once.
8740     var alpha = /^[a-zA-Z_]+$/;
8741     var alphanum = /^[a-zA-Z0-9_]+$/;
8742     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8743     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8744
8745     // All these messages and functions are configurable
8746     return {
8747         /**
8748          * The function used to validate email addresses
8749          * @param {String} value The email address
8750          */
8751         'email' : function(v){
8752             return email.test(v);
8753         },
8754         /**
8755          * The error text to display when the email validation function returns false
8756          * @type String
8757          */
8758         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8759         /**
8760          * The keystroke filter mask to be applied on email input
8761          * @type RegExp
8762          */
8763         'emailMask' : /[a-z0-9_\.\-@]/i,
8764
8765         /**
8766          * The function used to validate URLs
8767          * @param {String} value The URL
8768          */
8769         'url' : function(v){
8770             return url.test(v);
8771         },
8772         /**
8773          * The error text to display when the url validation function returns false
8774          * @type String
8775          */
8776         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8777         
8778         /**
8779          * The function used to validate alpha values
8780          * @param {String} value The value
8781          */
8782         'alpha' : function(v){
8783             return alpha.test(v);
8784         },
8785         /**
8786          * The error text to display when the alpha validation function returns false
8787          * @type String
8788          */
8789         'alphaText' : 'This field should only contain letters and _',
8790         /**
8791          * The keystroke filter mask to be applied on alpha input
8792          * @type RegExp
8793          */
8794         'alphaMask' : /[a-z_]/i,
8795
8796         /**
8797          * The function used to validate alphanumeric values
8798          * @param {String} value The value
8799          */
8800         'alphanum' : function(v){
8801             return alphanum.test(v);
8802         },
8803         /**
8804          * The error text to display when the alphanumeric validation function returns false
8805          * @type String
8806          */
8807         'alphanumText' : 'This field should only contain letters, numbers and _',
8808         /**
8809          * The keystroke filter mask to be applied on alphanumeric input
8810          * @type RegExp
8811          */
8812         'alphanumMask' : /[a-z0-9_]/i
8813     };
8814 }();/*
8815  * - LGPL
8816  *
8817  * Input
8818  * 
8819  */
8820
8821 /**
8822  * @class Roo.bootstrap.Input
8823  * @extends Roo.bootstrap.Component
8824  * Bootstrap Input class
8825  * @cfg {Boolean} disabled is it disabled
8826  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8827  * @cfg {String} name name of the input
8828  * @cfg {string} fieldLabel - the label associated
8829  * @cfg {string} placeholder - placeholder to put in text.
8830  * @cfg {string}  before - input group add on before
8831  * @cfg {string} after - input group add on after
8832  * @cfg {string} size - (lg|sm) or leave empty..
8833  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8834  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8835  * @cfg {Number} md colspan out of 12 for computer-sized screens
8836  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8837  * @cfg {string} value default value of the input
8838  * @cfg {Number} labelWidth set the width of label 
8839  * @cfg {Number} labellg set the width of label (1-12)
8840  * @cfg {Number} labelmd set the width of label (1-12)
8841  * @cfg {Number} labelsm set the width of label (1-12)
8842  * @cfg {Number} labelxs set the width of label (1-12)
8843  * @cfg {String} labelAlign (top|left)
8844  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8845  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8846  * @cfg {String} indicatorpos (left|right) default left
8847  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8848  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8849
8850  * @cfg {String} align (left|center|right) Default left
8851  * @cfg {Boolean} forceFeedback (true|false) Default false
8852  * 
8853  * @constructor
8854  * Create a new Input
8855  * @param {Object} config The config object
8856  */
8857
8858 Roo.bootstrap.Input = function(config){
8859     
8860     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8861     
8862     this.addEvents({
8863         /**
8864          * @event focus
8865          * Fires when this field receives input focus.
8866          * @param {Roo.form.Field} this
8867          */
8868         focus : true,
8869         /**
8870          * @event blur
8871          * Fires when this field loses input focus.
8872          * @param {Roo.form.Field} this
8873          */
8874         blur : true,
8875         /**
8876          * @event specialkey
8877          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8878          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8879          * @param {Roo.form.Field} this
8880          * @param {Roo.EventObject} e The event object
8881          */
8882         specialkey : true,
8883         /**
8884          * @event change
8885          * Fires just before the field blurs if the field value has changed.
8886          * @param {Roo.form.Field} this
8887          * @param {Mixed} newValue The new value
8888          * @param {Mixed} oldValue The original value
8889          */
8890         change : true,
8891         /**
8892          * @event invalid
8893          * Fires after the field has been marked as invalid.
8894          * @param {Roo.form.Field} this
8895          * @param {String} msg The validation message
8896          */
8897         invalid : true,
8898         /**
8899          * @event valid
8900          * Fires after the field has been validated with no errors.
8901          * @param {Roo.form.Field} this
8902          */
8903         valid : true,
8904          /**
8905          * @event keyup
8906          * Fires after the key up
8907          * @param {Roo.form.Field} this
8908          * @param {Roo.EventObject}  e The event Object
8909          */
8910         keyup : true
8911     });
8912 };
8913
8914 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8915      /**
8916      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8917       automatic validation (defaults to "keyup").
8918      */
8919     validationEvent : "keyup",
8920      /**
8921      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8922      */
8923     validateOnBlur : true,
8924     /**
8925      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8926      */
8927     validationDelay : 250,
8928      /**
8929      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8930      */
8931     focusClass : "x-form-focus",  // not needed???
8932     
8933        
8934     /**
8935      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8936      */
8937     invalidClass : "has-warning",
8938     
8939     /**
8940      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8941      */
8942     validClass : "has-success",
8943     
8944     /**
8945      * @cfg {Boolean} hasFeedback (true|false) default true
8946      */
8947     hasFeedback : true,
8948     
8949     /**
8950      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8951      */
8952     invalidFeedbackClass : "glyphicon-warning-sign",
8953     
8954     /**
8955      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8956      */
8957     validFeedbackClass : "glyphicon-ok",
8958     
8959     /**
8960      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8961      */
8962     selectOnFocus : false,
8963     
8964      /**
8965      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8966      */
8967     maskRe : null,
8968        /**
8969      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8970      */
8971     vtype : null,
8972     
8973       /**
8974      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8975      */
8976     disableKeyFilter : false,
8977     
8978        /**
8979      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8980      */
8981     disabled : false,
8982      /**
8983      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8984      */
8985     allowBlank : true,
8986     /**
8987      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8988      */
8989     blankText : "Please complete this mandatory field",
8990     
8991      /**
8992      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8993      */
8994     minLength : 0,
8995     /**
8996      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8997      */
8998     maxLength : Number.MAX_VALUE,
8999     /**
9000      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9001      */
9002     minLengthText : "The minimum length for this field is {0}",
9003     /**
9004      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9005      */
9006     maxLengthText : "The maximum length for this field is {0}",
9007   
9008     
9009     /**
9010      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9011      * If available, this function will be called only after the basic validators all return true, and will be passed the
9012      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9013      */
9014     validator : null,
9015     /**
9016      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9017      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9018      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9019      */
9020     regex : null,
9021     /**
9022      * @cfg {String} regexText -- Depricated - use Invalid Text
9023      */
9024     regexText : "",
9025     
9026     /**
9027      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9028      */
9029     invalidText : "",
9030     
9031     
9032     
9033     autocomplete: false,
9034     
9035     
9036     fieldLabel : '',
9037     inputType : 'text',
9038     
9039     name : false,
9040     placeholder: false,
9041     before : false,
9042     after : false,
9043     size : false,
9044     hasFocus : false,
9045     preventMark: false,
9046     isFormField : true,
9047     value : '',
9048     labelWidth : 2,
9049     labelAlign : false,
9050     readOnly : false,
9051     align : false,
9052     formatedValue : false,
9053     forceFeedback : false,
9054     
9055     indicatorpos : 'left',
9056     
9057     labellg : 0,
9058     labelmd : 0,
9059     labelsm : 0,
9060     labelxs : 0,
9061     
9062     capture : '',
9063     accept : '',
9064     
9065     parentLabelAlign : function()
9066     {
9067         var parent = this;
9068         while (parent.parent()) {
9069             parent = parent.parent();
9070             if (typeof(parent.labelAlign) !='undefined') {
9071                 return parent.labelAlign;
9072             }
9073         }
9074         return 'left';
9075         
9076     },
9077     
9078     getAutoCreate : function()
9079     {
9080         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9081         
9082         var id = Roo.id();
9083         
9084         var cfg = {};
9085         
9086         if(this.inputType != 'hidden'){
9087             cfg.cls = 'form-group' //input-group
9088         }
9089         
9090         var input =  {
9091             tag: 'input',
9092             id : id,
9093             type : this.inputType,
9094             value : this.value,
9095             cls : 'form-control',
9096             placeholder : this.placeholder || '',
9097             autocomplete : this.autocomplete || 'new-password'
9098         };
9099         
9100         if(this.capture.length){
9101             input.capture = this.capture;
9102         }
9103         
9104         if(this.accept.length){
9105             input.accept = this.accept + "/*";
9106         }
9107         
9108         if(this.align){
9109             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9110         }
9111         
9112         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9113             input.maxLength = this.maxLength;
9114         }
9115         
9116         if (this.disabled) {
9117             input.disabled=true;
9118         }
9119         
9120         if (this.readOnly) {
9121             input.readonly=true;
9122         }
9123         
9124         if (this.name) {
9125             input.name = this.name;
9126         }
9127         
9128         if (this.size) {
9129             input.cls += ' input-' + this.size;
9130         }
9131         
9132         var settings=this;
9133         ['xs','sm','md','lg'].map(function(size){
9134             if (settings[size]) {
9135                 cfg.cls += ' col-' + size + '-' + settings[size];
9136             }
9137         });
9138         
9139         var inputblock = input;
9140         
9141         var feedback = {
9142             tag: 'span',
9143             cls: 'glyphicon form-control-feedback'
9144         };
9145             
9146         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9147             
9148             inputblock = {
9149                 cls : 'has-feedback',
9150                 cn :  [
9151                     input,
9152                     feedback
9153                 ] 
9154             };  
9155         }
9156         
9157         if (this.before || this.after) {
9158             
9159             inputblock = {
9160                 cls : 'input-group',
9161                 cn :  [] 
9162             };
9163             
9164             if (this.before && typeof(this.before) == 'string') {
9165                 
9166                 inputblock.cn.push({
9167                     tag :'span',
9168                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9169                     html : this.before
9170                 });
9171             }
9172             if (this.before && typeof(this.before) == 'object') {
9173                 this.before = Roo.factory(this.before);
9174                 
9175                 inputblock.cn.push({
9176                     tag :'span',
9177                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9178                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9179                 });
9180             }
9181             
9182             inputblock.cn.push(input);
9183             
9184             if (this.after && typeof(this.after) == 'string') {
9185                 inputblock.cn.push({
9186                     tag :'span',
9187                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9188                     html : this.after
9189                 });
9190             }
9191             if (this.after && typeof(this.after) == 'object') {
9192                 this.after = Roo.factory(this.after);
9193                 
9194                 inputblock.cn.push({
9195                     tag :'span',
9196                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9197                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9198                 });
9199             }
9200             
9201             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9202                 inputblock.cls += ' has-feedback';
9203                 inputblock.cn.push(feedback);
9204             }
9205         };
9206         var indicator = {
9207             tag : 'i',
9208             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9209             tooltip : 'This field is required'
9210         };
9211         if (Roo.bootstrap.version == 4) {
9212             indicator = {
9213                 tag : 'i',
9214                 style : 'display-none'
9215             };
9216         }
9217         if (align ==='left' && this.fieldLabel.length) {
9218             
9219             cfg.cls += ' roo-form-group-label-left row';
9220             
9221             cfg.cn = [
9222                 indicator,
9223                 {
9224                     tag: 'label',
9225                     'for' :  id,
9226                     cls : 'control-label col-form-label',
9227                     html : this.fieldLabel
9228
9229                 },
9230                 {
9231                     cls : "", 
9232                     cn: [
9233                         inputblock
9234                     ]
9235                 }
9236             ];
9237             
9238             var labelCfg = cfg.cn[1];
9239             var contentCfg = cfg.cn[2];
9240             
9241             if(this.indicatorpos == 'right'){
9242                 cfg.cn = [
9243                     {
9244                         tag: 'label',
9245                         'for' :  id,
9246                         cls : 'control-label col-form-label',
9247                         cn : [
9248                             {
9249                                 tag : 'span',
9250                                 html : this.fieldLabel
9251                             },
9252                             indicator
9253                         ]
9254                     },
9255                     {
9256                         cls : "",
9257                         cn: [
9258                             inputblock
9259                         ]
9260                     }
9261
9262                 ];
9263                 
9264                 labelCfg = cfg.cn[0];
9265                 contentCfg = cfg.cn[1];
9266             
9267             }
9268             
9269             if(this.labelWidth > 12){
9270                 labelCfg.style = "width: " + this.labelWidth + 'px';
9271             }
9272             
9273             if(this.labelWidth < 13 && this.labelmd == 0){
9274                 this.labelmd = this.labelWidth;
9275             }
9276             
9277             if(this.labellg > 0){
9278                 labelCfg.cls += ' col-lg-' + this.labellg;
9279                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9280             }
9281             
9282             if(this.labelmd > 0){
9283                 labelCfg.cls += ' col-md-' + this.labelmd;
9284                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9285             }
9286             
9287             if(this.labelsm > 0){
9288                 labelCfg.cls += ' col-sm-' + this.labelsm;
9289                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9290             }
9291             
9292             if(this.labelxs > 0){
9293                 labelCfg.cls += ' col-xs-' + this.labelxs;
9294                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9295             }
9296             
9297             
9298         } else if ( this.fieldLabel.length) {
9299                 
9300             cfg.cn = [
9301                 {
9302                     tag : 'i',
9303                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9304                     tooltip : 'This field is required'
9305                 },
9306                 {
9307                     tag: 'label',
9308                    //cls : 'input-group-addon',
9309                     html : this.fieldLabel
9310
9311                 },
9312
9313                inputblock
9314
9315            ];
9316            
9317            if(this.indicatorpos == 'right'){
9318                 
9319                 cfg.cn = [
9320                     {
9321                         tag: 'label',
9322                        //cls : 'input-group-addon',
9323                         html : this.fieldLabel
9324
9325                     },
9326                     {
9327                         tag : 'i',
9328                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9329                         tooltip : 'This field is required'
9330                     },
9331
9332                    inputblock
9333
9334                ];
9335
9336             }
9337
9338         } else {
9339             
9340             cfg.cn = [
9341
9342                     inputblock
9343
9344             ];
9345                 
9346                 
9347         };
9348         
9349         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9350            cfg.cls += ' navbar-form';
9351         }
9352         
9353         if (this.parentType === 'NavGroup') {
9354            cfg.cls += ' navbar-form';
9355            cfg.tag = 'li';
9356         }
9357         
9358         return cfg;
9359         
9360     },
9361     /**
9362      * return the real input element.
9363      */
9364     inputEl: function ()
9365     {
9366         return this.el.select('input.form-control',true).first();
9367     },
9368     
9369     tooltipEl : function()
9370     {
9371         return this.inputEl();
9372     },
9373     
9374     indicatorEl : function()
9375     {
9376         if (Roo.bootstrap.version == 4) {
9377             return false; // not enabled in v4 yet.
9378         }
9379         
9380         var indicator = this.el.select('i.roo-required-indicator',true).first();
9381         
9382         if(!indicator){
9383             return false;
9384         }
9385         
9386         return indicator;
9387         
9388     },
9389     
9390     setDisabled : function(v)
9391     {
9392         var i  = this.inputEl().dom;
9393         if (!v) {
9394             i.removeAttribute('disabled');
9395             return;
9396             
9397         }
9398         i.setAttribute('disabled','true');
9399     },
9400     initEvents : function()
9401     {
9402           
9403         this.inputEl().on("keydown" , this.fireKey,  this);
9404         this.inputEl().on("focus", this.onFocus,  this);
9405         this.inputEl().on("blur", this.onBlur,  this);
9406         
9407         this.inputEl().relayEvent('keyup', this);
9408         
9409         this.indicator = this.indicatorEl();
9410         
9411         if(this.indicator){
9412             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9413         }
9414  
9415         // reference to original value for reset
9416         this.originalValue = this.getValue();
9417         //Roo.form.TextField.superclass.initEvents.call(this);
9418         if(this.validationEvent == 'keyup'){
9419             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9420             this.inputEl().on('keyup', this.filterValidation, this);
9421         }
9422         else if(this.validationEvent !== false){
9423             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9424         }
9425         
9426         if(this.selectOnFocus){
9427             this.on("focus", this.preFocus, this);
9428             
9429         }
9430         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9431             this.inputEl().on("keypress", this.filterKeys, this);
9432         } else {
9433             this.inputEl().relayEvent('keypress', this);
9434         }
9435        /* if(this.grow){
9436             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9437             this.el.on("click", this.autoSize,  this);
9438         }
9439         */
9440         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9441             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9442         }
9443         
9444         if (typeof(this.before) == 'object') {
9445             this.before.render(this.el.select('.roo-input-before',true).first());
9446         }
9447         if (typeof(this.after) == 'object') {
9448             this.after.render(this.el.select('.roo-input-after',true).first());
9449         }
9450         
9451         this.inputEl().on('change', this.onChange, this);
9452         
9453     },
9454     filterValidation : function(e){
9455         if(!e.isNavKeyPress()){
9456             this.validationTask.delay(this.validationDelay);
9457         }
9458     },
9459      /**
9460      * Validates the field value
9461      * @return {Boolean} True if the value is valid, else false
9462      */
9463     validate : function(){
9464         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9465         if(this.disabled || this.validateValue(this.getRawValue())){
9466             this.markValid();
9467             return true;
9468         }
9469         
9470         this.markInvalid();
9471         return false;
9472     },
9473     
9474     
9475     /**
9476      * Validates a value according to the field's validation rules and marks the field as invalid
9477      * if the validation fails
9478      * @param {Mixed} value The value to validate
9479      * @return {Boolean} True if the value is valid, else false
9480      */
9481     validateValue : function(value)
9482     {
9483         if(this.getVisibilityEl().hasClass('hidden')){
9484             return true;
9485         }
9486         
9487         if(value.length < 1)  { // if it's blank
9488             if(this.allowBlank){
9489                 return true;
9490             }
9491             return false;
9492         }
9493         
9494         if(value.length < this.minLength){
9495             return false;
9496         }
9497         if(value.length > this.maxLength){
9498             return false;
9499         }
9500         if(this.vtype){
9501             var vt = Roo.form.VTypes;
9502             if(!vt[this.vtype](value, this)){
9503                 return false;
9504             }
9505         }
9506         if(typeof this.validator == "function"){
9507             var msg = this.validator(value);
9508             if(msg !== true){
9509                 return false;
9510             }
9511             if (typeof(msg) == 'string') {
9512                 this.invalidText = msg;
9513             }
9514         }
9515         
9516         if(this.regex && !this.regex.test(value)){
9517             return false;
9518         }
9519         
9520         return true;
9521     },
9522     
9523      // private
9524     fireKey : function(e){
9525         //Roo.log('field ' + e.getKey());
9526         if(e.isNavKeyPress()){
9527             this.fireEvent("specialkey", this, e);
9528         }
9529     },
9530     focus : function (selectText){
9531         if(this.rendered){
9532             this.inputEl().focus();
9533             if(selectText === true){
9534                 this.inputEl().dom.select();
9535             }
9536         }
9537         return this;
9538     } ,
9539     
9540     onFocus : function(){
9541         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9542            // this.el.addClass(this.focusClass);
9543         }
9544         if(!this.hasFocus){
9545             this.hasFocus = true;
9546             this.startValue = this.getValue();
9547             this.fireEvent("focus", this);
9548         }
9549     },
9550     
9551     beforeBlur : Roo.emptyFn,
9552
9553     
9554     // private
9555     onBlur : function(){
9556         this.beforeBlur();
9557         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9558             //this.el.removeClass(this.focusClass);
9559         }
9560         this.hasFocus = false;
9561         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9562             this.validate();
9563         }
9564         var v = this.getValue();
9565         if(String(v) !== String(this.startValue)){
9566             this.fireEvent('change', this, v, this.startValue);
9567         }
9568         this.fireEvent("blur", this);
9569     },
9570     
9571     onChange : function(e)
9572     {
9573         var v = this.getValue();
9574         if(String(v) !== String(this.startValue)){
9575             this.fireEvent('change', this, v, this.startValue);
9576         }
9577         
9578     },
9579     
9580     /**
9581      * Resets the current field value to the originally loaded value and clears any validation messages
9582      */
9583     reset : function(){
9584         this.setValue(this.originalValue);
9585         this.validate();
9586     },
9587      /**
9588      * Returns the name of the field
9589      * @return {Mixed} name The name field
9590      */
9591     getName: function(){
9592         return this.name;
9593     },
9594      /**
9595      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9596      * @return {Mixed} value The field value
9597      */
9598     getValue : function(){
9599         
9600         var v = this.inputEl().getValue();
9601         
9602         return v;
9603     },
9604     /**
9605      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9606      * @return {Mixed} value The field value
9607      */
9608     getRawValue : function(){
9609         var v = this.inputEl().getValue();
9610         
9611         return v;
9612     },
9613     
9614     /**
9615      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9616      * @param {Mixed} value The value to set
9617      */
9618     setRawValue : function(v){
9619         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9620     },
9621     
9622     selectText : function(start, end){
9623         var v = this.getRawValue();
9624         if(v.length > 0){
9625             start = start === undefined ? 0 : start;
9626             end = end === undefined ? v.length : end;
9627             var d = this.inputEl().dom;
9628             if(d.setSelectionRange){
9629                 d.setSelectionRange(start, end);
9630             }else if(d.createTextRange){
9631                 var range = d.createTextRange();
9632                 range.moveStart("character", start);
9633                 range.moveEnd("character", v.length-end);
9634                 range.select();
9635             }
9636         }
9637     },
9638     
9639     /**
9640      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9641      * @param {Mixed} value The value to set
9642      */
9643     setValue : function(v){
9644         this.value = v;
9645         if(this.rendered){
9646             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9647             this.validate();
9648         }
9649     },
9650     
9651     /*
9652     processValue : function(value){
9653         if(this.stripCharsRe){
9654             var newValue = value.replace(this.stripCharsRe, '');
9655             if(newValue !== value){
9656                 this.setRawValue(newValue);
9657                 return newValue;
9658             }
9659         }
9660         return value;
9661     },
9662   */
9663     preFocus : function(){
9664         
9665         if(this.selectOnFocus){
9666             this.inputEl().dom.select();
9667         }
9668     },
9669     filterKeys : function(e){
9670         var k = e.getKey();
9671         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9672             return;
9673         }
9674         var c = e.getCharCode(), cc = String.fromCharCode(c);
9675         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9676             return;
9677         }
9678         if(!this.maskRe.test(cc)){
9679             e.stopEvent();
9680         }
9681     },
9682      /**
9683      * Clear any invalid styles/messages for this field
9684      */
9685     clearInvalid : function(){
9686         
9687         if(!this.el || this.preventMark){ // not rendered
9688             return;
9689         }
9690         
9691      
9692         this.el.removeClass(this.invalidClass);
9693         
9694         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9695             
9696             var feedback = this.el.select('.form-control-feedback', true).first();
9697             
9698             if(feedback){
9699                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9700             }
9701             
9702         }
9703         
9704         if(this.indicator){
9705             this.indicator.removeClass('visible');
9706             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9707         }
9708         
9709         this.fireEvent('valid', this);
9710     },
9711     
9712      /**
9713      * Mark this field as valid
9714      */
9715     markValid : function()
9716     {
9717         if(!this.el  || this.preventMark){ // not rendered...
9718             return;
9719         }
9720         
9721         this.el.removeClass([this.invalidClass, this.validClass]);
9722         
9723         var feedback = this.el.select('.form-control-feedback', true).first();
9724             
9725         if(feedback){
9726             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9727         }
9728         
9729         if(this.indicator){
9730             this.indicator.removeClass('visible');
9731             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9732         }
9733         
9734         if(this.disabled){
9735             return;
9736         }
9737         
9738         if(this.allowBlank && !this.getRawValue().length){
9739             return;
9740         }
9741         
9742         this.el.addClass(this.validClass);
9743         
9744         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9745             
9746             var feedback = this.el.select('.form-control-feedback', true).first();
9747             
9748             if(feedback){
9749                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9750                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9751             }
9752             
9753         }
9754         
9755         this.fireEvent('valid', this);
9756     },
9757     
9758      /**
9759      * Mark this field as invalid
9760      * @param {String} msg The validation message
9761      */
9762     markInvalid : function(msg)
9763     {
9764         if(!this.el  || this.preventMark){ // not rendered
9765             return;
9766         }
9767         
9768         this.el.removeClass([this.invalidClass, this.validClass]);
9769         
9770         var feedback = this.el.select('.form-control-feedback', true).first();
9771             
9772         if(feedback){
9773             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9774         }
9775
9776         if(this.disabled){
9777             return;
9778         }
9779         
9780         if(this.allowBlank && !this.getRawValue().length){
9781             return;
9782         }
9783         
9784         if(this.indicator){
9785             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9786             this.indicator.addClass('visible');
9787         }
9788         
9789         this.el.addClass(this.invalidClass);
9790         
9791         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9792             
9793             var feedback = this.el.select('.form-control-feedback', true).first();
9794             
9795             if(feedback){
9796                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9797                 
9798                 if(this.getValue().length || this.forceFeedback){
9799                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9800                 }
9801                 
9802             }
9803             
9804         }
9805         
9806         this.fireEvent('invalid', this, msg);
9807     },
9808     // private
9809     SafariOnKeyDown : function(event)
9810     {
9811         // this is a workaround for a password hang bug on chrome/ webkit.
9812         if (this.inputEl().dom.type != 'password') {
9813             return;
9814         }
9815         
9816         var isSelectAll = false;
9817         
9818         if(this.inputEl().dom.selectionEnd > 0){
9819             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9820         }
9821         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9822             event.preventDefault();
9823             this.setValue('');
9824             return;
9825         }
9826         
9827         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9828             
9829             event.preventDefault();
9830             // this is very hacky as keydown always get's upper case.
9831             //
9832             var cc = String.fromCharCode(event.getCharCode());
9833             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9834             
9835         }
9836     },
9837     adjustWidth : function(tag, w){
9838         tag = tag.toLowerCase();
9839         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9840             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9841                 if(tag == 'input'){
9842                     return w + 2;
9843                 }
9844                 if(tag == 'textarea'){
9845                     return w-2;
9846                 }
9847             }else if(Roo.isOpera){
9848                 if(tag == 'input'){
9849                     return w + 2;
9850                 }
9851                 if(tag == 'textarea'){
9852                     return w-2;
9853                 }
9854             }
9855         }
9856         return w;
9857     },
9858     
9859     setFieldLabel : function(v)
9860     {
9861         if(!this.rendered){
9862             return;
9863         }
9864         
9865         if(this.indicatorEl()){
9866             var ar = this.el.select('label > span',true);
9867             
9868             if (ar.elements.length) {
9869                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9870                 this.fieldLabel = v;
9871                 return;
9872             }
9873             
9874             var br = this.el.select('label',true);
9875             
9876             if(br.elements.length) {
9877                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9878                 this.fieldLabel = v;
9879                 return;
9880             }
9881             
9882             Roo.log('Cannot Found any of label > span || label in input');
9883             return;
9884         }
9885         
9886         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9887         this.fieldLabel = v;
9888         
9889         
9890     }
9891 });
9892
9893  
9894 /*
9895  * - LGPL
9896  *
9897  * Input
9898  * 
9899  */
9900
9901 /**
9902  * @class Roo.bootstrap.TextArea
9903  * @extends Roo.bootstrap.Input
9904  * Bootstrap TextArea class
9905  * @cfg {Number} cols Specifies the visible width of a text area
9906  * @cfg {Number} rows Specifies the visible number of lines in a text area
9907  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9908  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9909  * @cfg {string} html text
9910  * 
9911  * @constructor
9912  * Create a new TextArea
9913  * @param {Object} config The config object
9914  */
9915
9916 Roo.bootstrap.TextArea = function(config){
9917     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9918    
9919 };
9920
9921 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9922      
9923     cols : false,
9924     rows : 5,
9925     readOnly : false,
9926     warp : 'soft',
9927     resize : false,
9928     value: false,
9929     html: false,
9930     
9931     getAutoCreate : function(){
9932         
9933         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9934         
9935         var id = Roo.id();
9936         
9937         var cfg = {};
9938         
9939         if(this.inputType != 'hidden'){
9940             cfg.cls = 'form-group' //input-group
9941         }
9942         
9943         var input =  {
9944             tag: 'textarea',
9945             id : id,
9946             warp : this.warp,
9947             rows : this.rows,
9948             value : this.value || '',
9949             html: this.html || '',
9950             cls : 'form-control',
9951             placeholder : this.placeholder || '' 
9952             
9953         };
9954         
9955         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9956             input.maxLength = this.maxLength;
9957         }
9958         
9959         if(this.resize){
9960             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9961         }
9962         
9963         if(this.cols){
9964             input.cols = this.cols;
9965         }
9966         
9967         if (this.readOnly) {
9968             input.readonly = true;
9969         }
9970         
9971         if (this.name) {
9972             input.name = this.name;
9973         }
9974         
9975         if (this.size) {
9976             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9977         }
9978         
9979         var settings=this;
9980         ['xs','sm','md','lg'].map(function(size){
9981             if (settings[size]) {
9982                 cfg.cls += ' col-' + size + '-' + settings[size];
9983             }
9984         });
9985         
9986         var inputblock = input;
9987         
9988         if(this.hasFeedback && !this.allowBlank){
9989             
9990             var feedback = {
9991                 tag: 'span',
9992                 cls: 'glyphicon form-control-feedback'
9993             };
9994
9995             inputblock = {
9996                 cls : 'has-feedback',
9997                 cn :  [
9998                     input,
9999                     feedback
10000                 ] 
10001             };  
10002         }
10003         
10004         
10005         if (this.before || this.after) {
10006             
10007             inputblock = {
10008                 cls : 'input-group',
10009                 cn :  [] 
10010             };
10011             if (this.before) {
10012                 inputblock.cn.push({
10013                     tag :'span',
10014                     cls : 'input-group-addon',
10015                     html : this.before
10016                 });
10017             }
10018             
10019             inputblock.cn.push(input);
10020             
10021             if(this.hasFeedback && !this.allowBlank){
10022                 inputblock.cls += ' has-feedback';
10023                 inputblock.cn.push(feedback);
10024             }
10025             
10026             if (this.after) {
10027                 inputblock.cn.push({
10028                     tag :'span',
10029                     cls : 'input-group-addon',
10030                     html : this.after
10031                 });
10032             }
10033             
10034         }
10035         
10036         if (align ==='left' && this.fieldLabel.length) {
10037             cfg.cn = [
10038                 {
10039                     tag: 'label',
10040                     'for' :  id,
10041                     cls : 'control-label',
10042                     html : this.fieldLabel
10043                 },
10044                 {
10045                     cls : "",
10046                     cn: [
10047                         inputblock
10048                     ]
10049                 }
10050
10051             ];
10052             
10053             if(this.labelWidth > 12){
10054                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10055             }
10056
10057             if(this.labelWidth < 13 && this.labelmd == 0){
10058                 this.labelmd = this.labelWidth;
10059             }
10060
10061             if(this.labellg > 0){
10062                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10063                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10064             }
10065
10066             if(this.labelmd > 0){
10067                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10068                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10069             }
10070
10071             if(this.labelsm > 0){
10072                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10073                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10074             }
10075
10076             if(this.labelxs > 0){
10077                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10078                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10079             }
10080             
10081         } else if ( this.fieldLabel.length) {
10082             cfg.cn = [
10083
10084                {
10085                    tag: 'label',
10086                    //cls : 'input-group-addon',
10087                    html : this.fieldLabel
10088
10089                },
10090
10091                inputblock
10092
10093            ];
10094
10095         } else {
10096
10097             cfg.cn = [
10098
10099                 inputblock
10100
10101             ];
10102                 
10103         }
10104         
10105         if (this.disabled) {
10106             input.disabled=true;
10107         }
10108         
10109         return cfg;
10110         
10111     },
10112     /**
10113      * return the real textarea element.
10114      */
10115     inputEl: function ()
10116     {
10117         return this.el.select('textarea.form-control',true).first();
10118     },
10119     
10120     /**
10121      * Clear any invalid styles/messages for this field
10122      */
10123     clearInvalid : function()
10124     {
10125         
10126         if(!this.el || this.preventMark){ // not rendered
10127             return;
10128         }
10129         
10130         var label = this.el.select('label', true).first();
10131         var icon = this.el.select('i.fa-star', true).first();
10132         
10133         if(label && icon){
10134             icon.remove();
10135         }
10136         
10137         this.el.removeClass(this.invalidClass);
10138         
10139         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10140             
10141             var feedback = this.el.select('.form-control-feedback', true).first();
10142             
10143             if(feedback){
10144                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10145             }
10146             
10147         }
10148         
10149         this.fireEvent('valid', this);
10150     },
10151     
10152      /**
10153      * Mark this field as valid
10154      */
10155     markValid : function()
10156     {
10157         if(!this.el  || this.preventMark){ // not rendered
10158             return;
10159         }
10160         
10161         this.el.removeClass([this.invalidClass, this.validClass]);
10162         
10163         var feedback = this.el.select('.form-control-feedback', true).first();
10164             
10165         if(feedback){
10166             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10167         }
10168
10169         if(this.disabled || this.allowBlank){
10170             return;
10171         }
10172         
10173         var label = this.el.select('label', true).first();
10174         var icon = this.el.select('i.fa-star', true).first();
10175         
10176         if(label && icon){
10177             icon.remove();
10178         }
10179         
10180         this.el.addClass(this.validClass);
10181         
10182         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10183             
10184             var feedback = this.el.select('.form-control-feedback', true).first();
10185             
10186             if(feedback){
10187                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10188                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10189             }
10190             
10191         }
10192         
10193         this.fireEvent('valid', this);
10194     },
10195     
10196      /**
10197      * Mark this field as invalid
10198      * @param {String} msg The validation message
10199      */
10200     markInvalid : function(msg)
10201     {
10202         if(!this.el  || this.preventMark){ // not rendered
10203             return;
10204         }
10205         
10206         this.el.removeClass([this.invalidClass, this.validClass]);
10207         
10208         var feedback = this.el.select('.form-control-feedback', true).first();
10209             
10210         if(feedback){
10211             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10212         }
10213
10214         if(this.disabled || this.allowBlank){
10215             return;
10216         }
10217         
10218         var label = this.el.select('label', true).first();
10219         var icon = this.el.select('i.fa-star', true).first();
10220         
10221         if(!this.getValue().length && label && !icon){
10222             this.el.createChild({
10223                 tag : 'i',
10224                 cls : 'text-danger fa fa-lg fa-star',
10225                 tooltip : 'This field is required',
10226                 style : 'margin-right:5px;'
10227             }, label, true);
10228         }
10229
10230         this.el.addClass(this.invalidClass);
10231         
10232         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10233             
10234             var feedback = this.el.select('.form-control-feedback', true).first();
10235             
10236             if(feedback){
10237                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10238                 
10239                 if(this.getValue().length || this.forceFeedback){
10240                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10241                 }
10242                 
10243             }
10244             
10245         }
10246         
10247         this.fireEvent('invalid', this, msg);
10248     }
10249 });
10250
10251  
10252 /*
10253  * - LGPL
10254  *
10255  * trigger field - base class for combo..
10256  * 
10257  */
10258  
10259 /**
10260  * @class Roo.bootstrap.TriggerField
10261  * @extends Roo.bootstrap.Input
10262  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10263  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10264  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10265  * for which you can provide a custom implementation.  For example:
10266  * <pre><code>
10267 var trigger = new Roo.bootstrap.TriggerField();
10268 trigger.onTriggerClick = myTriggerFn;
10269 trigger.applyTo('my-field');
10270 </code></pre>
10271  *
10272  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10273  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10274  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10275  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10276  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10277
10278  * @constructor
10279  * Create a new TriggerField.
10280  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10281  * to the base TextField)
10282  */
10283 Roo.bootstrap.TriggerField = function(config){
10284     this.mimicing = false;
10285     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10286 };
10287
10288 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10289     /**
10290      * @cfg {String} triggerClass A CSS class to apply to the trigger
10291      */
10292      /**
10293      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10294      */
10295     hideTrigger:false,
10296
10297     /**
10298      * @cfg {Boolean} removable (true|false) special filter default false
10299      */
10300     removable : false,
10301     
10302     /** @cfg {Boolean} grow @hide */
10303     /** @cfg {Number} growMin @hide */
10304     /** @cfg {Number} growMax @hide */
10305
10306     /**
10307      * @hide 
10308      * @method
10309      */
10310     autoSize: Roo.emptyFn,
10311     // private
10312     monitorTab : true,
10313     // private
10314     deferHeight : true,
10315
10316     
10317     actionMode : 'wrap',
10318     
10319     caret : false,
10320     
10321     
10322     getAutoCreate : function(){
10323        
10324         var align = this.labelAlign || this.parentLabelAlign();
10325         
10326         var id = Roo.id();
10327         
10328         var cfg = {
10329             cls: 'form-group' //input-group
10330         };
10331         
10332         
10333         var input =  {
10334             tag: 'input',
10335             id : id,
10336             type : this.inputType,
10337             cls : 'form-control',
10338             autocomplete: 'new-password',
10339             placeholder : this.placeholder || '' 
10340             
10341         };
10342         if (this.name) {
10343             input.name = this.name;
10344         }
10345         if (this.size) {
10346             input.cls += ' input-' + this.size;
10347         }
10348         
10349         if (this.disabled) {
10350             input.disabled=true;
10351         }
10352         
10353         var inputblock = input;
10354         
10355         if(this.hasFeedback && !this.allowBlank){
10356             
10357             var feedback = {
10358                 tag: 'span',
10359                 cls: 'glyphicon form-control-feedback'
10360             };
10361             
10362             if(this.removable && !this.editable && !this.tickable){
10363                 inputblock = {
10364                     cls : 'has-feedback',
10365                     cn :  [
10366                         inputblock,
10367                         {
10368                             tag: 'button',
10369                             html : 'x',
10370                             cls : 'roo-combo-removable-btn close'
10371                         },
10372                         feedback
10373                     ] 
10374                 };
10375             } else {
10376                 inputblock = {
10377                     cls : 'has-feedback',
10378                     cn :  [
10379                         inputblock,
10380                         feedback
10381                     ] 
10382                 };
10383             }
10384
10385         } else {
10386             if(this.removable && !this.editable && !this.tickable){
10387                 inputblock = {
10388                     cls : 'roo-removable',
10389                     cn :  [
10390                         inputblock,
10391                         {
10392                             tag: 'button',
10393                             html : 'x',
10394                             cls : 'roo-combo-removable-btn close'
10395                         }
10396                     ] 
10397                 };
10398             }
10399         }
10400         
10401         if (this.before || this.after) {
10402             
10403             inputblock = {
10404                 cls : 'input-group',
10405                 cn :  [] 
10406             };
10407             if (this.before) {
10408                 inputblock.cn.push({
10409                     tag :'span',
10410                     cls : 'input-group-addon input-group-prepend input-group-text',
10411                     html : this.before
10412                 });
10413             }
10414             
10415             inputblock.cn.push(input);
10416             
10417             if(this.hasFeedback && !this.allowBlank){
10418                 inputblock.cls += ' has-feedback';
10419                 inputblock.cn.push(feedback);
10420             }
10421             
10422             if (this.after) {
10423                 inputblock.cn.push({
10424                     tag :'span',
10425                     cls : 'input-group-addon input-group-append input-group-text',
10426                     html : this.after
10427                 });
10428             }
10429             
10430         };
10431         
10432       
10433         
10434         var ibwrap = inputblock;
10435         
10436         if(this.multiple){
10437             ibwrap = {
10438                 tag: 'ul',
10439                 cls: 'roo-select2-choices',
10440                 cn:[
10441                     {
10442                         tag: 'li',
10443                         cls: 'roo-select2-search-field',
10444                         cn: [
10445
10446                             inputblock
10447                         ]
10448                     }
10449                 ]
10450             };
10451                 
10452         }
10453         
10454         var combobox = {
10455             cls: 'roo-select2-container input-group',
10456             cn: [
10457                  {
10458                     tag: 'input',
10459                     type : 'hidden',
10460                     cls: 'form-hidden-field'
10461                 },
10462                 ibwrap
10463             ]
10464         };
10465         
10466         if(!this.multiple && this.showToggleBtn){
10467             
10468             var caret = {
10469                         tag: 'span',
10470                         cls: 'caret'
10471              };
10472             if (this.caret != false) {
10473                 caret = {
10474                      tag: 'i',
10475                      cls: 'fa fa-' + this.caret
10476                 };
10477                 
10478             }
10479             
10480             combobox.cn.push({
10481                 tag :'span',
10482                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10483                 cn : [
10484                     caret,
10485                     {
10486                         tag: 'span',
10487                         cls: 'combobox-clear',
10488                         cn  : [
10489                             {
10490                                 tag : 'i',
10491                                 cls: 'icon-remove'
10492                             }
10493                         ]
10494                     }
10495                 ]
10496
10497             })
10498         }
10499         
10500         if(this.multiple){
10501             combobox.cls += ' roo-select2-container-multi';
10502         }
10503          var indicator = {
10504             tag : 'i',
10505             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10506             tooltip : 'This field is required'
10507         };
10508         if (Roo.bootstrap.version == 4) {
10509             indicator = {
10510                 tag : 'i',
10511                 style : 'display:none'
10512             };
10513         }
10514         
10515         
10516         if (align ==='left' && this.fieldLabel.length) {
10517             
10518             cfg.cls += ' roo-form-group-label-left row';
10519
10520             cfg.cn = [
10521                 indicator,
10522                 {
10523                     tag: 'label',
10524                     'for' :  id,
10525                     cls : 'control-label',
10526                     html : this.fieldLabel
10527
10528                 },
10529                 {
10530                     cls : "", 
10531                     cn: [
10532                         combobox
10533                     ]
10534                 }
10535
10536             ];
10537             
10538             var labelCfg = cfg.cn[1];
10539             var contentCfg = cfg.cn[2];
10540             
10541             if(this.indicatorpos == 'right'){
10542                 cfg.cn = [
10543                     {
10544                         tag: 'label',
10545                         'for' :  id,
10546                         cls : 'control-label',
10547                         cn : [
10548                             {
10549                                 tag : 'span',
10550                                 html : this.fieldLabel
10551                             },
10552                             indicator
10553                         ]
10554                     },
10555                     {
10556                         cls : "", 
10557                         cn: [
10558                             combobox
10559                         ]
10560                     }
10561
10562                 ];
10563                 
10564                 labelCfg = cfg.cn[0];
10565                 contentCfg = cfg.cn[1];
10566             }
10567             
10568             if(this.labelWidth > 12){
10569                 labelCfg.style = "width: " + this.labelWidth + 'px';
10570             }
10571             
10572             if(this.labelWidth < 13 && this.labelmd == 0){
10573                 this.labelmd = this.labelWidth;
10574             }
10575             
10576             if(this.labellg > 0){
10577                 labelCfg.cls += ' col-lg-' + this.labellg;
10578                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10579             }
10580             
10581             if(this.labelmd > 0){
10582                 labelCfg.cls += ' col-md-' + this.labelmd;
10583                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10584             }
10585             
10586             if(this.labelsm > 0){
10587                 labelCfg.cls += ' col-sm-' + this.labelsm;
10588                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10589             }
10590             
10591             if(this.labelxs > 0){
10592                 labelCfg.cls += ' col-xs-' + this.labelxs;
10593                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10594             }
10595             
10596         } else if ( this.fieldLabel.length) {
10597 //                Roo.log(" label");
10598             cfg.cn = [
10599                 indicator,
10600                {
10601                    tag: 'label',
10602                    //cls : 'input-group-addon',
10603                    html : this.fieldLabel
10604
10605                },
10606
10607                combobox
10608
10609             ];
10610             
10611             if(this.indicatorpos == 'right'){
10612                 
10613                 cfg.cn = [
10614                     {
10615                        tag: 'label',
10616                        cn : [
10617                            {
10618                                tag : 'span',
10619                                html : this.fieldLabel
10620                            },
10621                            indicator
10622                        ]
10623
10624                     },
10625                     combobox
10626
10627                 ];
10628
10629             }
10630
10631         } else {
10632             
10633 //                Roo.log(" no label && no align");
10634                 cfg = combobox
10635                      
10636                 
10637         }
10638         
10639         var settings=this;
10640         ['xs','sm','md','lg'].map(function(size){
10641             if (settings[size]) {
10642                 cfg.cls += ' col-' + size + '-' + settings[size];
10643             }
10644         });
10645         
10646         return cfg;
10647         
10648     },
10649     
10650     
10651     
10652     // private
10653     onResize : function(w, h){
10654 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10655 //        if(typeof w == 'number'){
10656 //            var x = w - this.trigger.getWidth();
10657 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10658 //            this.trigger.setStyle('left', x+'px');
10659 //        }
10660     },
10661
10662     // private
10663     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10664
10665     // private
10666     getResizeEl : function(){
10667         return this.inputEl();
10668     },
10669
10670     // private
10671     getPositionEl : function(){
10672         return this.inputEl();
10673     },
10674
10675     // private
10676     alignErrorIcon : function(){
10677         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10678     },
10679
10680     // private
10681     initEvents : function(){
10682         
10683         this.createList();
10684         
10685         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10686         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10687         if(!this.multiple && this.showToggleBtn){
10688             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10689             if(this.hideTrigger){
10690                 this.trigger.setDisplayed(false);
10691             }
10692             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10693         }
10694         
10695         if(this.multiple){
10696             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10697         }
10698         
10699         if(this.removable && !this.editable && !this.tickable){
10700             var close = this.closeTriggerEl();
10701             
10702             if(close){
10703                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10704                 close.on('click', this.removeBtnClick, this, close);
10705             }
10706         }
10707         
10708         //this.trigger.addClassOnOver('x-form-trigger-over');
10709         //this.trigger.addClassOnClick('x-form-trigger-click');
10710         
10711         //if(!this.width){
10712         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10713         //}
10714     },
10715     
10716     closeTriggerEl : function()
10717     {
10718         var close = this.el.select('.roo-combo-removable-btn', true).first();
10719         return close ? close : false;
10720     },
10721     
10722     removeBtnClick : function(e, h, el)
10723     {
10724         e.preventDefault();
10725         
10726         if(this.fireEvent("remove", this) !== false){
10727             this.reset();
10728             this.fireEvent("afterremove", this)
10729         }
10730     },
10731     
10732     createList : function()
10733     {
10734         this.list = Roo.get(document.body).createChild({
10735             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10736             cls: 'typeahead typeahead-long dropdown-menu',
10737             style: 'display:none'
10738         });
10739         
10740         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10741         
10742     },
10743
10744     // private
10745     initTrigger : function(){
10746        
10747     },
10748
10749     // private
10750     onDestroy : function(){
10751         if(this.trigger){
10752             this.trigger.removeAllListeners();
10753           //  this.trigger.remove();
10754         }
10755         //if(this.wrap){
10756         //    this.wrap.remove();
10757         //}
10758         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10759     },
10760
10761     // private
10762     onFocus : function(){
10763         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10764         /*
10765         if(!this.mimicing){
10766             this.wrap.addClass('x-trigger-wrap-focus');
10767             this.mimicing = true;
10768             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10769             if(this.monitorTab){
10770                 this.el.on("keydown", this.checkTab, this);
10771             }
10772         }
10773         */
10774     },
10775
10776     // private
10777     checkTab : function(e){
10778         if(e.getKey() == e.TAB){
10779             this.triggerBlur();
10780         }
10781     },
10782
10783     // private
10784     onBlur : function(){
10785         // do nothing
10786     },
10787
10788     // private
10789     mimicBlur : function(e, t){
10790         /*
10791         if(!this.wrap.contains(t) && this.validateBlur()){
10792             this.triggerBlur();
10793         }
10794         */
10795     },
10796
10797     // private
10798     triggerBlur : function(){
10799         this.mimicing = false;
10800         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10801         if(this.monitorTab){
10802             this.el.un("keydown", this.checkTab, this);
10803         }
10804         //this.wrap.removeClass('x-trigger-wrap-focus');
10805         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10806     },
10807
10808     // private
10809     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10810     validateBlur : function(e, t){
10811         return true;
10812     },
10813
10814     // private
10815     onDisable : function(){
10816         this.inputEl().dom.disabled = true;
10817         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10818         //if(this.wrap){
10819         //    this.wrap.addClass('x-item-disabled');
10820         //}
10821     },
10822
10823     // private
10824     onEnable : function(){
10825         this.inputEl().dom.disabled = false;
10826         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10827         //if(this.wrap){
10828         //    this.el.removeClass('x-item-disabled');
10829         //}
10830     },
10831
10832     // private
10833     onShow : function(){
10834         var ae = this.getActionEl();
10835         
10836         if(ae){
10837             ae.dom.style.display = '';
10838             ae.dom.style.visibility = 'visible';
10839         }
10840     },
10841
10842     // private
10843     
10844     onHide : function(){
10845         var ae = this.getActionEl();
10846         ae.dom.style.display = 'none';
10847     },
10848
10849     /**
10850      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10851      * by an implementing function.
10852      * @method
10853      * @param {EventObject} e
10854      */
10855     onTriggerClick : Roo.emptyFn
10856 });
10857  /*
10858  * Based on:
10859  * Ext JS Library 1.1.1
10860  * Copyright(c) 2006-2007, Ext JS, LLC.
10861  *
10862  * Originally Released Under LGPL - original licence link has changed is not relivant.
10863  *
10864  * Fork - LGPL
10865  * <script type="text/javascript">
10866  */
10867
10868
10869 /**
10870  * @class Roo.data.SortTypes
10871  * @singleton
10872  * Defines the default sorting (casting?) comparison functions used when sorting data.
10873  */
10874 Roo.data.SortTypes = {
10875     /**
10876      * Default sort that does nothing
10877      * @param {Mixed} s The value being converted
10878      * @return {Mixed} The comparison value
10879      */
10880     none : function(s){
10881         return s;
10882     },
10883     
10884     /**
10885      * The regular expression used to strip tags
10886      * @type {RegExp}
10887      * @property
10888      */
10889     stripTagsRE : /<\/?[^>]+>/gi,
10890     
10891     /**
10892      * Strips all HTML tags to sort on text only
10893      * @param {Mixed} s The value being converted
10894      * @return {String} The comparison value
10895      */
10896     asText : function(s){
10897         return String(s).replace(this.stripTagsRE, "");
10898     },
10899     
10900     /**
10901      * Strips all HTML tags to sort on text only - Case insensitive
10902      * @param {Mixed} s The value being converted
10903      * @return {String} The comparison value
10904      */
10905     asUCText : function(s){
10906         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10907     },
10908     
10909     /**
10910      * Case insensitive string
10911      * @param {Mixed} s The value being converted
10912      * @return {String} The comparison value
10913      */
10914     asUCString : function(s) {
10915         return String(s).toUpperCase();
10916     },
10917     
10918     /**
10919      * Date sorting
10920      * @param {Mixed} s The value being converted
10921      * @return {Number} The comparison value
10922      */
10923     asDate : function(s) {
10924         if(!s){
10925             return 0;
10926         }
10927         if(s instanceof Date){
10928             return s.getTime();
10929         }
10930         return Date.parse(String(s));
10931     },
10932     
10933     /**
10934      * Float sorting
10935      * @param {Mixed} s The value being converted
10936      * @return {Float} The comparison value
10937      */
10938     asFloat : function(s) {
10939         var val = parseFloat(String(s).replace(/,/g, ""));
10940         if(isNaN(val)) {
10941             val = 0;
10942         }
10943         return val;
10944     },
10945     
10946     /**
10947      * Integer sorting
10948      * @param {Mixed} s The value being converted
10949      * @return {Number} The comparison value
10950      */
10951     asInt : function(s) {
10952         var val = parseInt(String(s).replace(/,/g, ""));
10953         if(isNaN(val)) {
10954             val = 0;
10955         }
10956         return val;
10957     }
10958 };/*
10959  * Based on:
10960  * Ext JS Library 1.1.1
10961  * Copyright(c) 2006-2007, Ext JS, LLC.
10962  *
10963  * Originally Released Under LGPL - original licence link has changed is not relivant.
10964  *
10965  * Fork - LGPL
10966  * <script type="text/javascript">
10967  */
10968
10969 /**
10970 * @class Roo.data.Record
10971  * Instances of this class encapsulate both record <em>definition</em> information, and record
10972  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10973  * to access Records cached in an {@link Roo.data.Store} object.<br>
10974  * <p>
10975  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10976  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10977  * objects.<br>
10978  * <p>
10979  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10980  * @constructor
10981  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10982  * {@link #create}. The parameters are the same.
10983  * @param {Array} data An associative Array of data values keyed by the field name.
10984  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10985  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10986  * not specified an integer id is generated.
10987  */
10988 Roo.data.Record = function(data, id){
10989     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10990     this.data = data;
10991 };
10992
10993 /**
10994  * Generate a constructor for a specific record layout.
10995  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10996  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10997  * Each field definition object may contain the following properties: <ul>
10998  * <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,
10999  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11000  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11001  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11002  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11003  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11004  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11005  * this may be omitted.</p></li>
11006  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11007  * <ul><li>auto (Default, implies no conversion)</li>
11008  * <li>string</li>
11009  * <li>int</li>
11010  * <li>float</li>
11011  * <li>boolean</li>
11012  * <li>date</li></ul></p></li>
11013  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11014  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11015  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11016  * by the Reader into an object that will be stored in the Record. It is passed the
11017  * following parameters:<ul>
11018  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11019  * </ul></p></li>
11020  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11021  * </ul>
11022  * <br>usage:<br><pre><code>
11023 var TopicRecord = Roo.data.Record.create(
11024     {name: 'title', mapping: 'topic_title'},
11025     {name: 'author', mapping: 'username'},
11026     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11027     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11028     {name: 'lastPoster', mapping: 'user2'},
11029     {name: 'excerpt', mapping: 'post_text'}
11030 );
11031
11032 var myNewRecord = new TopicRecord({
11033     title: 'Do my job please',
11034     author: 'noobie',
11035     totalPosts: 1,
11036     lastPost: new Date(),
11037     lastPoster: 'Animal',
11038     excerpt: 'No way dude!'
11039 });
11040 myStore.add(myNewRecord);
11041 </code></pre>
11042  * @method create
11043  * @static
11044  */
11045 Roo.data.Record.create = function(o){
11046     var f = function(){
11047         f.superclass.constructor.apply(this, arguments);
11048     };
11049     Roo.extend(f, Roo.data.Record);
11050     var p = f.prototype;
11051     p.fields = new Roo.util.MixedCollection(false, function(field){
11052         return field.name;
11053     });
11054     for(var i = 0, len = o.length; i < len; i++){
11055         p.fields.add(new Roo.data.Field(o[i]));
11056     }
11057     f.getField = function(name){
11058         return p.fields.get(name);  
11059     };
11060     return f;
11061 };
11062
11063 Roo.data.Record.AUTO_ID = 1000;
11064 Roo.data.Record.EDIT = 'edit';
11065 Roo.data.Record.REJECT = 'reject';
11066 Roo.data.Record.COMMIT = 'commit';
11067
11068 Roo.data.Record.prototype = {
11069     /**
11070      * Readonly flag - true if this record has been modified.
11071      * @type Boolean
11072      */
11073     dirty : false,
11074     editing : false,
11075     error: null,
11076     modified: null,
11077
11078     // private
11079     join : function(store){
11080         this.store = store;
11081     },
11082
11083     /**
11084      * Set the named field to the specified value.
11085      * @param {String} name The name of the field to set.
11086      * @param {Object} value The value to set the field to.
11087      */
11088     set : function(name, value){
11089         if(this.data[name] == value){
11090             return;
11091         }
11092         this.dirty = true;
11093         if(!this.modified){
11094             this.modified = {};
11095         }
11096         if(typeof this.modified[name] == 'undefined'){
11097             this.modified[name] = this.data[name];
11098         }
11099         this.data[name] = value;
11100         if(!this.editing && this.store){
11101             this.store.afterEdit(this);
11102         }       
11103     },
11104
11105     /**
11106      * Get the value of the named field.
11107      * @param {String} name The name of the field to get the value of.
11108      * @return {Object} The value of the field.
11109      */
11110     get : function(name){
11111         return this.data[name]; 
11112     },
11113
11114     // private
11115     beginEdit : function(){
11116         this.editing = true;
11117         this.modified = {}; 
11118     },
11119
11120     // private
11121     cancelEdit : function(){
11122         this.editing = false;
11123         delete this.modified;
11124     },
11125
11126     // private
11127     endEdit : function(){
11128         this.editing = false;
11129         if(this.dirty && this.store){
11130             this.store.afterEdit(this);
11131         }
11132     },
11133
11134     /**
11135      * Usually called by the {@link Roo.data.Store} which owns the Record.
11136      * Rejects all changes made to the Record since either creation, or the last commit operation.
11137      * Modified fields are reverted to their original values.
11138      * <p>
11139      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11140      * of reject operations.
11141      */
11142     reject : function(){
11143         var m = this.modified;
11144         for(var n in m){
11145             if(typeof m[n] != "function"){
11146                 this.data[n] = m[n];
11147             }
11148         }
11149         this.dirty = false;
11150         delete this.modified;
11151         this.editing = false;
11152         if(this.store){
11153             this.store.afterReject(this);
11154         }
11155     },
11156
11157     /**
11158      * Usually called by the {@link Roo.data.Store} which owns the Record.
11159      * Commits all changes made to the Record since either creation, or the last commit operation.
11160      * <p>
11161      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11162      * of commit operations.
11163      */
11164     commit : function(){
11165         this.dirty = false;
11166         delete this.modified;
11167         this.editing = false;
11168         if(this.store){
11169             this.store.afterCommit(this);
11170         }
11171     },
11172
11173     // private
11174     hasError : function(){
11175         return this.error != null;
11176     },
11177
11178     // private
11179     clearError : function(){
11180         this.error = null;
11181     },
11182
11183     /**
11184      * Creates a copy of this record.
11185      * @param {String} id (optional) A new record id if you don't want to use this record's id
11186      * @return {Record}
11187      */
11188     copy : function(newId) {
11189         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11190     }
11191 };/*
11192  * Based on:
11193  * Ext JS Library 1.1.1
11194  * Copyright(c) 2006-2007, Ext JS, LLC.
11195  *
11196  * Originally Released Under LGPL - original licence link has changed is not relivant.
11197  *
11198  * Fork - LGPL
11199  * <script type="text/javascript">
11200  */
11201
11202
11203
11204 /**
11205  * @class Roo.data.Store
11206  * @extends Roo.util.Observable
11207  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11208  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11209  * <p>
11210  * 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
11211  * has no knowledge of the format of the data returned by the Proxy.<br>
11212  * <p>
11213  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11214  * instances from the data object. These records are cached and made available through accessor functions.
11215  * @constructor
11216  * Creates a new Store.
11217  * @param {Object} config A config object containing the objects needed for the Store to access data,
11218  * and read the data into Records.
11219  */
11220 Roo.data.Store = function(config){
11221     this.data = new Roo.util.MixedCollection(false);
11222     this.data.getKey = function(o){
11223         return o.id;
11224     };
11225     this.baseParams = {};
11226     // private
11227     this.paramNames = {
11228         "start" : "start",
11229         "limit" : "limit",
11230         "sort" : "sort",
11231         "dir" : "dir",
11232         "multisort" : "_multisort"
11233     };
11234
11235     if(config && config.data){
11236         this.inlineData = config.data;
11237         delete config.data;
11238     }
11239
11240     Roo.apply(this, config);
11241     
11242     if(this.reader){ // reader passed
11243         this.reader = Roo.factory(this.reader, Roo.data);
11244         this.reader.xmodule = this.xmodule || false;
11245         if(!this.recordType){
11246             this.recordType = this.reader.recordType;
11247         }
11248         if(this.reader.onMetaChange){
11249             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11250         }
11251     }
11252
11253     if(this.recordType){
11254         this.fields = this.recordType.prototype.fields;
11255     }
11256     this.modified = [];
11257
11258     this.addEvents({
11259         /**
11260          * @event datachanged
11261          * Fires when the data cache has changed, and a widget which is using this Store
11262          * as a Record cache should refresh its view.
11263          * @param {Store} this
11264          */
11265         datachanged : true,
11266         /**
11267          * @event metachange
11268          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11269          * @param {Store} this
11270          * @param {Object} meta The JSON metadata
11271          */
11272         metachange : true,
11273         /**
11274          * @event add
11275          * Fires when Records have been added to the Store
11276          * @param {Store} this
11277          * @param {Roo.data.Record[]} records The array of Records added
11278          * @param {Number} index The index at which the record(s) were added
11279          */
11280         add : true,
11281         /**
11282          * @event remove
11283          * Fires when a Record has been removed from the Store
11284          * @param {Store} this
11285          * @param {Roo.data.Record} record The Record that was removed
11286          * @param {Number} index The index at which the record was removed
11287          */
11288         remove : true,
11289         /**
11290          * @event update
11291          * Fires when a Record has been updated
11292          * @param {Store} this
11293          * @param {Roo.data.Record} record The Record that was updated
11294          * @param {String} operation The update operation being performed.  Value may be one of:
11295          * <pre><code>
11296  Roo.data.Record.EDIT
11297  Roo.data.Record.REJECT
11298  Roo.data.Record.COMMIT
11299          * </code></pre>
11300          */
11301         update : true,
11302         /**
11303          * @event clear
11304          * Fires when the data cache has been cleared.
11305          * @param {Store} this
11306          */
11307         clear : true,
11308         /**
11309          * @event beforeload
11310          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11311          * the load action will be canceled.
11312          * @param {Store} this
11313          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11314          */
11315         beforeload : true,
11316         /**
11317          * @event beforeloadadd
11318          * Fires after a new set of Records has been loaded.
11319          * @param {Store} this
11320          * @param {Roo.data.Record[]} records The Records that were loaded
11321          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11322          */
11323         beforeloadadd : true,
11324         /**
11325          * @event load
11326          * Fires after a new set of Records has been loaded, before they are added to the store.
11327          * @param {Store} this
11328          * @param {Roo.data.Record[]} records The Records that were loaded
11329          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11330          * @params {Object} return from reader
11331          */
11332         load : true,
11333         /**
11334          * @event loadexception
11335          * Fires if an exception occurs in the Proxy during loading.
11336          * Called with the signature of the Proxy's "loadexception" event.
11337          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11338          * 
11339          * @param {Proxy} 
11340          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11341          * @param {Object} load options 
11342          * @param {Object} jsonData from your request (normally this contains the Exception)
11343          */
11344         loadexception : true
11345     });
11346     
11347     if(this.proxy){
11348         this.proxy = Roo.factory(this.proxy, Roo.data);
11349         this.proxy.xmodule = this.xmodule || false;
11350         this.relayEvents(this.proxy,  ["loadexception"]);
11351     }
11352     this.sortToggle = {};
11353     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11354
11355     Roo.data.Store.superclass.constructor.call(this);
11356
11357     if(this.inlineData){
11358         this.loadData(this.inlineData);
11359         delete this.inlineData;
11360     }
11361 };
11362
11363 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11364      /**
11365     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11366     * without a remote query - used by combo/forms at present.
11367     */
11368     
11369     /**
11370     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11371     */
11372     /**
11373     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11374     */
11375     /**
11376     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11377     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11378     */
11379     /**
11380     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11381     * on any HTTP request
11382     */
11383     /**
11384     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11385     */
11386     /**
11387     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11388     */
11389     multiSort: false,
11390     /**
11391     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11392     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11393     */
11394     remoteSort : false,
11395
11396     /**
11397     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11398      * loaded or when a record is removed. (defaults to false).
11399     */
11400     pruneModifiedRecords : false,
11401
11402     // private
11403     lastOptions : null,
11404
11405     /**
11406      * Add Records to the Store and fires the add event.
11407      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11408      */
11409     add : function(records){
11410         records = [].concat(records);
11411         for(var i = 0, len = records.length; i < len; i++){
11412             records[i].join(this);
11413         }
11414         var index = this.data.length;
11415         this.data.addAll(records);
11416         this.fireEvent("add", this, records, index);
11417     },
11418
11419     /**
11420      * Remove a Record from the Store and fires the remove event.
11421      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11422      */
11423     remove : function(record){
11424         var index = this.data.indexOf(record);
11425         this.data.removeAt(index);
11426  
11427         if(this.pruneModifiedRecords){
11428             this.modified.remove(record);
11429         }
11430         this.fireEvent("remove", this, record, index);
11431     },
11432
11433     /**
11434      * Remove all Records from the Store and fires the clear event.
11435      */
11436     removeAll : function(){
11437         this.data.clear();
11438         if(this.pruneModifiedRecords){
11439             this.modified = [];
11440         }
11441         this.fireEvent("clear", this);
11442     },
11443
11444     /**
11445      * Inserts Records to the Store at the given index and fires the add event.
11446      * @param {Number} index The start index at which to insert the passed Records.
11447      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11448      */
11449     insert : function(index, records){
11450         records = [].concat(records);
11451         for(var i = 0, len = records.length; i < len; i++){
11452             this.data.insert(index, records[i]);
11453             records[i].join(this);
11454         }
11455         this.fireEvent("add", this, records, index);
11456     },
11457
11458     /**
11459      * Get the index within the cache of the passed Record.
11460      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11461      * @return {Number} The index of the passed Record. Returns -1 if not found.
11462      */
11463     indexOf : function(record){
11464         return this.data.indexOf(record);
11465     },
11466
11467     /**
11468      * Get the index within the cache of the Record with the passed id.
11469      * @param {String} id The id of the Record to find.
11470      * @return {Number} The index of the Record. Returns -1 if not found.
11471      */
11472     indexOfId : function(id){
11473         return this.data.indexOfKey(id);
11474     },
11475
11476     /**
11477      * Get the Record with the specified id.
11478      * @param {String} id The id of the Record to find.
11479      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11480      */
11481     getById : function(id){
11482         return this.data.key(id);
11483     },
11484
11485     /**
11486      * Get the Record at the specified index.
11487      * @param {Number} index The index of the Record to find.
11488      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11489      */
11490     getAt : function(index){
11491         return this.data.itemAt(index);
11492     },
11493
11494     /**
11495      * Returns a range of Records between specified indices.
11496      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11497      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11498      * @return {Roo.data.Record[]} An array of Records
11499      */
11500     getRange : function(start, end){
11501         return this.data.getRange(start, end);
11502     },
11503
11504     // private
11505     storeOptions : function(o){
11506         o = Roo.apply({}, o);
11507         delete o.callback;
11508         delete o.scope;
11509         this.lastOptions = o;
11510     },
11511
11512     /**
11513      * Loads the Record cache from the configured Proxy using the configured Reader.
11514      * <p>
11515      * If using remote paging, then the first load call must specify the <em>start</em>
11516      * and <em>limit</em> properties in the options.params property to establish the initial
11517      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11518      * <p>
11519      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11520      * and this call will return before the new data has been loaded. Perform any post-processing
11521      * in a callback function, or in a "load" event handler.</strong>
11522      * <p>
11523      * @param {Object} options An object containing properties which control loading options:<ul>
11524      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11525      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11526      * passed the following arguments:<ul>
11527      * <li>r : Roo.data.Record[]</li>
11528      * <li>options: Options object from the load call</li>
11529      * <li>success: Boolean success indicator</li></ul></li>
11530      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11531      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11532      * </ul>
11533      */
11534     load : function(options){
11535         options = options || {};
11536         if(this.fireEvent("beforeload", this, options) !== false){
11537             this.storeOptions(options);
11538             var p = Roo.apply(options.params || {}, this.baseParams);
11539             // if meta was not loaded from remote source.. try requesting it.
11540             if (!this.reader.metaFromRemote) {
11541                 p._requestMeta = 1;
11542             }
11543             if(this.sortInfo && this.remoteSort){
11544                 var pn = this.paramNames;
11545                 p[pn["sort"]] = this.sortInfo.field;
11546                 p[pn["dir"]] = this.sortInfo.direction;
11547             }
11548             if (this.multiSort) {
11549                 var pn = this.paramNames;
11550                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11551             }
11552             
11553             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11554         }
11555     },
11556
11557     /**
11558      * Reloads the Record cache from the configured Proxy using the configured Reader and
11559      * the options from the last load operation performed.
11560      * @param {Object} options (optional) An object containing properties which may override the options
11561      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11562      * the most recently used options are reused).
11563      */
11564     reload : function(options){
11565         this.load(Roo.applyIf(options||{}, this.lastOptions));
11566     },
11567
11568     // private
11569     // Called as a callback by the Reader during a load operation.
11570     loadRecords : function(o, options, success){
11571         if(!o || success === false){
11572             if(success !== false){
11573                 this.fireEvent("load", this, [], options, o);
11574             }
11575             if(options.callback){
11576                 options.callback.call(options.scope || this, [], options, false);
11577             }
11578             return;
11579         }
11580         // if data returned failure - throw an exception.
11581         if (o.success === false) {
11582             // show a message if no listener is registered.
11583             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11584                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11585             }
11586             // loadmask wil be hooked into this..
11587             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11588             return;
11589         }
11590         var r = o.records, t = o.totalRecords || r.length;
11591         
11592         this.fireEvent("beforeloadadd", this, r, options, o);
11593         
11594         if(!options || options.add !== true){
11595             if(this.pruneModifiedRecords){
11596                 this.modified = [];
11597             }
11598             for(var i = 0, len = r.length; i < len; i++){
11599                 r[i].join(this);
11600             }
11601             if(this.snapshot){
11602                 this.data = this.snapshot;
11603                 delete this.snapshot;
11604             }
11605             this.data.clear();
11606             this.data.addAll(r);
11607             this.totalLength = t;
11608             this.applySort();
11609             this.fireEvent("datachanged", this);
11610         }else{
11611             this.totalLength = Math.max(t, this.data.length+r.length);
11612             this.add(r);
11613         }
11614         
11615         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11616                 
11617             var e = new Roo.data.Record({});
11618
11619             e.set(this.parent.displayField, this.parent.emptyTitle);
11620             e.set(this.parent.valueField, '');
11621
11622             this.insert(0, e);
11623         }
11624             
11625         this.fireEvent("load", this, r, options, o);
11626         if(options.callback){
11627             options.callback.call(options.scope || this, r, options, true);
11628         }
11629     },
11630
11631
11632     /**
11633      * Loads data from a passed data block. A Reader which understands the format of the data
11634      * must have been configured in the constructor.
11635      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11636      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11637      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11638      */
11639     loadData : function(o, append){
11640         var r = this.reader.readRecords(o);
11641         this.loadRecords(r, {add: append}, true);
11642     },
11643
11644     /**
11645      * Gets the number of cached records.
11646      * <p>
11647      * <em>If using paging, this may not be the total size of the dataset. If the data object
11648      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11649      * the data set size</em>
11650      */
11651     getCount : function(){
11652         return this.data.length || 0;
11653     },
11654
11655     /**
11656      * Gets the total number of records in the dataset as returned by the server.
11657      * <p>
11658      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11659      * the dataset size</em>
11660      */
11661     getTotalCount : function(){
11662         return this.totalLength || 0;
11663     },
11664
11665     /**
11666      * Returns the sort state of the Store as an object with two properties:
11667      * <pre><code>
11668  field {String} The name of the field by which the Records are sorted
11669  direction {String} The sort order, "ASC" or "DESC"
11670      * </code></pre>
11671      */
11672     getSortState : function(){
11673         return this.sortInfo;
11674     },
11675
11676     // private
11677     applySort : function(){
11678         if(this.sortInfo && !this.remoteSort){
11679             var s = this.sortInfo, f = s.field;
11680             var st = this.fields.get(f).sortType;
11681             var fn = function(r1, r2){
11682                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11683                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11684             };
11685             this.data.sort(s.direction, fn);
11686             if(this.snapshot && this.snapshot != this.data){
11687                 this.snapshot.sort(s.direction, fn);
11688             }
11689         }
11690     },
11691
11692     /**
11693      * Sets the default sort column and order to be used by the next load operation.
11694      * @param {String} fieldName The name of the field to sort by.
11695      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11696      */
11697     setDefaultSort : function(field, dir){
11698         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11699     },
11700
11701     /**
11702      * Sort the Records.
11703      * If remote sorting is used, the sort is performed on the server, and the cache is
11704      * reloaded. If local sorting is used, the cache is sorted internally.
11705      * @param {String} fieldName The name of the field to sort by.
11706      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11707      */
11708     sort : function(fieldName, dir){
11709         var f = this.fields.get(fieldName);
11710         if(!dir){
11711             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11712             
11713             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11714                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11715             }else{
11716                 dir = f.sortDir;
11717             }
11718         }
11719         this.sortToggle[f.name] = dir;
11720         this.sortInfo = {field: f.name, direction: dir};
11721         if(!this.remoteSort){
11722             this.applySort();
11723             this.fireEvent("datachanged", this);
11724         }else{
11725             this.load(this.lastOptions);
11726         }
11727     },
11728
11729     /**
11730      * Calls the specified function for each of the Records in the cache.
11731      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11732      * Returning <em>false</em> aborts and exits the iteration.
11733      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11734      */
11735     each : function(fn, scope){
11736         this.data.each(fn, scope);
11737     },
11738
11739     /**
11740      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11741      * (e.g., during paging).
11742      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11743      */
11744     getModifiedRecords : function(){
11745         return this.modified;
11746     },
11747
11748     // private
11749     createFilterFn : function(property, value, anyMatch){
11750         if(!value.exec){ // not a regex
11751             value = String(value);
11752             if(value.length == 0){
11753                 return false;
11754             }
11755             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11756         }
11757         return function(r){
11758             return value.test(r.data[property]);
11759         };
11760     },
11761
11762     /**
11763      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11764      * @param {String} property A field on your records
11765      * @param {Number} start The record index to start at (defaults to 0)
11766      * @param {Number} end The last record index to include (defaults to length - 1)
11767      * @return {Number} The sum
11768      */
11769     sum : function(property, start, end){
11770         var rs = this.data.items, v = 0;
11771         start = start || 0;
11772         end = (end || end === 0) ? end : rs.length-1;
11773
11774         for(var i = start; i <= end; i++){
11775             v += (rs[i].data[property] || 0);
11776         }
11777         return v;
11778     },
11779
11780     /**
11781      * Filter the records by a specified property.
11782      * @param {String} field A field on your records
11783      * @param {String/RegExp} value Either a string that the field
11784      * should start with or a RegExp to test against the field
11785      * @param {Boolean} anyMatch True to match any part not just the beginning
11786      */
11787     filter : function(property, value, anyMatch){
11788         var fn = this.createFilterFn(property, value, anyMatch);
11789         return fn ? this.filterBy(fn) : this.clearFilter();
11790     },
11791
11792     /**
11793      * Filter by a function. The specified function will be called with each
11794      * record in this data source. If the function returns true the record is included,
11795      * otherwise it is filtered.
11796      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11797      * @param {Object} scope (optional) The scope of the function (defaults to this)
11798      */
11799     filterBy : function(fn, scope){
11800         this.snapshot = this.snapshot || this.data;
11801         this.data = this.queryBy(fn, scope||this);
11802         this.fireEvent("datachanged", this);
11803     },
11804
11805     /**
11806      * Query the records by a specified property.
11807      * @param {String} field A field on your records
11808      * @param {String/RegExp} value Either a string that the field
11809      * should start with or a RegExp to test against the field
11810      * @param {Boolean} anyMatch True to match any part not just the beginning
11811      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11812      */
11813     query : function(property, value, anyMatch){
11814         var fn = this.createFilterFn(property, value, anyMatch);
11815         return fn ? this.queryBy(fn) : this.data.clone();
11816     },
11817
11818     /**
11819      * Query by a function. The specified function will be called with each
11820      * record in this data source. If the function returns true the record is included
11821      * in the results.
11822      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11823      * @param {Object} scope (optional) The scope of the function (defaults to this)
11824       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11825      **/
11826     queryBy : function(fn, scope){
11827         var data = this.snapshot || this.data;
11828         return data.filterBy(fn, scope||this);
11829     },
11830
11831     /**
11832      * Collects unique values for a particular dataIndex from this store.
11833      * @param {String} dataIndex The property to collect
11834      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11835      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11836      * @return {Array} An array of the unique values
11837      **/
11838     collect : function(dataIndex, allowNull, bypassFilter){
11839         var d = (bypassFilter === true && this.snapshot) ?
11840                 this.snapshot.items : this.data.items;
11841         var v, sv, r = [], l = {};
11842         for(var i = 0, len = d.length; i < len; i++){
11843             v = d[i].data[dataIndex];
11844             sv = String(v);
11845             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11846                 l[sv] = true;
11847                 r[r.length] = v;
11848             }
11849         }
11850         return r;
11851     },
11852
11853     /**
11854      * Revert to a view of the Record cache with no filtering applied.
11855      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11856      */
11857     clearFilter : function(suppressEvent){
11858         if(this.snapshot && this.snapshot != this.data){
11859             this.data = this.snapshot;
11860             delete this.snapshot;
11861             if(suppressEvent !== true){
11862                 this.fireEvent("datachanged", this);
11863             }
11864         }
11865     },
11866
11867     // private
11868     afterEdit : function(record){
11869         if(this.modified.indexOf(record) == -1){
11870             this.modified.push(record);
11871         }
11872         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11873     },
11874     
11875     // private
11876     afterReject : function(record){
11877         this.modified.remove(record);
11878         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11879     },
11880
11881     // private
11882     afterCommit : function(record){
11883         this.modified.remove(record);
11884         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11885     },
11886
11887     /**
11888      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11889      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11890      */
11891     commitChanges : function(){
11892         var m = this.modified.slice(0);
11893         this.modified = [];
11894         for(var i = 0, len = m.length; i < len; i++){
11895             m[i].commit();
11896         }
11897     },
11898
11899     /**
11900      * Cancel outstanding changes on all changed records.
11901      */
11902     rejectChanges : function(){
11903         var m = this.modified.slice(0);
11904         this.modified = [];
11905         for(var i = 0, len = m.length; i < len; i++){
11906             m[i].reject();
11907         }
11908     },
11909
11910     onMetaChange : function(meta, rtype, o){
11911         this.recordType = rtype;
11912         this.fields = rtype.prototype.fields;
11913         delete this.snapshot;
11914         this.sortInfo = meta.sortInfo || this.sortInfo;
11915         this.modified = [];
11916         this.fireEvent('metachange', this, this.reader.meta);
11917     },
11918     
11919     moveIndex : function(data, type)
11920     {
11921         var index = this.indexOf(data);
11922         
11923         var newIndex = index + type;
11924         
11925         this.remove(data);
11926         
11927         this.insert(newIndex, data);
11928         
11929     }
11930 });/*
11931  * Based on:
11932  * Ext JS Library 1.1.1
11933  * Copyright(c) 2006-2007, Ext JS, LLC.
11934  *
11935  * Originally Released Under LGPL - original licence link has changed is not relivant.
11936  *
11937  * Fork - LGPL
11938  * <script type="text/javascript">
11939  */
11940
11941 /**
11942  * @class Roo.data.SimpleStore
11943  * @extends Roo.data.Store
11944  * Small helper class to make creating Stores from Array data easier.
11945  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11946  * @cfg {Array} fields An array of field definition objects, or field name strings.
11947  * @cfg {Array} data The multi-dimensional array of data
11948  * @constructor
11949  * @param {Object} config
11950  */
11951 Roo.data.SimpleStore = function(config){
11952     Roo.data.SimpleStore.superclass.constructor.call(this, {
11953         isLocal : true,
11954         reader: new Roo.data.ArrayReader({
11955                 id: config.id
11956             },
11957             Roo.data.Record.create(config.fields)
11958         ),
11959         proxy : new Roo.data.MemoryProxy(config.data)
11960     });
11961     this.load();
11962 };
11963 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11964  * Based on:
11965  * Ext JS Library 1.1.1
11966  * Copyright(c) 2006-2007, Ext JS, LLC.
11967  *
11968  * Originally Released Under LGPL - original licence link has changed is not relivant.
11969  *
11970  * Fork - LGPL
11971  * <script type="text/javascript">
11972  */
11973
11974 /**
11975 /**
11976  * @extends Roo.data.Store
11977  * @class Roo.data.JsonStore
11978  * Small helper class to make creating Stores for JSON data easier. <br/>
11979 <pre><code>
11980 var store = new Roo.data.JsonStore({
11981     url: 'get-images.php',
11982     root: 'images',
11983     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11984 });
11985 </code></pre>
11986  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11987  * JsonReader and HttpProxy (unless inline data is provided).</b>
11988  * @cfg {Array} fields An array of field definition objects, or field name strings.
11989  * @constructor
11990  * @param {Object} config
11991  */
11992 Roo.data.JsonStore = function(c){
11993     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11994         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11995         reader: new Roo.data.JsonReader(c, c.fields)
11996     }));
11997 };
11998 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11999  * Based on:
12000  * Ext JS Library 1.1.1
12001  * Copyright(c) 2006-2007, Ext JS, LLC.
12002  *
12003  * Originally Released Under LGPL - original licence link has changed is not relivant.
12004  *
12005  * Fork - LGPL
12006  * <script type="text/javascript">
12007  */
12008
12009  
12010 Roo.data.Field = function(config){
12011     if(typeof config == "string"){
12012         config = {name: config};
12013     }
12014     Roo.apply(this, config);
12015     
12016     if(!this.type){
12017         this.type = "auto";
12018     }
12019     
12020     var st = Roo.data.SortTypes;
12021     // named sortTypes are supported, here we look them up
12022     if(typeof this.sortType == "string"){
12023         this.sortType = st[this.sortType];
12024     }
12025     
12026     // set default sortType for strings and dates
12027     if(!this.sortType){
12028         switch(this.type){
12029             case "string":
12030                 this.sortType = st.asUCString;
12031                 break;
12032             case "date":
12033                 this.sortType = st.asDate;
12034                 break;
12035             default:
12036                 this.sortType = st.none;
12037         }
12038     }
12039
12040     // define once
12041     var stripRe = /[\$,%]/g;
12042
12043     // prebuilt conversion function for this field, instead of
12044     // switching every time we're reading a value
12045     if(!this.convert){
12046         var cv, dateFormat = this.dateFormat;
12047         switch(this.type){
12048             case "":
12049             case "auto":
12050             case undefined:
12051                 cv = function(v){ return v; };
12052                 break;
12053             case "string":
12054                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12055                 break;
12056             case "int":
12057                 cv = function(v){
12058                     return v !== undefined && v !== null && v !== '' ?
12059                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12060                     };
12061                 break;
12062             case "float":
12063                 cv = function(v){
12064                     return v !== undefined && v !== null && v !== '' ?
12065                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12066                     };
12067                 break;
12068             case "bool":
12069             case "boolean":
12070                 cv = function(v){ return v === true || v === "true" || v == 1; };
12071                 break;
12072             case "date":
12073                 cv = function(v){
12074                     if(!v){
12075                         return '';
12076                     }
12077                     if(v instanceof Date){
12078                         return v;
12079                     }
12080                     if(dateFormat){
12081                         if(dateFormat == "timestamp"){
12082                             return new Date(v*1000);
12083                         }
12084                         return Date.parseDate(v, dateFormat);
12085                     }
12086                     var parsed = Date.parse(v);
12087                     return parsed ? new Date(parsed) : null;
12088                 };
12089              break;
12090             
12091         }
12092         this.convert = cv;
12093     }
12094 };
12095
12096 Roo.data.Field.prototype = {
12097     dateFormat: null,
12098     defaultValue: "",
12099     mapping: null,
12100     sortType : null,
12101     sortDir : "ASC"
12102 };/*
12103  * Based on:
12104  * Ext JS Library 1.1.1
12105  * Copyright(c) 2006-2007, Ext JS, LLC.
12106  *
12107  * Originally Released Under LGPL - original licence link has changed is not relivant.
12108  *
12109  * Fork - LGPL
12110  * <script type="text/javascript">
12111  */
12112  
12113 // Base class for reading structured data from a data source.  This class is intended to be
12114 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12115
12116 /**
12117  * @class Roo.data.DataReader
12118  * Base class for reading structured data from a data source.  This class is intended to be
12119  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12120  */
12121
12122 Roo.data.DataReader = function(meta, recordType){
12123     
12124     this.meta = meta;
12125     
12126     this.recordType = recordType instanceof Array ? 
12127         Roo.data.Record.create(recordType) : recordType;
12128 };
12129
12130 Roo.data.DataReader.prototype = {
12131      /**
12132      * Create an empty record
12133      * @param {Object} data (optional) - overlay some values
12134      * @return {Roo.data.Record} record created.
12135      */
12136     newRow :  function(d) {
12137         var da =  {};
12138         this.recordType.prototype.fields.each(function(c) {
12139             switch( c.type) {
12140                 case 'int' : da[c.name] = 0; break;
12141                 case 'date' : da[c.name] = new Date(); break;
12142                 case 'float' : da[c.name] = 0.0; break;
12143                 case 'boolean' : da[c.name] = false; break;
12144                 default : da[c.name] = ""; break;
12145             }
12146             
12147         });
12148         return new this.recordType(Roo.apply(da, d));
12149     }
12150     
12151 };/*
12152  * Based on:
12153  * Ext JS Library 1.1.1
12154  * Copyright(c) 2006-2007, Ext JS, LLC.
12155  *
12156  * Originally Released Under LGPL - original licence link has changed is not relivant.
12157  *
12158  * Fork - LGPL
12159  * <script type="text/javascript">
12160  */
12161
12162 /**
12163  * @class Roo.data.DataProxy
12164  * @extends Roo.data.Observable
12165  * This class is an abstract base class for implementations which provide retrieval of
12166  * unformatted data objects.<br>
12167  * <p>
12168  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12169  * (of the appropriate type which knows how to parse the data object) to provide a block of
12170  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12171  * <p>
12172  * Custom implementations must implement the load method as described in
12173  * {@link Roo.data.HttpProxy#load}.
12174  */
12175 Roo.data.DataProxy = function(){
12176     this.addEvents({
12177         /**
12178          * @event beforeload
12179          * Fires before a network request is made to retrieve a data object.
12180          * @param {Object} This DataProxy object.
12181          * @param {Object} params The params parameter to the load function.
12182          */
12183         beforeload : true,
12184         /**
12185          * @event load
12186          * Fires before the load method's callback is called.
12187          * @param {Object} This DataProxy object.
12188          * @param {Object} o The data object.
12189          * @param {Object} arg The callback argument object passed to the load function.
12190          */
12191         load : true,
12192         /**
12193          * @event loadexception
12194          * Fires if an Exception occurs during data retrieval.
12195          * @param {Object} This DataProxy object.
12196          * @param {Object} o The data object.
12197          * @param {Object} arg The callback argument object passed to the load function.
12198          * @param {Object} e The Exception.
12199          */
12200         loadexception : true
12201     });
12202     Roo.data.DataProxy.superclass.constructor.call(this);
12203 };
12204
12205 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12206
12207     /**
12208      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12209      */
12210 /*
12211  * Based on:
12212  * Ext JS Library 1.1.1
12213  * Copyright(c) 2006-2007, Ext JS, LLC.
12214  *
12215  * Originally Released Under LGPL - original licence link has changed is not relivant.
12216  *
12217  * Fork - LGPL
12218  * <script type="text/javascript">
12219  */
12220 /**
12221  * @class Roo.data.MemoryProxy
12222  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12223  * to the Reader when its load method is called.
12224  * @constructor
12225  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12226  */
12227 Roo.data.MemoryProxy = function(data){
12228     if (data.data) {
12229         data = data.data;
12230     }
12231     Roo.data.MemoryProxy.superclass.constructor.call(this);
12232     this.data = data;
12233 };
12234
12235 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12236     
12237     /**
12238      * Load data from the requested source (in this case an in-memory
12239      * data object passed to the constructor), read the data object into
12240      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12241      * process that block using the passed callback.
12242      * @param {Object} params This parameter is not used by the MemoryProxy class.
12243      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12244      * object into a block of Roo.data.Records.
12245      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12246      * The function must be passed <ul>
12247      * <li>The Record block object</li>
12248      * <li>The "arg" argument from the load function</li>
12249      * <li>A boolean success indicator</li>
12250      * </ul>
12251      * @param {Object} scope The scope in which to call the callback
12252      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12253      */
12254     load : function(params, reader, callback, scope, arg){
12255         params = params || {};
12256         var result;
12257         try {
12258             result = reader.readRecords(this.data);
12259         }catch(e){
12260             this.fireEvent("loadexception", this, arg, null, e);
12261             callback.call(scope, null, arg, false);
12262             return;
12263         }
12264         callback.call(scope, result, arg, true);
12265     },
12266     
12267     // private
12268     update : function(params, records){
12269         
12270     }
12271 });/*
12272  * Based on:
12273  * Ext JS Library 1.1.1
12274  * Copyright(c) 2006-2007, Ext JS, LLC.
12275  *
12276  * Originally Released Under LGPL - original licence link has changed is not relivant.
12277  *
12278  * Fork - LGPL
12279  * <script type="text/javascript">
12280  */
12281 /**
12282  * @class Roo.data.HttpProxy
12283  * @extends Roo.data.DataProxy
12284  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12285  * configured to reference a certain URL.<br><br>
12286  * <p>
12287  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12288  * from which the running page was served.<br><br>
12289  * <p>
12290  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12291  * <p>
12292  * Be aware that to enable the browser to parse an XML document, the server must set
12293  * the Content-Type header in the HTTP response to "text/xml".
12294  * @constructor
12295  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12296  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12297  * will be used to make the request.
12298  */
12299 Roo.data.HttpProxy = function(conn){
12300     Roo.data.HttpProxy.superclass.constructor.call(this);
12301     // is conn a conn config or a real conn?
12302     this.conn = conn;
12303     this.useAjax = !conn || !conn.events;
12304   
12305 };
12306
12307 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12308     // thse are take from connection...
12309     
12310     /**
12311      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12312      */
12313     /**
12314      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12315      * extra parameters to each request made by this object. (defaults to undefined)
12316      */
12317     /**
12318      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12319      *  to each request made by this object. (defaults to undefined)
12320      */
12321     /**
12322      * @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)
12323      */
12324     /**
12325      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12326      */
12327      /**
12328      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12329      * @type Boolean
12330      */
12331   
12332
12333     /**
12334      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12335      * @type Boolean
12336      */
12337     /**
12338      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12339      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12340      * a finer-grained basis than the DataProxy events.
12341      */
12342     getConnection : function(){
12343         return this.useAjax ? Roo.Ajax : this.conn;
12344     },
12345
12346     /**
12347      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12348      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12349      * process that block using the passed callback.
12350      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12351      * for the request to the remote server.
12352      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12353      * object into a block of Roo.data.Records.
12354      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12355      * The function must be passed <ul>
12356      * <li>The Record block object</li>
12357      * <li>The "arg" argument from the load function</li>
12358      * <li>A boolean success indicator</li>
12359      * </ul>
12360      * @param {Object} scope The scope in which to call the callback
12361      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12362      */
12363     load : function(params, reader, callback, scope, arg){
12364         if(this.fireEvent("beforeload", this, params) !== false){
12365             var  o = {
12366                 params : params || {},
12367                 request: {
12368                     callback : callback,
12369                     scope : scope,
12370                     arg : arg
12371                 },
12372                 reader: reader,
12373                 callback : this.loadResponse,
12374                 scope: this
12375             };
12376             if(this.useAjax){
12377                 Roo.applyIf(o, this.conn);
12378                 if(this.activeRequest){
12379                     Roo.Ajax.abort(this.activeRequest);
12380                 }
12381                 this.activeRequest = Roo.Ajax.request(o);
12382             }else{
12383                 this.conn.request(o);
12384             }
12385         }else{
12386             callback.call(scope||this, null, arg, false);
12387         }
12388     },
12389
12390     // private
12391     loadResponse : function(o, success, response){
12392         delete this.activeRequest;
12393         if(!success){
12394             this.fireEvent("loadexception", this, o, response);
12395             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12396             return;
12397         }
12398         var result;
12399         try {
12400             result = o.reader.read(response);
12401         }catch(e){
12402             this.fireEvent("loadexception", this, o, response, e);
12403             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12404             return;
12405         }
12406         
12407         this.fireEvent("load", this, o, o.request.arg);
12408         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12409     },
12410
12411     // private
12412     update : function(dataSet){
12413
12414     },
12415
12416     // private
12417     updateResponse : function(dataSet){
12418
12419     }
12420 });/*
12421  * Based on:
12422  * Ext JS Library 1.1.1
12423  * Copyright(c) 2006-2007, Ext JS, LLC.
12424  *
12425  * Originally Released Under LGPL - original licence link has changed is not relivant.
12426  *
12427  * Fork - LGPL
12428  * <script type="text/javascript">
12429  */
12430
12431 /**
12432  * @class Roo.data.ScriptTagProxy
12433  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12434  * other than the originating domain of the running page.<br><br>
12435  * <p>
12436  * <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
12437  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12438  * <p>
12439  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12440  * source code that is used as the source inside a &lt;script> tag.<br><br>
12441  * <p>
12442  * In order for the browser to process the returned data, the server must wrap the data object
12443  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12444  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12445  * depending on whether the callback name was passed:
12446  * <p>
12447  * <pre><code>
12448 boolean scriptTag = false;
12449 String cb = request.getParameter("callback");
12450 if (cb != null) {
12451     scriptTag = true;
12452     response.setContentType("text/javascript");
12453 } else {
12454     response.setContentType("application/x-json");
12455 }
12456 Writer out = response.getWriter();
12457 if (scriptTag) {
12458     out.write(cb + "(");
12459 }
12460 out.print(dataBlock.toJsonString());
12461 if (scriptTag) {
12462     out.write(");");
12463 }
12464 </pre></code>
12465  *
12466  * @constructor
12467  * @param {Object} config A configuration object.
12468  */
12469 Roo.data.ScriptTagProxy = function(config){
12470     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12471     Roo.apply(this, config);
12472     this.head = document.getElementsByTagName("head")[0];
12473 };
12474
12475 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12476
12477 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12478     /**
12479      * @cfg {String} url The URL from which to request the data object.
12480      */
12481     /**
12482      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12483      */
12484     timeout : 30000,
12485     /**
12486      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12487      * the server the name of the callback function set up by the load call to process the returned data object.
12488      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12489      * javascript output which calls this named function passing the data object as its only parameter.
12490      */
12491     callbackParam : "callback",
12492     /**
12493      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12494      * name to the request.
12495      */
12496     nocache : true,
12497
12498     /**
12499      * Load data from the configured URL, read the data object into
12500      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12501      * process that block using the passed callback.
12502      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12503      * for the request to the remote server.
12504      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12505      * object into a block of Roo.data.Records.
12506      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12507      * The function must be passed <ul>
12508      * <li>The Record block object</li>
12509      * <li>The "arg" argument from the load function</li>
12510      * <li>A boolean success indicator</li>
12511      * </ul>
12512      * @param {Object} scope The scope in which to call the callback
12513      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12514      */
12515     load : function(params, reader, callback, scope, arg){
12516         if(this.fireEvent("beforeload", this, params) !== false){
12517
12518             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12519
12520             var url = this.url;
12521             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12522             if(this.nocache){
12523                 url += "&_dc=" + (new Date().getTime());
12524             }
12525             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12526             var trans = {
12527                 id : transId,
12528                 cb : "stcCallback"+transId,
12529                 scriptId : "stcScript"+transId,
12530                 params : params,
12531                 arg : arg,
12532                 url : url,
12533                 callback : callback,
12534                 scope : scope,
12535                 reader : reader
12536             };
12537             var conn = this;
12538
12539             window[trans.cb] = function(o){
12540                 conn.handleResponse(o, trans);
12541             };
12542
12543             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12544
12545             if(this.autoAbort !== false){
12546                 this.abort();
12547             }
12548
12549             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12550
12551             var script = document.createElement("script");
12552             script.setAttribute("src", url);
12553             script.setAttribute("type", "text/javascript");
12554             script.setAttribute("id", trans.scriptId);
12555             this.head.appendChild(script);
12556
12557             this.trans = trans;
12558         }else{
12559             callback.call(scope||this, null, arg, false);
12560         }
12561     },
12562
12563     // private
12564     isLoading : function(){
12565         return this.trans ? true : false;
12566     },
12567
12568     /**
12569      * Abort the current server request.
12570      */
12571     abort : function(){
12572         if(this.isLoading()){
12573             this.destroyTrans(this.trans);
12574         }
12575     },
12576
12577     // private
12578     destroyTrans : function(trans, isLoaded){
12579         this.head.removeChild(document.getElementById(trans.scriptId));
12580         clearTimeout(trans.timeoutId);
12581         if(isLoaded){
12582             window[trans.cb] = undefined;
12583             try{
12584                 delete window[trans.cb];
12585             }catch(e){}
12586         }else{
12587             // if hasn't been loaded, wait for load to remove it to prevent script error
12588             window[trans.cb] = function(){
12589                 window[trans.cb] = undefined;
12590                 try{
12591                     delete window[trans.cb];
12592                 }catch(e){}
12593             };
12594         }
12595     },
12596
12597     // private
12598     handleResponse : function(o, trans){
12599         this.trans = false;
12600         this.destroyTrans(trans, true);
12601         var result;
12602         try {
12603             result = trans.reader.readRecords(o);
12604         }catch(e){
12605             this.fireEvent("loadexception", this, o, trans.arg, e);
12606             trans.callback.call(trans.scope||window, null, trans.arg, false);
12607             return;
12608         }
12609         this.fireEvent("load", this, o, trans.arg);
12610         trans.callback.call(trans.scope||window, result, trans.arg, true);
12611     },
12612
12613     // private
12614     handleFailure : function(trans){
12615         this.trans = false;
12616         this.destroyTrans(trans, false);
12617         this.fireEvent("loadexception", this, null, trans.arg);
12618         trans.callback.call(trans.scope||window, null, trans.arg, false);
12619     }
12620 });/*
12621  * Based on:
12622  * Ext JS Library 1.1.1
12623  * Copyright(c) 2006-2007, Ext JS, LLC.
12624  *
12625  * Originally Released Under LGPL - original licence link has changed is not relivant.
12626  *
12627  * Fork - LGPL
12628  * <script type="text/javascript">
12629  */
12630
12631 /**
12632  * @class Roo.data.JsonReader
12633  * @extends Roo.data.DataReader
12634  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12635  * based on mappings in a provided Roo.data.Record constructor.
12636  * 
12637  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12638  * in the reply previously. 
12639  * 
12640  * <p>
12641  * Example code:
12642  * <pre><code>
12643 var RecordDef = Roo.data.Record.create([
12644     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12645     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12646 ]);
12647 var myReader = new Roo.data.JsonReader({
12648     totalProperty: "results",    // The property which contains the total dataset size (optional)
12649     root: "rows",                // The property which contains an Array of row objects
12650     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12651 }, RecordDef);
12652 </code></pre>
12653  * <p>
12654  * This would consume a JSON file like this:
12655  * <pre><code>
12656 { 'results': 2, 'rows': [
12657     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12658     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12659 }
12660 </code></pre>
12661  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12662  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12663  * paged from the remote server.
12664  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12665  * @cfg {String} root name of the property which contains the Array of row objects.
12666  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12667  * @cfg {Array} fields Array of field definition objects
12668  * @constructor
12669  * Create a new JsonReader
12670  * @param {Object} meta Metadata configuration options
12671  * @param {Object} recordType Either an Array of field definition objects,
12672  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12673  */
12674 Roo.data.JsonReader = function(meta, recordType){
12675     
12676     meta = meta || {};
12677     // set some defaults:
12678     Roo.applyIf(meta, {
12679         totalProperty: 'total',
12680         successProperty : 'success',
12681         root : 'data',
12682         id : 'id'
12683     });
12684     
12685     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12686 };
12687 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12688     
12689     /**
12690      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12691      * Used by Store query builder to append _requestMeta to params.
12692      * 
12693      */
12694     metaFromRemote : false,
12695     /**
12696      * This method is only used by a DataProxy which has retrieved data from a remote server.
12697      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12698      * @return {Object} data A data block which is used by an Roo.data.Store object as
12699      * a cache of Roo.data.Records.
12700      */
12701     read : function(response){
12702         var json = response.responseText;
12703        
12704         var o = /* eval:var:o */ eval("("+json+")");
12705         if(!o) {
12706             throw {message: "JsonReader.read: Json object not found"};
12707         }
12708         
12709         if(o.metaData){
12710             
12711             delete this.ef;
12712             this.metaFromRemote = true;
12713             this.meta = o.metaData;
12714             this.recordType = Roo.data.Record.create(o.metaData.fields);
12715             this.onMetaChange(this.meta, this.recordType, o);
12716         }
12717         return this.readRecords(o);
12718     },
12719
12720     // private function a store will implement
12721     onMetaChange : function(meta, recordType, o){
12722
12723     },
12724
12725     /**
12726          * @ignore
12727          */
12728     simpleAccess: function(obj, subsc) {
12729         return obj[subsc];
12730     },
12731
12732         /**
12733          * @ignore
12734          */
12735     getJsonAccessor: function(){
12736         var re = /[\[\.]/;
12737         return function(expr) {
12738             try {
12739                 return(re.test(expr))
12740                     ? new Function("obj", "return obj." + expr)
12741                     : function(obj){
12742                         return obj[expr];
12743                     };
12744             } catch(e){}
12745             return Roo.emptyFn;
12746         };
12747     }(),
12748
12749     /**
12750      * Create a data block containing Roo.data.Records from an XML document.
12751      * @param {Object} o An object which contains an Array of row objects in the property specified
12752      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12753      * which contains the total size of the dataset.
12754      * @return {Object} data A data block which is used by an Roo.data.Store object as
12755      * a cache of Roo.data.Records.
12756      */
12757     readRecords : function(o){
12758         /**
12759          * After any data loads, the raw JSON data is available for further custom processing.
12760          * @type Object
12761          */
12762         this.o = o;
12763         var s = this.meta, Record = this.recordType,
12764             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12765
12766 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12767         if (!this.ef) {
12768             if(s.totalProperty) {
12769                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12770                 }
12771                 if(s.successProperty) {
12772                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12773                 }
12774                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12775                 if (s.id) {
12776                         var g = this.getJsonAccessor(s.id);
12777                         this.getId = function(rec) {
12778                                 var r = g(rec);  
12779                                 return (r === undefined || r === "") ? null : r;
12780                         };
12781                 } else {
12782                         this.getId = function(){return null;};
12783                 }
12784             this.ef = [];
12785             for(var jj = 0; jj < fl; jj++){
12786                 f = fi[jj];
12787                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12788                 this.ef[jj] = this.getJsonAccessor(map);
12789             }
12790         }
12791
12792         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12793         if(s.totalProperty){
12794             var vt = parseInt(this.getTotal(o), 10);
12795             if(!isNaN(vt)){
12796                 totalRecords = vt;
12797             }
12798         }
12799         if(s.successProperty){
12800             var vs = this.getSuccess(o);
12801             if(vs === false || vs === 'false'){
12802                 success = false;
12803             }
12804         }
12805         var records = [];
12806         for(var i = 0; i < c; i++){
12807                 var n = root[i];
12808             var values = {};
12809             var id = this.getId(n);
12810             for(var j = 0; j < fl; j++){
12811                 f = fi[j];
12812             var v = this.ef[j](n);
12813             if (!f.convert) {
12814                 Roo.log('missing convert for ' + f.name);
12815                 Roo.log(f);
12816                 continue;
12817             }
12818             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12819             }
12820             var record = new Record(values, id);
12821             record.json = n;
12822             records[i] = record;
12823         }
12824         return {
12825             raw : o,
12826             success : success,
12827             records : records,
12828             totalRecords : totalRecords
12829         };
12830     }
12831 });/*
12832  * Based on:
12833  * Ext JS Library 1.1.1
12834  * Copyright(c) 2006-2007, Ext JS, LLC.
12835  *
12836  * Originally Released Under LGPL - original licence link has changed is not relivant.
12837  *
12838  * Fork - LGPL
12839  * <script type="text/javascript">
12840  */
12841
12842 /**
12843  * @class Roo.data.ArrayReader
12844  * @extends Roo.data.DataReader
12845  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12846  * Each element of that Array represents a row of data fields. The
12847  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12848  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12849  * <p>
12850  * Example code:.
12851  * <pre><code>
12852 var RecordDef = Roo.data.Record.create([
12853     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12854     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12855 ]);
12856 var myReader = new Roo.data.ArrayReader({
12857     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12858 }, RecordDef);
12859 </code></pre>
12860  * <p>
12861  * This would consume an Array like this:
12862  * <pre><code>
12863 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12864   </code></pre>
12865  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12866  * @constructor
12867  * Create a new JsonReader
12868  * @param {Object} meta Metadata configuration options.
12869  * @param {Object} recordType Either an Array of field definition objects
12870  * as specified to {@link Roo.data.Record#create},
12871  * or an {@link Roo.data.Record} object
12872  * created using {@link Roo.data.Record#create}.
12873  */
12874 Roo.data.ArrayReader = function(meta, recordType){
12875     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12876 };
12877
12878 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12879     /**
12880      * Create a data block containing Roo.data.Records from an XML document.
12881      * @param {Object} o An Array of row objects which represents the dataset.
12882      * @return {Object} data A data block which is used by an Roo.data.Store object as
12883      * a cache of Roo.data.Records.
12884      */
12885     readRecords : function(o){
12886         var sid = this.meta ? this.meta.id : null;
12887         var recordType = this.recordType, fields = recordType.prototype.fields;
12888         var records = [];
12889         var root = o;
12890             for(var i = 0; i < root.length; i++){
12891                     var n = root[i];
12892                 var values = {};
12893                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12894                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12895                 var f = fields.items[j];
12896                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12897                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12898                 v = f.convert(v);
12899                 values[f.name] = v;
12900             }
12901                 var record = new recordType(values, id);
12902                 record.json = n;
12903                 records[records.length] = record;
12904             }
12905             return {
12906                 records : records,
12907                 totalRecords : records.length
12908             };
12909     }
12910 });/*
12911  * - LGPL
12912  * * 
12913  */
12914
12915 /**
12916  * @class Roo.bootstrap.ComboBox
12917  * @extends Roo.bootstrap.TriggerField
12918  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12919  * @cfg {Boolean} append (true|false) default false
12920  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12921  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12922  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12923  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12924  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12925  * @cfg {Boolean} animate default true
12926  * @cfg {Boolean} emptyResultText only for touch device
12927  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12928  * @cfg {String} emptyTitle default ''
12929  * @constructor
12930  * Create a new ComboBox.
12931  * @param {Object} config Configuration options
12932  */
12933 Roo.bootstrap.ComboBox = function(config){
12934     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12935     this.addEvents({
12936         /**
12937          * @event expand
12938          * Fires when the dropdown list is expanded
12939         * @param {Roo.bootstrap.ComboBox} combo This combo box
12940         */
12941         'expand' : true,
12942         /**
12943          * @event collapse
12944          * Fires when the dropdown list is collapsed
12945         * @param {Roo.bootstrap.ComboBox} combo This combo box
12946         */
12947         'collapse' : true,
12948         /**
12949          * @event beforeselect
12950          * Fires before a list item is selected. Return false to cancel the selection.
12951         * @param {Roo.bootstrap.ComboBox} combo This combo box
12952         * @param {Roo.data.Record} record The data record returned from the underlying store
12953         * @param {Number} index The index of the selected item in the dropdown list
12954         */
12955         'beforeselect' : true,
12956         /**
12957          * @event select
12958          * Fires when a list item is selected
12959         * @param {Roo.bootstrap.ComboBox} combo This combo box
12960         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12961         * @param {Number} index The index of the selected item in the dropdown list
12962         */
12963         'select' : true,
12964         /**
12965          * @event beforequery
12966          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12967          * The event object passed has these properties:
12968         * @param {Roo.bootstrap.ComboBox} combo This combo box
12969         * @param {String} query The query
12970         * @param {Boolean} forceAll true to force "all" query
12971         * @param {Boolean} cancel true to cancel the query
12972         * @param {Object} e The query event object
12973         */
12974         'beforequery': true,
12975          /**
12976          * @event add
12977          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12978         * @param {Roo.bootstrap.ComboBox} combo This combo box
12979         */
12980         'add' : true,
12981         /**
12982          * @event edit
12983          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12984         * @param {Roo.bootstrap.ComboBox} combo This combo box
12985         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12986         */
12987         'edit' : true,
12988         /**
12989          * @event remove
12990          * Fires when the remove value from the combobox array
12991         * @param {Roo.bootstrap.ComboBox} combo This combo box
12992         */
12993         'remove' : true,
12994         /**
12995          * @event afterremove
12996          * Fires when the remove value from the combobox array
12997         * @param {Roo.bootstrap.ComboBox} combo This combo box
12998         */
12999         'afterremove' : true,
13000         /**
13001          * @event specialfilter
13002          * Fires when specialfilter
13003             * @param {Roo.bootstrap.ComboBox} combo This combo box
13004             */
13005         'specialfilter' : true,
13006         /**
13007          * @event tick
13008          * Fires when tick the element
13009             * @param {Roo.bootstrap.ComboBox} combo This combo box
13010             */
13011         'tick' : true,
13012         /**
13013          * @event touchviewdisplay
13014          * Fires when touch view require special display (default is using displayField)
13015             * @param {Roo.bootstrap.ComboBox} combo This combo box
13016             * @param {Object} cfg set html .
13017             */
13018         'touchviewdisplay' : true
13019         
13020     });
13021     
13022     this.item = [];
13023     this.tickItems = [];
13024     
13025     this.selectedIndex = -1;
13026     if(this.mode == 'local'){
13027         if(config.queryDelay === undefined){
13028             this.queryDelay = 10;
13029         }
13030         if(config.minChars === undefined){
13031             this.minChars = 0;
13032         }
13033     }
13034 };
13035
13036 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13037      
13038     /**
13039      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13040      * rendering into an Roo.Editor, defaults to false)
13041      */
13042     /**
13043      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13044      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13045      */
13046     /**
13047      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13048      */
13049     /**
13050      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13051      * the dropdown list (defaults to undefined, with no header element)
13052      */
13053
13054      /**
13055      * @cfg {String/Roo.Template} tpl The template to use to render the output
13056      */
13057      
13058      /**
13059      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13060      */
13061     listWidth: undefined,
13062     /**
13063      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13064      * mode = 'remote' or 'text' if mode = 'local')
13065      */
13066     displayField: undefined,
13067     
13068     /**
13069      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13070      * mode = 'remote' or 'value' if mode = 'local'). 
13071      * Note: use of a valueField requires the user make a selection
13072      * in order for a value to be mapped.
13073      */
13074     valueField: undefined,
13075     /**
13076      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13077      */
13078     modalTitle : '',
13079     
13080     /**
13081      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13082      * field's data value (defaults to the underlying DOM element's name)
13083      */
13084     hiddenName: undefined,
13085     /**
13086      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13087      */
13088     listClass: '',
13089     /**
13090      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13091      */
13092     selectedClass: 'active',
13093     
13094     /**
13095      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13096      */
13097     shadow:'sides',
13098     /**
13099      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13100      * anchor positions (defaults to 'tl-bl')
13101      */
13102     listAlign: 'tl-bl?',
13103     /**
13104      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13105      */
13106     maxHeight: 300,
13107     /**
13108      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13109      * query specified by the allQuery config option (defaults to 'query')
13110      */
13111     triggerAction: 'query',
13112     /**
13113      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13114      * (defaults to 4, does not apply if editable = false)
13115      */
13116     minChars : 4,
13117     /**
13118      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13119      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13120      */
13121     typeAhead: false,
13122     /**
13123      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13124      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13125      */
13126     queryDelay: 500,
13127     /**
13128      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13129      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13130      */
13131     pageSize: 0,
13132     /**
13133      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13134      * when editable = true (defaults to false)
13135      */
13136     selectOnFocus:false,
13137     /**
13138      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13139      */
13140     queryParam: 'query',
13141     /**
13142      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13143      * when mode = 'remote' (defaults to 'Loading...')
13144      */
13145     loadingText: 'Loading...',
13146     /**
13147      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13148      */
13149     resizable: false,
13150     /**
13151      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13152      */
13153     handleHeight : 8,
13154     /**
13155      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13156      * traditional select (defaults to true)
13157      */
13158     editable: true,
13159     /**
13160      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13161      */
13162     allQuery: '',
13163     /**
13164      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13165      */
13166     mode: 'remote',
13167     /**
13168      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13169      * listWidth has a higher value)
13170      */
13171     minListWidth : 70,
13172     /**
13173      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13174      * allow the user to set arbitrary text into the field (defaults to false)
13175      */
13176     forceSelection:false,
13177     /**
13178      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13179      * if typeAhead = true (defaults to 250)
13180      */
13181     typeAheadDelay : 250,
13182     /**
13183      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13184      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13185      */
13186     valueNotFoundText : undefined,
13187     /**
13188      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13189      */
13190     blockFocus : false,
13191     
13192     /**
13193      * @cfg {Boolean} disableClear Disable showing of clear button.
13194      */
13195     disableClear : false,
13196     /**
13197      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13198      */
13199     alwaysQuery : false,
13200     
13201     /**
13202      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13203      */
13204     multiple : false,
13205     
13206     /**
13207      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13208      */
13209     invalidClass : "has-warning",
13210     
13211     /**
13212      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13213      */
13214     validClass : "has-success",
13215     
13216     /**
13217      * @cfg {Boolean} specialFilter (true|false) special filter default false
13218      */
13219     specialFilter : false,
13220     
13221     /**
13222      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13223      */
13224     mobileTouchView : true,
13225     
13226     /**
13227      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13228      */
13229     useNativeIOS : false,
13230     
13231     /**
13232      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13233      */
13234     mobile_restrict_height : false,
13235     
13236     ios_options : false,
13237     
13238     //private
13239     addicon : false,
13240     editicon: false,
13241     
13242     page: 0,
13243     hasQuery: false,
13244     append: false,
13245     loadNext: false,
13246     autoFocus : true,
13247     tickable : false,
13248     btnPosition : 'right',
13249     triggerList : true,
13250     showToggleBtn : true,
13251     animate : true,
13252     emptyResultText: 'Empty',
13253     triggerText : 'Select',
13254     emptyTitle : '',
13255     
13256     // element that contains real text value.. (when hidden is used..)
13257     
13258     getAutoCreate : function()
13259     {   
13260         var cfg = false;
13261         //render
13262         /*
13263          * Render classic select for iso
13264          */
13265         
13266         if(Roo.isIOS && this.useNativeIOS){
13267             cfg = this.getAutoCreateNativeIOS();
13268             return cfg;
13269         }
13270         
13271         /*
13272          * Touch Devices
13273          */
13274         
13275         if(Roo.isTouch && this.mobileTouchView){
13276             cfg = this.getAutoCreateTouchView();
13277             return cfg;;
13278         }
13279         
13280         /*
13281          *  Normal ComboBox
13282          */
13283         if(!this.tickable){
13284             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13285             return cfg;
13286         }
13287         
13288         /*
13289          *  ComboBox with tickable selections
13290          */
13291              
13292         var align = this.labelAlign || this.parentLabelAlign();
13293         
13294         cfg = {
13295             cls : 'form-group roo-combobox-tickable' //input-group
13296         };
13297         
13298         var btn_text_select = '';
13299         var btn_text_done = '';
13300         var btn_text_cancel = '';
13301         
13302         if (this.btn_text_show) {
13303             btn_text_select = 'Select';
13304             btn_text_done = 'Done';
13305             btn_text_cancel = 'Cancel'; 
13306         }
13307         
13308         var buttons = {
13309             tag : 'div',
13310             cls : 'tickable-buttons',
13311             cn : [
13312                 {
13313                     tag : 'button',
13314                     type : 'button',
13315                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13316                     //html : this.triggerText
13317                     html: btn_text_select
13318                 },
13319                 {
13320                     tag : 'button',
13321                     type : 'button',
13322                     name : 'ok',
13323                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13324                     //html : 'Done'
13325                     html: btn_text_done
13326                 },
13327                 {
13328                     tag : 'button',
13329                     type : 'button',
13330                     name : 'cancel',
13331                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13332                     //html : 'Cancel'
13333                     html: btn_text_cancel
13334                 }
13335             ]
13336         };
13337         
13338         if(this.editable){
13339             buttons.cn.unshift({
13340                 tag: 'input',
13341                 cls: 'roo-select2-search-field-input'
13342             });
13343         }
13344         
13345         var _this = this;
13346         
13347         Roo.each(buttons.cn, function(c){
13348             if (_this.size) {
13349                 c.cls += ' btn-' + _this.size;
13350             }
13351
13352             if (_this.disabled) {
13353                 c.disabled = true;
13354             }
13355         });
13356         
13357         var box = {
13358             tag: 'div',
13359             cn: [
13360                 {
13361                     tag: 'input',
13362                     type : 'hidden',
13363                     cls: 'form-hidden-field'
13364                 },
13365                 {
13366                     tag: 'ul',
13367                     cls: 'roo-select2-choices',
13368                     cn:[
13369                         {
13370                             tag: 'li',
13371                             cls: 'roo-select2-search-field',
13372                             cn: [
13373                                 buttons
13374                             ]
13375                         }
13376                     ]
13377                 }
13378             ]
13379         };
13380         
13381         var combobox = {
13382             cls: 'roo-select2-container input-group roo-select2-container-multi',
13383             cn: [
13384                 
13385                 box
13386 //                {
13387 //                    tag: 'ul',
13388 //                    cls: 'typeahead typeahead-long dropdown-menu',
13389 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13390 //                }
13391             ]
13392         };
13393         
13394         if(this.hasFeedback && !this.allowBlank){
13395             
13396             var feedback = {
13397                 tag: 'span',
13398                 cls: 'glyphicon form-control-feedback'
13399             };
13400
13401             combobox.cn.push(feedback);
13402         }
13403         
13404         var indicator = {
13405             tag : 'i',
13406             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13407             tooltip : 'This field is required'
13408         };
13409         if (Roo.bootstrap.version == 4) {
13410             indicator = {
13411                 tag : 'i',
13412                 style : 'display:none'
13413             };
13414         }
13415         if (align ==='left' && this.fieldLabel.length) {
13416             
13417             cfg.cls += ' roo-form-group-label-left row';
13418             
13419             cfg.cn = [
13420                 indicator,
13421                 {
13422                     tag: 'label',
13423                     'for' :  id,
13424                     cls : 'control-label col-form-label',
13425                     html : this.fieldLabel
13426
13427                 },
13428                 {
13429                     cls : "", 
13430                     cn: [
13431                         combobox
13432                     ]
13433                 }
13434
13435             ];
13436             
13437             var labelCfg = cfg.cn[1];
13438             var contentCfg = cfg.cn[2];
13439             
13440
13441             if(this.indicatorpos == 'right'){
13442                 
13443                 cfg.cn = [
13444                     {
13445                         tag: 'label',
13446                         'for' :  id,
13447                         cls : 'control-label col-form-label',
13448                         cn : [
13449                             {
13450                                 tag : 'span',
13451                                 html : this.fieldLabel
13452                             },
13453                             indicator
13454                         ]
13455                     },
13456                     {
13457                         cls : "",
13458                         cn: [
13459                             combobox
13460                         ]
13461                     }
13462
13463                 ];
13464                 
13465                 
13466                 
13467                 labelCfg = cfg.cn[0];
13468                 contentCfg = cfg.cn[1];
13469             
13470             }
13471             
13472             if(this.labelWidth > 12){
13473                 labelCfg.style = "width: " + this.labelWidth + 'px';
13474             }
13475             
13476             if(this.labelWidth < 13 && this.labelmd == 0){
13477                 this.labelmd = this.labelWidth;
13478             }
13479             
13480             if(this.labellg > 0){
13481                 labelCfg.cls += ' col-lg-' + this.labellg;
13482                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13483             }
13484             
13485             if(this.labelmd > 0){
13486                 labelCfg.cls += ' col-md-' + this.labelmd;
13487                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13488             }
13489             
13490             if(this.labelsm > 0){
13491                 labelCfg.cls += ' col-sm-' + this.labelsm;
13492                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13493             }
13494             
13495             if(this.labelxs > 0){
13496                 labelCfg.cls += ' col-xs-' + this.labelxs;
13497                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13498             }
13499                 
13500                 
13501         } else if ( this.fieldLabel.length) {
13502 //                Roo.log(" label");
13503                  cfg.cn = [
13504                    indicator,
13505                     {
13506                         tag: 'label',
13507                         //cls : 'input-group-addon',
13508                         html : this.fieldLabel
13509                     },
13510                     combobox
13511                 ];
13512                 
13513                 if(this.indicatorpos == 'right'){
13514                     cfg.cn = [
13515                         {
13516                             tag: 'label',
13517                             //cls : 'input-group-addon',
13518                             html : this.fieldLabel
13519                         },
13520                         indicator,
13521                         combobox
13522                     ];
13523                     
13524                 }
13525
13526         } else {
13527             
13528 //                Roo.log(" no label && no align");
13529                 cfg = combobox
13530                      
13531                 
13532         }
13533          
13534         var settings=this;
13535         ['xs','sm','md','lg'].map(function(size){
13536             if (settings[size]) {
13537                 cfg.cls += ' col-' + size + '-' + settings[size];
13538             }
13539         });
13540         
13541         return cfg;
13542         
13543     },
13544     
13545     _initEventsCalled : false,
13546     
13547     // private
13548     initEvents: function()
13549     {   
13550         if (this._initEventsCalled) { // as we call render... prevent looping...
13551             return;
13552         }
13553         this._initEventsCalled = true;
13554         
13555         if (!this.store) {
13556             throw "can not find store for combo";
13557         }
13558         
13559         this.indicator = this.indicatorEl();
13560         
13561         this.store = Roo.factory(this.store, Roo.data);
13562         this.store.parent = this;
13563         
13564         // if we are building from html. then this element is so complex, that we can not really
13565         // use the rendered HTML.
13566         // so we have to trash and replace the previous code.
13567         if (Roo.XComponent.build_from_html) {
13568             // remove this element....
13569             var e = this.el.dom, k=0;
13570             while (e ) { e = e.previousSibling;  ++k;}
13571
13572             this.el.remove();
13573             
13574             this.el=false;
13575             this.rendered = false;
13576             
13577             this.render(this.parent().getChildContainer(true), k);
13578         }
13579         
13580         if(Roo.isIOS && this.useNativeIOS){
13581             this.initIOSView();
13582             return;
13583         }
13584         
13585         /*
13586          * Touch Devices
13587          */
13588         
13589         if(Roo.isTouch && this.mobileTouchView){
13590             this.initTouchView();
13591             return;
13592         }
13593         
13594         if(this.tickable){
13595             this.initTickableEvents();
13596             return;
13597         }
13598         
13599         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13600         
13601         if(this.hiddenName){
13602             
13603             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13604             
13605             this.hiddenField.dom.value =
13606                 this.hiddenValue !== undefined ? this.hiddenValue :
13607                 this.value !== undefined ? this.value : '';
13608
13609             // prevent input submission
13610             this.el.dom.removeAttribute('name');
13611             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13612              
13613              
13614         }
13615         //if(Roo.isGecko){
13616         //    this.el.dom.setAttribute('autocomplete', 'off');
13617         //}
13618         
13619         var cls = 'x-combo-list';
13620         
13621         //this.list = new Roo.Layer({
13622         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13623         //});
13624         
13625         var _this = this;
13626         
13627         (function(){
13628             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13629             _this.list.setWidth(lw);
13630         }).defer(100);
13631         
13632         this.list.on('mouseover', this.onViewOver, this);
13633         this.list.on('mousemove', this.onViewMove, this);
13634         this.list.on('scroll', this.onViewScroll, this);
13635         
13636         /*
13637         this.list.swallowEvent('mousewheel');
13638         this.assetHeight = 0;
13639
13640         if(this.title){
13641             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13642             this.assetHeight += this.header.getHeight();
13643         }
13644
13645         this.innerList = this.list.createChild({cls:cls+'-inner'});
13646         this.innerList.on('mouseover', this.onViewOver, this);
13647         this.innerList.on('mousemove', this.onViewMove, this);
13648         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13649         
13650         if(this.allowBlank && !this.pageSize && !this.disableClear){
13651             this.footer = this.list.createChild({cls:cls+'-ft'});
13652             this.pageTb = new Roo.Toolbar(this.footer);
13653            
13654         }
13655         if(this.pageSize){
13656             this.footer = this.list.createChild({cls:cls+'-ft'});
13657             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13658                     {pageSize: this.pageSize});
13659             
13660         }
13661         
13662         if (this.pageTb && this.allowBlank && !this.disableClear) {
13663             var _this = this;
13664             this.pageTb.add(new Roo.Toolbar.Fill(), {
13665                 cls: 'x-btn-icon x-btn-clear',
13666                 text: '&#160;',
13667                 handler: function()
13668                 {
13669                     _this.collapse();
13670                     _this.clearValue();
13671                     _this.onSelect(false, -1);
13672                 }
13673             });
13674         }
13675         if (this.footer) {
13676             this.assetHeight += this.footer.getHeight();
13677         }
13678         */
13679             
13680         if(!this.tpl){
13681             this.tpl = Roo.bootstrap.version == 4 ?
13682                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13683                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13684         }
13685
13686         this.view = new Roo.View(this.list, this.tpl, {
13687             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13688         });
13689         //this.view.wrapEl.setDisplayed(false);
13690         this.view.on('click', this.onViewClick, this);
13691         
13692         
13693         this.store.on('beforeload', this.onBeforeLoad, this);
13694         this.store.on('load', this.onLoad, this);
13695         this.store.on('loadexception', this.onLoadException, this);
13696         /*
13697         if(this.resizable){
13698             this.resizer = new Roo.Resizable(this.list,  {
13699                pinned:true, handles:'se'
13700             });
13701             this.resizer.on('resize', function(r, w, h){
13702                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13703                 this.listWidth = w;
13704                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13705                 this.restrictHeight();
13706             }, this);
13707             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13708         }
13709         */
13710         if(!this.editable){
13711             this.editable = true;
13712             this.setEditable(false);
13713         }
13714         
13715         /*
13716         
13717         if (typeof(this.events.add.listeners) != 'undefined') {
13718             
13719             this.addicon = this.wrap.createChild(
13720                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13721        
13722             this.addicon.on('click', function(e) {
13723                 this.fireEvent('add', this);
13724             }, this);
13725         }
13726         if (typeof(this.events.edit.listeners) != 'undefined') {
13727             
13728             this.editicon = this.wrap.createChild(
13729                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13730             if (this.addicon) {
13731                 this.editicon.setStyle('margin-left', '40px');
13732             }
13733             this.editicon.on('click', function(e) {
13734                 
13735                 // we fire even  if inothing is selected..
13736                 this.fireEvent('edit', this, this.lastData );
13737                 
13738             }, this);
13739         }
13740         */
13741         
13742         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13743             "up" : function(e){
13744                 this.inKeyMode = true;
13745                 this.selectPrev();
13746             },
13747
13748             "down" : function(e){
13749                 if(!this.isExpanded()){
13750                     this.onTriggerClick();
13751                 }else{
13752                     this.inKeyMode = true;
13753                     this.selectNext();
13754                 }
13755             },
13756
13757             "enter" : function(e){
13758 //                this.onViewClick();
13759                 //return true;
13760                 this.collapse();
13761                 
13762                 if(this.fireEvent("specialkey", this, e)){
13763                     this.onViewClick(false);
13764                 }
13765                 
13766                 return true;
13767             },
13768
13769             "esc" : function(e){
13770                 this.collapse();
13771             },
13772
13773             "tab" : function(e){
13774                 this.collapse();
13775                 
13776                 if(this.fireEvent("specialkey", this, e)){
13777                     this.onViewClick(false);
13778                 }
13779                 
13780                 return true;
13781             },
13782
13783             scope : this,
13784
13785             doRelay : function(foo, bar, hname){
13786                 if(hname == 'down' || this.scope.isExpanded()){
13787                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13788                 }
13789                 return true;
13790             },
13791
13792             forceKeyDown: true
13793         });
13794         
13795         
13796         this.queryDelay = Math.max(this.queryDelay || 10,
13797                 this.mode == 'local' ? 10 : 250);
13798         
13799         
13800         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13801         
13802         if(this.typeAhead){
13803             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13804         }
13805         if(this.editable !== false){
13806             this.inputEl().on("keyup", this.onKeyUp, this);
13807         }
13808         if(this.forceSelection){
13809             this.inputEl().on('blur', this.doForce, this);
13810         }
13811         
13812         if(this.multiple){
13813             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13814             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13815         }
13816     },
13817     
13818     initTickableEvents: function()
13819     {   
13820         this.createList();
13821         
13822         if(this.hiddenName){
13823             
13824             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13825             
13826             this.hiddenField.dom.value =
13827                 this.hiddenValue !== undefined ? this.hiddenValue :
13828                 this.value !== undefined ? this.value : '';
13829
13830             // prevent input submission
13831             this.el.dom.removeAttribute('name');
13832             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13833              
13834              
13835         }
13836         
13837 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13838         
13839         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13840         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13841         if(this.triggerList){
13842             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13843         }
13844          
13845         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13846         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13847         
13848         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13849         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13850         
13851         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13852         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13853         
13854         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13855         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13856         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13857         
13858         this.okBtn.hide();
13859         this.cancelBtn.hide();
13860         
13861         var _this = this;
13862         
13863         (function(){
13864             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13865             _this.list.setWidth(lw);
13866         }).defer(100);
13867         
13868         this.list.on('mouseover', this.onViewOver, this);
13869         this.list.on('mousemove', this.onViewMove, this);
13870         
13871         this.list.on('scroll', this.onViewScroll, this);
13872         
13873         if(!this.tpl){
13874             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13875                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13876         }
13877
13878         this.view = new Roo.View(this.list, this.tpl, {
13879             singleSelect:true,
13880             tickable:true,
13881             parent:this,
13882             store: this.store,
13883             selectedClass: this.selectedClass
13884         });
13885         
13886         //this.view.wrapEl.setDisplayed(false);
13887         this.view.on('click', this.onViewClick, this);
13888         
13889         
13890         
13891         this.store.on('beforeload', this.onBeforeLoad, this);
13892         this.store.on('load', this.onLoad, this);
13893         this.store.on('loadexception', this.onLoadException, this);
13894         
13895         if(this.editable){
13896             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13897                 "up" : function(e){
13898                     this.inKeyMode = true;
13899                     this.selectPrev();
13900                 },
13901
13902                 "down" : function(e){
13903                     this.inKeyMode = true;
13904                     this.selectNext();
13905                 },
13906
13907                 "enter" : function(e){
13908                     if(this.fireEvent("specialkey", this, e)){
13909                         this.onViewClick(false);
13910                     }
13911                     
13912                     return true;
13913                 },
13914
13915                 "esc" : function(e){
13916                     this.onTickableFooterButtonClick(e, false, false);
13917                 },
13918
13919                 "tab" : function(e){
13920                     this.fireEvent("specialkey", this, e);
13921                     
13922                     this.onTickableFooterButtonClick(e, false, false);
13923                     
13924                     return true;
13925                 },
13926
13927                 scope : this,
13928
13929                 doRelay : function(e, fn, key){
13930                     if(this.scope.isExpanded()){
13931                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13932                     }
13933                     return true;
13934                 },
13935
13936                 forceKeyDown: true
13937             });
13938         }
13939         
13940         this.queryDelay = Math.max(this.queryDelay || 10,
13941                 this.mode == 'local' ? 10 : 250);
13942         
13943         
13944         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13945         
13946         if(this.typeAhead){
13947             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13948         }
13949         
13950         if(this.editable !== false){
13951             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13952         }
13953         
13954         this.indicator = this.indicatorEl();
13955         
13956         if(this.indicator){
13957             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13958             this.indicator.hide();
13959         }
13960         
13961     },
13962
13963     onDestroy : function(){
13964         if(this.view){
13965             this.view.setStore(null);
13966             this.view.el.removeAllListeners();
13967             this.view.el.remove();
13968             this.view.purgeListeners();
13969         }
13970         if(this.list){
13971             this.list.dom.innerHTML  = '';
13972         }
13973         
13974         if(this.store){
13975             this.store.un('beforeload', this.onBeforeLoad, this);
13976             this.store.un('load', this.onLoad, this);
13977             this.store.un('loadexception', this.onLoadException, this);
13978         }
13979         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13980     },
13981
13982     // private
13983     fireKey : function(e){
13984         if(e.isNavKeyPress() && !this.list.isVisible()){
13985             this.fireEvent("specialkey", this, e);
13986         }
13987     },
13988
13989     // private
13990     onResize: function(w, h){
13991 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13992 //        
13993 //        if(typeof w != 'number'){
13994 //            // we do not handle it!?!?
13995 //            return;
13996 //        }
13997 //        var tw = this.trigger.getWidth();
13998 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13999 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14000 //        var x = w - tw;
14001 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14002 //            
14003 //        //this.trigger.setStyle('left', x+'px');
14004 //        
14005 //        if(this.list && this.listWidth === undefined){
14006 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14007 //            this.list.setWidth(lw);
14008 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14009 //        }
14010         
14011     
14012         
14013     },
14014
14015     /**
14016      * Allow or prevent the user from directly editing the field text.  If false is passed,
14017      * the user will only be able to select from the items defined in the dropdown list.  This method
14018      * is the runtime equivalent of setting the 'editable' config option at config time.
14019      * @param {Boolean} value True to allow the user to directly edit the field text
14020      */
14021     setEditable : function(value){
14022         if(value == this.editable){
14023             return;
14024         }
14025         this.editable = value;
14026         if(!value){
14027             this.inputEl().dom.setAttribute('readOnly', true);
14028             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14029             this.inputEl().addClass('x-combo-noedit');
14030         }else{
14031             this.inputEl().dom.setAttribute('readOnly', false);
14032             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14033             this.inputEl().removeClass('x-combo-noedit');
14034         }
14035     },
14036
14037     // private
14038     
14039     onBeforeLoad : function(combo,opts){
14040         if(!this.hasFocus){
14041             return;
14042         }
14043          if (!opts.add) {
14044             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14045          }
14046         this.restrictHeight();
14047         this.selectedIndex = -1;
14048     },
14049
14050     // private
14051     onLoad : function(){
14052         
14053         this.hasQuery = false;
14054         
14055         if(!this.hasFocus){
14056             return;
14057         }
14058         
14059         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14060             this.loading.hide();
14061         }
14062         
14063         if(this.store.getCount() > 0){
14064             
14065             this.expand();
14066             this.restrictHeight();
14067             if(this.lastQuery == this.allQuery){
14068                 if(this.editable && !this.tickable){
14069                     this.inputEl().dom.select();
14070                 }
14071                 
14072                 if(
14073                     !this.selectByValue(this.value, true) &&
14074                     this.autoFocus && 
14075                     (
14076                         !this.store.lastOptions ||
14077                         typeof(this.store.lastOptions.add) == 'undefined' || 
14078                         this.store.lastOptions.add != true
14079                     )
14080                 ){
14081                     this.select(0, true);
14082                 }
14083             }else{
14084                 if(this.autoFocus){
14085                     this.selectNext();
14086                 }
14087                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14088                     this.taTask.delay(this.typeAheadDelay);
14089                 }
14090             }
14091         }else{
14092             this.onEmptyResults();
14093         }
14094         
14095         //this.el.focus();
14096     },
14097     // private
14098     onLoadException : function()
14099     {
14100         this.hasQuery = false;
14101         
14102         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14103             this.loading.hide();
14104         }
14105         
14106         if(this.tickable && this.editable){
14107             return;
14108         }
14109         
14110         this.collapse();
14111         // only causes errors at present
14112         //Roo.log(this.store.reader.jsonData);
14113         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14114             // fixme
14115             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14116         //}
14117         
14118         
14119     },
14120     // private
14121     onTypeAhead : function(){
14122         if(this.store.getCount() > 0){
14123             var r = this.store.getAt(0);
14124             var newValue = r.data[this.displayField];
14125             var len = newValue.length;
14126             var selStart = this.getRawValue().length;
14127             
14128             if(selStart != len){
14129                 this.setRawValue(newValue);
14130                 this.selectText(selStart, newValue.length);
14131             }
14132         }
14133     },
14134
14135     // private
14136     onSelect : function(record, index){
14137         
14138         if(this.fireEvent('beforeselect', this, record, index) !== false){
14139         
14140             this.setFromData(index > -1 ? record.data : false);
14141             
14142             this.collapse();
14143             this.fireEvent('select', this, record, index);
14144         }
14145     },
14146
14147     /**
14148      * Returns the currently selected field value or empty string if no value is set.
14149      * @return {String} value The selected value
14150      */
14151     getValue : function()
14152     {
14153         if(Roo.isIOS && this.useNativeIOS){
14154             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14155         }
14156         
14157         if(this.multiple){
14158             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14159         }
14160         
14161         if(this.valueField){
14162             return typeof this.value != 'undefined' ? this.value : '';
14163         }else{
14164             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14165         }
14166     },
14167     
14168     getRawValue : function()
14169     {
14170         if(Roo.isIOS && this.useNativeIOS){
14171             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14172         }
14173         
14174         var v = this.inputEl().getValue();
14175         
14176         return v;
14177     },
14178
14179     /**
14180      * Clears any text/value currently set in the field
14181      */
14182     clearValue : function(){
14183         
14184         if(this.hiddenField){
14185             this.hiddenField.dom.value = '';
14186         }
14187         this.value = '';
14188         this.setRawValue('');
14189         this.lastSelectionText = '';
14190         this.lastData = false;
14191         
14192         var close = this.closeTriggerEl();
14193         
14194         if(close){
14195             close.hide();
14196         }
14197         
14198         this.validate();
14199         
14200     },
14201
14202     /**
14203      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14204      * will be displayed in the field.  If the value does not match the data value of an existing item,
14205      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14206      * Otherwise the field will be blank (although the value will still be set).
14207      * @param {String} value The value to match
14208      */
14209     setValue : function(v)
14210     {
14211         if(Roo.isIOS && this.useNativeIOS){
14212             this.setIOSValue(v);
14213             return;
14214         }
14215         
14216         if(this.multiple){
14217             this.syncValue();
14218             return;
14219         }
14220         
14221         var text = v;
14222         if(this.valueField){
14223             var r = this.findRecord(this.valueField, v);
14224             if(r){
14225                 text = r.data[this.displayField];
14226             }else if(this.valueNotFoundText !== undefined){
14227                 text = this.valueNotFoundText;
14228             }
14229         }
14230         this.lastSelectionText = text;
14231         if(this.hiddenField){
14232             this.hiddenField.dom.value = v;
14233         }
14234         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14235         this.value = v;
14236         
14237         var close = this.closeTriggerEl();
14238         
14239         if(close){
14240             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14241         }
14242         
14243         this.validate();
14244     },
14245     /**
14246      * @property {Object} the last set data for the element
14247      */
14248     
14249     lastData : false,
14250     /**
14251      * Sets the value of the field based on a object which is related to the record format for the store.
14252      * @param {Object} value the value to set as. or false on reset?
14253      */
14254     setFromData : function(o){
14255         
14256         if(this.multiple){
14257             this.addItem(o);
14258             return;
14259         }
14260             
14261         var dv = ''; // display value
14262         var vv = ''; // value value..
14263         this.lastData = o;
14264         if (this.displayField) {
14265             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14266         } else {
14267             // this is an error condition!!!
14268             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14269         }
14270         
14271         if(this.valueField){
14272             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14273         }
14274         
14275         var close = this.closeTriggerEl();
14276         
14277         if(close){
14278             if(dv.length || vv * 1 > 0){
14279                 close.show() ;
14280                 this.blockFocus=true;
14281             } else {
14282                 close.hide();
14283             }             
14284         }
14285         
14286         if(this.hiddenField){
14287             this.hiddenField.dom.value = vv;
14288             
14289             this.lastSelectionText = dv;
14290             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14291             this.value = vv;
14292             return;
14293         }
14294         // no hidden field.. - we store the value in 'value', but still display
14295         // display field!!!!
14296         this.lastSelectionText = dv;
14297         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14298         this.value = vv;
14299         
14300         
14301         
14302     },
14303     // private
14304     reset : function(){
14305         // overridden so that last data is reset..
14306         
14307         if(this.multiple){
14308             this.clearItem();
14309             return;
14310         }
14311         
14312         this.setValue(this.originalValue);
14313         //this.clearInvalid();
14314         this.lastData = false;
14315         if (this.view) {
14316             this.view.clearSelections();
14317         }
14318         
14319         this.validate();
14320     },
14321     // private
14322     findRecord : function(prop, value){
14323         var record;
14324         if(this.store.getCount() > 0){
14325             this.store.each(function(r){
14326                 if(r.data[prop] == value){
14327                     record = r;
14328                     return false;
14329                 }
14330                 return true;
14331             });
14332         }
14333         return record;
14334     },
14335     
14336     getName: function()
14337     {
14338         // returns hidden if it's set..
14339         if (!this.rendered) {return ''};
14340         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14341         
14342     },
14343     // private
14344     onViewMove : function(e, t){
14345         this.inKeyMode = false;
14346     },
14347
14348     // private
14349     onViewOver : function(e, t){
14350         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14351             return;
14352         }
14353         var item = this.view.findItemFromChild(t);
14354         
14355         if(item){
14356             var index = this.view.indexOf(item);
14357             this.select(index, false);
14358         }
14359     },
14360
14361     // private
14362     onViewClick : function(view, doFocus, el, e)
14363     {
14364         var index = this.view.getSelectedIndexes()[0];
14365         
14366         var r = this.store.getAt(index);
14367         
14368         if(this.tickable){
14369             
14370             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14371                 return;
14372             }
14373             
14374             var rm = false;
14375             var _this = this;
14376             
14377             Roo.each(this.tickItems, function(v,k){
14378                 
14379                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14380                     Roo.log(v);
14381                     _this.tickItems.splice(k, 1);
14382                     
14383                     if(typeof(e) == 'undefined' && view == false){
14384                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14385                     }
14386                     
14387                     rm = true;
14388                     return;
14389                 }
14390             });
14391             
14392             if(rm){
14393                 return;
14394             }
14395             
14396             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14397                 this.tickItems.push(r.data);
14398             }
14399             
14400             if(typeof(e) == 'undefined' && view == false){
14401                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14402             }
14403                     
14404             return;
14405         }
14406         
14407         if(r){
14408             this.onSelect(r, index);
14409         }
14410         if(doFocus !== false && !this.blockFocus){
14411             this.inputEl().focus();
14412         }
14413     },
14414
14415     // private
14416     restrictHeight : function(){
14417         //this.innerList.dom.style.height = '';
14418         //var inner = this.innerList.dom;
14419         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14420         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14421         //this.list.beginUpdate();
14422         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14423         this.list.alignTo(this.inputEl(), this.listAlign);
14424         this.list.alignTo(this.inputEl(), this.listAlign);
14425         //this.list.endUpdate();
14426     },
14427
14428     // private
14429     onEmptyResults : function(){
14430         
14431         if(this.tickable && this.editable){
14432             this.hasFocus = false;
14433             this.restrictHeight();
14434             return;
14435         }
14436         
14437         this.collapse();
14438     },
14439
14440     /**
14441      * Returns true if the dropdown list is expanded, else false.
14442      */
14443     isExpanded : function(){
14444         return this.list.isVisible();
14445     },
14446
14447     /**
14448      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14449      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14450      * @param {String} value The data value of the item to select
14451      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14452      * selected item if it is not currently in view (defaults to true)
14453      * @return {Boolean} True if the value matched an item in the list, else false
14454      */
14455     selectByValue : function(v, scrollIntoView){
14456         if(v !== undefined && v !== null){
14457             var r = this.findRecord(this.valueField || this.displayField, v);
14458             if(r){
14459                 this.select(this.store.indexOf(r), scrollIntoView);
14460                 return true;
14461             }
14462         }
14463         return false;
14464     },
14465
14466     /**
14467      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14468      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14469      * @param {Number} index The zero-based index of the list item to select
14470      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14471      * selected item if it is not currently in view (defaults to true)
14472      */
14473     select : function(index, scrollIntoView){
14474         this.selectedIndex = index;
14475         this.view.select(index);
14476         if(scrollIntoView !== false){
14477             var el = this.view.getNode(index);
14478             /*
14479              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14480              */
14481             if(el){
14482                 this.list.scrollChildIntoView(el, false);
14483             }
14484         }
14485     },
14486
14487     // private
14488     selectNext : function(){
14489         var ct = this.store.getCount();
14490         if(ct > 0){
14491             if(this.selectedIndex == -1){
14492                 this.select(0);
14493             }else if(this.selectedIndex < ct-1){
14494                 this.select(this.selectedIndex+1);
14495             }
14496         }
14497     },
14498
14499     // private
14500     selectPrev : function(){
14501         var ct = this.store.getCount();
14502         if(ct > 0){
14503             if(this.selectedIndex == -1){
14504                 this.select(0);
14505             }else if(this.selectedIndex != 0){
14506                 this.select(this.selectedIndex-1);
14507             }
14508         }
14509     },
14510
14511     // private
14512     onKeyUp : function(e){
14513         if(this.editable !== false && !e.isSpecialKey()){
14514             this.lastKey = e.getKey();
14515             this.dqTask.delay(this.queryDelay);
14516         }
14517     },
14518
14519     // private
14520     validateBlur : function(){
14521         return !this.list || !this.list.isVisible();   
14522     },
14523
14524     // private
14525     initQuery : function(){
14526         
14527         var v = this.getRawValue();
14528         
14529         if(this.tickable && this.editable){
14530             v = this.tickableInputEl().getValue();
14531         }
14532         
14533         this.doQuery(v);
14534     },
14535
14536     // private
14537     doForce : function(){
14538         if(this.inputEl().dom.value.length > 0){
14539             this.inputEl().dom.value =
14540                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14541              
14542         }
14543     },
14544
14545     /**
14546      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14547      * query allowing the query action to be canceled if needed.
14548      * @param {String} query The SQL query to execute
14549      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14550      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14551      * saved in the current store (defaults to false)
14552      */
14553     doQuery : function(q, forceAll){
14554         
14555         if(q === undefined || q === null){
14556             q = '';
14557         }
14558         var qe = {
14559             query: q,
14560             forceAll: forceAll,
14561             combo: this,
14562             cancel:false
14563         };
14564         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14565             return false;
14566         }
14567         q = qe.query;
14568         
14569         forceAll = qe.forceAll;
14570         if(forceAll === true || (q.length >= this.minChars)){
14571             
14572             this.hasQuery = true;
14573             
14574             if(this.lastQuery != q || this.alwaysQuery){
14575                 this.lastQuery = q;
14576                 if(this.mode == 'local'){
14577                     this.selectedIndex = -1;
14578                     if(forceAll){
14579                         this.store.clearFilter();
14580                     }else{
14581                         
14582                         if(this.specialFilter){
14583                             this.fireEvent('specialfilter', this);
14584                             this.onLoad();
14585                             return;
14586                         }
14587                         
14588                         this.store.filter(this.displayField, q);
14589                     }
14590                     
14591                     this.store.fireEvent("datachanged", this.store);
14592                     
14593                     this.onLoad();
14594                     
14595                     
14596                 }else{
14597                     
14598                     this.store.baseParams[this.queryParam] = q;
14599                     
14600                     var options = {params : this.getParams(q)};
14601                     
14602                     if(this.loadNext){
14603                         options.add = true;
14604                         options.params.start = this.page * this.pageSize;
14605                     }
14606                     
14607                     this.store.load(options);
14608                     
14609                     /*
14610                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14611                      *  we should expand the list on onLoad
14612                      *  so command out it
14613                      */
14614 //                    this.expand();
14615                 }
14616             }else{
14617                 this.selectedIndex = -1;
14618                 this.onLoad();   
14619             }
14620         }
14621         
14622         this.loadNext = false;
14623     },
14624     
14625     // private
14626     getParams : function(q){
14627         var p = {};
14628         //p[this.queryParam] = q;
14629         
14630         if(this.pageSize){
14631             p.start = 0;
14632             p.limit = this.pageSize;
14633         }
14634         return p;
14635     },
14636
14637     /**
14638      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14639      */
14640     collapse : function(){
14641         if(!this.isExpanded()){
14642             return;
14643         }
14644         
14645         this.list.hide();
14646         
14647         this.hasFocus = false;
14648         
14649         if(this.tickable){
14650             this.okBtn.hide();
14651             this.cancelBtn.hide();
14652             this.trigger.show();
14653             
14654             if(this.editable){
14655                 this.tickableInputEl().dom.value = '';
14656                 this.tickableInputEl().blur();
14657             }
14658             
14659         }
14660         
14661         Roo.get(document).un('mousedown', this.collapseIf, this);
14662         Roo.get(document).un('mousewheel', this.collapseIf, this);
14663         if (!this.editable) {
14664             Roo.get(document).un('keydown', this.listKeyPress, this);
14665         }
14666         this.fireEvent('collapse', this);
14667         
14668         this.validate();
14669     },
14670
14671     // private
14672     collapseIf : function(e){
14673         var in_combo  = e.within(this.el);
14674         var in_list =  e.within(this.list);
14675         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14676         
14677         if (in_combo || in_list || is_list) {
14678             //e.stopPropagation();
14679             return;
14680         }
14681         
14682         if(this.tickable){
14683             this.onTickableFooterButtonClick(e, false, false);
14684         }
14685
14686         this.collapse();
14687         
14688     },
14689
14690     /**
14691      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14692      */
14693     expand : function(){
14694        
14695         if(this.isExpanded() || !this.hasFocus){
14696             return;
14697         }
14698         
14699         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14700         this.list.setWidth(lw);
14701         
14702         Roo.log('expand');
14703         
14704         this.list.show();
14705         
14706         this.restrictHeight();
14707         
14708         if(this.tickable){
14709             
14710             this.tickItems = Roo.apply([], this.item);
14711             
14712             this.okBtn.show();
14713             this.cancelBtn.show();
14714             this.trigger.hide();
14715             
14716             if(this.editable){
14717                 this.tickableInputEl().focus();
14718             }
14719             
14720         }
14721         
14722         Roo.get(document).on('mousedown', this.collapseIf, this);
14723         Roo.get(document).on('mousewheel', this.collapseIf, this);
14724         if (!this.editable) {
14725             Roo.get(document).on('keydown', this.listKeyPress, this);
14726         }
14727         
14728         this.fireEvent('expand', this);
14729     },
14730
14731     // private
14732     // Implements the default empty TriggerField.onTriggerClick function
14733     onTriggerClick : function(e)
14734     {
14735         Roo.log('trigger click');
14736         
14737         if(this.disabled || !this.triggerList){
14738             return;
14739         }
14740         
14741         this.page = 0;
14742         this.loadNext = false;
14743         
14744         if(this.isExpanded()){
14745             this.collapse();
14746             if (!this.blockFocus) {
14747                 this.inputEl().focus();
14748             }
14749             
14750         }else {
14751             this.hasFocus = true;
14752             if(this.triggerAction == 'all') {
14753                 this.doQuery(this.allQuery, true);
14754             } else {
14755                 this.doQuery(this.getRawValue());
14756             }
14757             if (!this.blockFocus) {
14758                 this.inputEl().focus();
14759             }
14760         }
14761     },
14762     
14763     onTickableTriggerClick : function(e)
14764     {
14765         if(this.disabled){
14766             return;
14767         }
14768         
14769         this.page = 0;
14770         this.loadNext = false;
14771         this.hasFocus = true;
14772         
14773         if(this.triggerAction == 'all') {
14774             this.doQuery(this.allQuery, true);
14775         } else {
14776             this.doQuery(this.getRawValue());
14777         }
14778     },
14779     
14780     onSearchFieldClick : function(e)
14781     {
14782         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14783             this.onTickableFooterButtonClick(e, false, false);
14784             return;
14785         }
14786         
14787         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14788             return;
14789         }
14790         
14791         this.page = 0;
14792         this.loadNext = false;
14793         this.hasFocus = true;
14794         
14795         if(this.triggerAction == 'all') {
14796             this.doQuery(this.allQuery, true);
14797         } else {
14798             this.doQuery(this.getRawValue());
14799         }
14800     },
14801     
14802     listKeyPress : function(e)
14803     {
14804         //Roo.log('listkeypress');
14805         // scroll to first matching element based on key pres..
14806         if (e.isSpecialKey()) {
14807             return false;
14808         }
14809         var k = String.fromCharCode(e.getKey()).toUpperCase();
14810         //Roo.log(k);
14811         var match  = false;
14812         var csel = this.view.getSelectedNodes();
14813         var cselitem = false;
14814         if (csel.length) {
14815             var ix = this.view.indexOf(csel[0]);
14816             cselitem  = this.store.getAt(ix);
14817             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14818                 cselitem = false;
14819             }
14820             
14821         }
14822         
14823         this.store.each(function(v) { 
14824             if (cselitem) {
14825                 // start at existing selection.
14826                 if (cselitem.id == v.id) {
14827                     cselitem = false;
14828                 }
14829                 return true;
14830             }
14831                 
14832             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14833                 match = this.store.indexOf(v);
14834                 return false;
14835             }
14836             return true;
14837         }, this);
14838         
14839         if (match === false) {
14840             return true; // no more action?
14841         }
14842         // scroll to?
14843         this.view.select(match);
14844         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14845         sn.scrollIntoView(sn.dom.parentNode, false);
14846     },
14847     
14848     onViewScroll : function(e, t){
14849         
14850         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){
14851             return;
14852         }
14853         
14854         this.hasQuery = true;
14855         
14856         this.loading = this.list.select('.loading', true).first();
14857         
14858         if(this.loading === null){
14859             this.list.createChild({
14860                 tag: 'div',
14861                 cls: 'loading roo-select2-more-results roo-select2-active',
14862                 html: 'Loading more results...'
14863             });
14864             
14865             this.loading = this.list.select('.loading', true).first();
14866             
14867             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14868             
14869             this.loading.hide();
14870         }
14871         
14872         this.loading.show();
14873         
14874         var _combo = this;
14875         
14876         this.page++;
14877         this.loadNext = true;
14878         
14879         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14880         
14881         return;
14882     },
14883     
14884     addItem : function(o)
14885     {   
14886         var dv = ''; // display value
14887         
14888         if (this.displayField) {
14889             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14890         } else {
14891             // this is an error condition!!!
14892             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14893         }
14894         
14895         if(!dv.length){
14896             return;
14897         }
14898         
14899         var choice = this.choices.createChild({
14900             tag: 'li',
14901             cls: 'roo-select2-search-choice',
14902             cn: [
14903                 {
14904                     tag: 'div',
14905                     html: dv
14906                 },
14907                 {
14908                     tag: 'a',
14909                     href: '#',
14910                     cls: 'roo-select2-search-choice-close fa fa-times',
14911                     tabindex: '-1'
14912                 }
14913             ]
14914             
14915         }, this.searchField);
14916         
14917         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14918         
14919         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14920         
14921         this.item.push(o);
14922         
14923         this.lastData = o;
14924         
14925         this.syncValue();
14926         
14927         this.inputEl().dom.value = '';
14928         
14929         this.validate();
14930     },
14931     
14932     onRemoveItem : function(e, _self, o)
14933     {
14934         e.preventDefault();
14935         
14936         this.lastItem = Roo.apply([], this.item);
14937         
14938         var index = this.item.indexOf(o.data) * 1;
14939         
14940         if( index < 0){
14941             Roo.log('not this item?!');
14942             return;
14943         }
14944         
14945         this.item.splice(index, 1);
14946         o.item.remove();
14947         
14948         this.syncValue();
14949         
14950         this.fireEvent('remove', this, e);
14951         
14952         this.validate();
14953         
14954     },
14955     
14956     syncValue : function()
14957     {
14958         if(!this.item.length){
14959             this.clearValue();
14960             return;
14961         }
14962             
14963         var value = [];
14964         var _this = this;
14965         Roo.each(this.item, function(i){
14966             if(_this.valueField){
14967                 value.push(i[_this.valueField]);
14968                 return;
14969             }
14970
14971             value.push(i);
14972         });
14973
14974         this.value = value.join(',');
14975
14976         if(this.hiddenField){
14977             this.hiddenField.dom.value = this.value;
14978         }
14979         
14980         this.store.fireEvent("datachanged", this.store);
14981         
14982         this.validate();
14983     },
14984     
14985     clearItem : function()
14986     {
14987         if(!this.multiple){
14988             return;
14989         }
14990         
14991         this.item = [];
14992         
14993         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14994            c.remove();
14995         });
14996         
14997         this.syncValue();
14998         
14999         this.validate();
15000         
15001         if(this.tickable && !Roo.isTouch){
15002             this.view.refresh();
15003         }
15004     },
15005     
15006     inputEl: function ()
15007     {
15008         if(Roo.isIOS && this.useNativeIOS){
15009             return this.el.select('select.roo-ios-select', true).first();
15010         }
15011         
15012         if(Roo.isTouch && this.mobileTouchView){
15013             return this.el.select('input.form-control',true).first();
15014         }
15015         
15016         if(this.tickable){
15017             return this.searchField;
15018         }
15019         
15020         return this.el.select('input.form-control',true).first();
15021     },
15022     
15023     onTickableFooterButtonClick : function(e, btn, el)
15024     {
15025         e.preventDefault();
15026         
15027         this.lastItem = Roo.apply([], this.item);
15028         
15029         if(btn && btn.name == 'cancel'){
15030             this.tickItems = Roo.apply([], this.item);
15031             this.collapse();
15032             return;
15033         }
15034         
15035         this.clearItem();
15036         
15037         var _this = this;
15038         
15039         Roo.each(this.tickItems, function(o){
15040             _this.addItem(o);
15041         });
15042         
15043         this.collapse();
15044         
15045     },
15046     
15047     validate : function()
15048     {
15049         if(this.getVisibilityEl().hasClass('hidden')){
15050             return true;
15051         }
15052         
15053         var v = this.getRawValue();
15054         
15055         if(this.multiple){
15056             v = this.getValue();
15057         }
15058         
15059         if(this.disabled || this.allowBlank || v.length){
15060             this.markValid();
15061             return true;
15062         }
15063         
15064         this.markInvalid();
15065         return false;
15066     },
15067     
15068     tickableInputEl : function()
15069     {
15070         if(!this.tickable || !this.editable){
15071             return this.inputEl();
15072         }
15073         
15074         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15075     },
15076     
15077     
15078     getAutoCreateTouchView : function()
15079     {
15080         var id = Roo.id();
15081         
15082         var cfg = {
15083             cls: 'form-group' //input-group
15084         };
15085         
15086         var input =  {
15087             tag: 'input',
15088             id : id,
15089             type : this.inputType,
15090             cls : 'form-control x-combo-noedit',
15091             autocomplete: 'new-password',
15092             placeholder : this.placeholder || '',
15093             readonly : true
15094         };
15095         
15096         if (this.name) {
15097             input.name = this.name;
15098         }
15099         
15100         if (this.size) {
15101             input.cls += ' input-' + this.size;
15102         }
15103         
15104         if (this.disabled) {
15105             input.disabled = true;
15106         }
15107         
15108         var inputblock = {
15109             cls : '',
15110             cn : [
15111                 input
15112             ]
15113         };
15114         
15115         if(this.before){
15116             inputblock.cls += ' input-group';
15117             
15118             inputblock.cn.unshift({
15119                 tag :'span',
15120                 cls : 'input-group-addon input-group-prepend input-group-text',
15121                 html : this.before
15122             });
15123         }
15124         
15125         if(this.removable && !this.multiple){
15126             inputblock.cls += ' roo-removable';
15127             
15128             inputblock.cn.push({
15129                 tag: 'button',
15130                 html : 'x',
15131                 cls : 'roo-combo-removable-btn close'
15132             });
15133         }
15134
15135         if(this.hasFeedback && !this.allowBlank){
15136             
15137             inputblock.cls += ' has-feedback';
15138             
15139             inputblock.cn.push({
15140                 tag: 'span',
15141                 cls: 'glyphicon form-control-feedback'
15142             });
15143             
15144         }
15145         
15146         if (this.after) {
15147             
15148             inputblock.cls += (this.before) ? '' : ' input-group';
15149             
15150             inputblock.cn.push({
15151                 tag :'span',
15152                 cls : 'input-group-addon input-group-append input-group-text',
15153                 html : this.after
15154             });
15155         }
15156
15157         
15158         var ibwrap = inputblock;
15159         
15160         if(this.multiple){
15161             ibwrap = {
15162                 tag: 'ul',
15163                 cls: 'roo-select2-choices',
15164                 cn:[
15165                     {
15166                         tag: 'li',
15167                         cls: 'roo-select2-search-field',
15168                         cn: [
15169
15170                             inputblock
15171                         ]
15172                     }
15173                 ]
15174             };
15175         
15176             
15177         }
15178         
15179         var combobox = {
15180             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15181             cn: [
15182                 {
15183                     tag: 'input',
15184                     type : 'hidden',
15185                     cls: 'form-hidden-field'
15186                 },
15187                 ibwrap
15188             ]
15189         };
15190         
15191         if(!this.multiple && this.showToggleBtn){
15192             
15193             var caret = {
15194                         tag: 'span',
15195                         cls: 'caret'
15196             };
15197             
15198             if (this.caret != false) {
15199                 caret = {
15200                      tag: 'i',
15201                      cls: 'fa fa-' + this.caret
15202                 };
15203                 
15204             }
15205             
15206             combobox.cn.push({
15207                 tag :'span',
15208                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15209                 cn : [
15210                     caret,
15211                     {
15212                         tag: 'span',
15213                         cls: 'combobox-clear',
15214                         cn  : [
15215                             {
15216                                 tag : 'i',
15217                                 cls: 'icon-remove'
15218                             }
15219                         ]
15220                     }
15221                 ]
15222
15223             })
15224         }
15225         
15226         if(this.multiple){
15227             combobox.cls += ' roo-select2-container-multi';
15228         }
15229         
15230         var align = this.labelAlign || this.parentLabelAlign();
15231         
15232         if (align ==='left' && this.fieldLabel.length) {
15233
15234             cfg.cn = [
15235                 {
15236                    tag : 'i',
15237                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15238                    tooltip : 'This field is required'
15239                 },
15240                 {
15241                     tag: 'label',
15242                     cls : 'control-label col-form-label',
15243                     html : this.fieldLabel
15244
15245                 },
15246                 {
15247                     cls : '', 
15248                     cn: [
15249                         combobox
15250                     ]
15251                 }
15252             ];
15253             
15254             var labelCfg = cfg.cn[1];
15255             var contentCfg = cfg.cn[2];
15256             
15257
15258             if(this.indicatorpos == 'right'){
15259                 cfg.cn = [
15260                     {
15261                         tag: 'label',
15262                         'for' :  id,
15263                         cls : 'control-label col-form-label',
15264                         cn : [
15265                             {
15266                                 tag : 'span',
15267                                 html : this.fieldLabel
15268                             },
15269                             {
15270                                 tag : 'i',
15271                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15272                                 tooltip : 'This field is required'
15273                             }
15274                         ]
15275                     },
15276                     {
15277                         cls : "",
15278                         cn: [
15279                             combobox
15280                         ]
15281                     }
15282
15283                 ];
15284                 
15285                 labelCfg = cfg.cn[0];
15286                 contentCfg = cfg.cn[1];
15287             }
15288             
15289            
15290             
15291             if(this.labelWidth > 12){
15292                 labelCfg.style = "width: " + this.labelWidth + 'px';
15293             }
15294             
15295             if(this.labelWidth < 13 && this.labelmd == 0){
15296                 this.labelmd = this.labelWidth;
15297             }
15298             
15299             if(this.labellg > 0){
15300                 labelCfg.cls += ' col-lg-' + this.labellg;
15301                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15302             }
15303             
15304             if(this.labelmd > 0){
15305                 labelCfg.cls += ' col-md-' + this.labelmd;
15306                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15307             }
15308             
15309             if(this.labelsm > 0){
15310                 labelCfg.cls += ' col-sm-' + this.labelsm;
15311                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15312             }
15313             
15314             if(this.labelxs > 0){
15315                 labelCfg.cls += ' col-xs-' + this.labelxs;
15316                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15317             }
15318                 
15319                 
15320         } else if ( this.fieldLabel.length) {
15321             cfg.cn = [
15322                 {
15323                    tag : 'i',
15324                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15325                    tooltip : 'This field is required'
15326                 },
15327                 {
15328                     tag: 'label',
15329                     cls : 'control-label',
15330                     html : this.fieldLabel
15331
15332                 },
15333                 {
15334                     cls : '', 
15335                     cn: [
15336                         combobox
15337                     ]
15338                 }
15339             ];
15340             
15341             if(this.indicatorpos == 'right'){
15342                 cfg.cn = [
15343                     {
15344                         tag: 'label',
15345                         cls : 'control-label',
15346                         html : this.fieldLabel,
15347                         cn : [
15348                             {
15349                                tag : 'i',
15350                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15351                                tooltip : 'This field is required'
15352                             }
15353                         ]
15354                     },
15355                     {
15356                         cls : '', 
15357                         cn: [
15358                             combobox
15359                         ]
15360                     }
15361                 ];
15362             }
15363         } else {
15364             cfg.cn = combobox;    
15365         }
15366         
15367         
15368         var settings = this;
15369         
15370         ['xs','sm','md','lg'].map(function(size){
15371             if (settings[size]) {
15372                 cfg.cls += ' col-' + size + '-' + settings[size];
15373             }
15374         });
15375         
15376         return cfg;
15377     },
15378     
15379     initTouchView : function()
15380     {
15381         this.renderTouchView();
15382         
15383         this.touchViewEl.on('scroll', function(){
15384             this.el.dom.scrollTop = 0;
15385         }, this);
15386         
15387         this.originalValue = this.getValue();
15388         
15389         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15390         
15391         this.inputEl().on("click", this.showTouchView, this);
15392         if (this.triggerEl) {
15393             this.triggerEl.on("click", this.showTouchView, this);
15394         }
15395         
15396         
15397         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15398         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15399         
15400         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15401         
15402         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15403         this.store.on('load', this.onTouchViewLoad, this);
15404         this.store.on('loadexception', this.onTouchViewLoadException, this);
15405         
15406         if(this.hiddenName){
15407             
15408             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15409             
15410             this.hiddenField.dom.value =
15411                 this.hiddenValue !== undefined ? this.hiddenValue :
15412                 this.value !== undefined ? this.value : '';
15413         
15414             this.el.dom.removeAttribute('name');
15415             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15416         }
15417         
15418         if(this.multiple){
15419             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15420             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15421         }
15422         
15423         if(this.removable && !this.multiple){
15424             var close = this.closeTriggerEl();
15425             if(close){
15426                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15427                 close.on('click', this.removeBtnClick, this, close);
15428             }
15429         }
15430         /*
15431          * fix the bug in Safari iOS8
15432          */
15433         this.inputEl().on("focus", function(e){
15434             document.activeElement.blur();
15435         }, this);
15436         
15437         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15438         
15439         return;
15440         
15441         
15442     },
15443     
15444     renderTouchView : function()
15445     {
15446         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15447         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15448         
15449         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15450         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15451         
15452         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15453         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15454         this.touchViewBodyEl.setStyle('overflow', 'auto');
15455         
15456         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15457         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15458         
15459         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15460         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15461         
15462     },
15463     
15464     showTouchView : function()
15465     {
15466         if(this.disabled){
15467             return;
15468         }
15469         
15470         this.touchViewHeaderEl.hide();
15471
15472         if(this.modalTitle.length){
15473             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15474             this.touchViewHeaderEl.show();
15475         }
15476
15477         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15478         this.touchViewEl.show();
15479
15480         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15481         
15482         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15483         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15484
15485         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15486
15487         if(this.modalTitle.length){
15488             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15489         }
15490         
15491         this.touchViewBodyEl.setHeight(bodyHeight);
15492
15493         if(this.animate){
15494             var _this = this;
15495             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15496         }else{
15497             this.touchViewEl.addClass('in');
15498         }
15499         
15500         if(this._touchViewMask){
15501             Roo.get(document.body).addClass("x-body-masked");
15502             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15503             this._touchViewMask.setStyle('z-index', 10000);
15504             this._touchViewMask.addClass('show');
15505         }
15506         
15507         this.doTouchViewQuery();
15508         
15509     },
15510     
15511     hideTouchView : function()
15512     {
15513         this.touchViewEl.removeClass('in');
15514
15515         if(this.animate){
15516             var _this = this;
15517             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15518         }else{
15519             this.touchViewEl.setStyle('display', 'none');
15520         }
15521         
15522         if(this._touchViewMask){
15523             this._touchViewMask.removeClass('show');
15524             Roo.get(document.body).removeClass("x-body-masked");
15525         }
15526     },
15527     
15528     setTouchViewValue : function()
15529     {
15530         if(this.multiple){
15531             this.clearItem();
15532         
15533             var _this = this;
15534
15535             Roo.each(this.tickItems, function(o){
15536                 this.addItem(o);
15537             }, this);
15538         }
15539         
15540         this.hideTouchView();
15541     },
15542     
15543     doTouchViewQuery : function()
15544     {
15545         var qe = {
15546             query: '',
15547             forceAll: true,
15548             combo: this,
15549             cancel:false
15550         };
15551         
15552         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15553             return false;
15554         }
15555         
15556         if(!this.alwaysQuery || this.mode == 'local'){
15557             this.onTouchViewLoad();
15558             return;
15559         }
15560         
15561         this.store.load();
15562     },
15563     
15564     onTouchViewBeforeLoad : function(combo,opts)
15565     {
15566         return;
15567     },
15568
15569     // private
15570     onTouchViewLoad : function()
15571     {
15572         if(this.store.getCount() < 1){
15573             this.onTouchViewEmptyResults();
15574             return;
15575         }
15576         
15577         this.clearTouchView();
15578         
15579         var rawValue = this.getRawValue();
15580         
15581         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15582         
15583         this.tickItems = [];
15584         
15585         this.store.data.each(function(d, rowIndex){
15586             var row = this.touchViewListGroup.createChild(template);
15587             
15588             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15589                 row.addClass(d.data.cls);
15590             }
15591             
15592             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15593                 var cfg = {
15594                     data : d.data,
15595                     html : d.data[this.displayField]
15596                 };
15597                 
15598                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15599                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15600                 }
15601             }
15602             row.removeClass('selected');
15603             if(!this.multiple && this.valueField &&
15604                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15605             {
15606                 // radio buttons..
15607                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15608                 row.addClass('selected');
15609             }
15610             
15611             if(this.multiple && this.valueField &&
15612                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15613             {
15614                 
15615                 // checkboxes...
15616                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15617                 this.tickItems.push(d.data);
15618             }
15619             
15620             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15621             
15622         }, this);
15623         
15624         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15625         
15626         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15627
15628         if(this.modalTitle.length){
15629             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15630         }
15631
15632         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15633         
15634         if(this.mobile_restrict_height && listHeight < bodyHeight){
15635             this.touchViewBodyEl.setHeight(listHeight);
15636         }
15637         
15638         var _this = this;
15639         
15640         if(firstChecked && listHeight > bodyHeight){
15641             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15642         }
15643         
15644     },
15645     
15646     onTouchViewLoadException : function()
15647     {
15648         this.hideTouchView();
15649     },
15650     
15651     onTouchViewEmptyResults : function()
15652     {
15653         this.clearTouchView();
15654         
15655         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15656         
15657         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15658         
15659     },
15660     
15661     clearTouchView : function()
15662     {
15663         this.touchViewListGroup.dom.innerHTML = '';
15664     },
15665     
15666     onTouchViewClick : function(e, el, o)
15667     {
15668         e.preventDefault();
15669         
15670         var row = o.row;
15671         var rowIndex = o.rowIndex;
15672         
15673         var r = this.store.getAt(rowIndex);
15674         
15675         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15676             
15677             if(!this.multiple){
15678                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15679                     c.dom.removeAttribute('checked');
15680                 }, this);
15681
15682                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15683
15684                 this.setFromData(r.data);
15685
15686                 var close = this.closeTriggerEl();
15687
15688                 if(close){
15689                     close.show();
15690                 }
15691
15692                 this.hideTouchView();
15693
15694                 this.fireEvent('select', this, r, rowIndex);
15695
15696                 return;
15697             }
15698
15699             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15700                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15701                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15702                 return;
15703             }
15704
15705             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15706             this.addItem(r.data);
15707             this.tickItems.push(r.data);
15708         }
15709     },
15710     
15711     getAutoCreateNativeIOS : function()
15712     {
15713         var cfg = {
15714             cls: 'form-group' //input-group,
15715         };
15716         
15717         var combobox =  {
15718             tag: 'select',
15719             cls : 'roo-ios-select'
15720         };
15721         
15722         if (this.name) {
15723             combobox.name = this.name;
15724         }
15725         
15726         if (this.disabled) {
15727             combobox.disabled = true;
15728         }
15729         
15730         var settings = this;
15731         
15732         ['xs','sm','md','lg'].map(function(size){
15733             if (settings[size]) {
15734                 cfg.cls += ' col-' + size + '-' + settings[size];
15735             }
15736         });
15737         
15738         cfg.cn = combobox;
15739         
15740         return cfg;
15741         
15742     },
15743     
15744     initIOSView : function()
15745     {
15746         this.store.on('load', this.onIOSViewLoad, this);
15747         
15748         return;
15749     },
15750     
15751     onIOSViewLoad : function()
15752     {
15753         if(this.store.getCount() < 1){
15754             return;
15755         }
15756         
15757         this.clearIOSView();
15758         
15759         if(this.allowBlank) {
15760             
15761             var default_text = '-- SELECT --';
15762             
15763             if(this.placeholder.length){
15764                 default_text = this.placeholder;
15765             }
15766             
15767             if(this.emptyTitle.length){
15768                 default_text += ' - ' + this.emptyTitle + ' -';
15769             }
15770             
15771             var opt = this.inputEl().createChild({
15772                 tag: 'option',
15773                 value : 0,
15774                 html : default_text
15775             });
15776             
15777             var o = {};
15778             o[this.valueField] = 0;
15779             o[this.displayField] = default_text;
15780             
15781             this.ios_options.push({
15782                 data : o,
15783                 el : opt
15784             });
15785             
15786         }
15787         
15788         this.store.data.each(function(d, rowIndex){
15789             
15790             var html = '';
15791             
15792             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15793                 html = d.data[this.displayField];
15794             }
15795             
15796             var value = '';
15797             
15798             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15799                 value = d.data[this.valueField];
15800             }
15801             
15802             var option = {
15803                 tag: 'option',
15804                 value : value,
15805                 html : html
15806             };
15807             
15808             if(this.value == d.data[this.valueField]){
15809                 option['selected'] = true;
15810             }
15811             
15812             var opt = this.inputEl().createChild(option);
15813             
15814             this.ios_options.push({
15815                 data : d.data,
15816                 el : opt
15817             });
15818             
15819         }, this);
15820         
15821         this.inputEl().on('change', function(){
15822            this.fireEvent('select', this);
15823         }, this);
15824         
15825     },
15826     
15827     clearIOSView: function()
15828     {
15829         this.inputEl().dom.innerHTML = '';
15830         
15831         this.ios_options = [];
15832     },
15833     
15834     setIOSValue: function(v)
15835     {
15836         this.value = v;
15837         
15838         if(!this.ios_options){
15839             return;
15840         }
15841         
15842         Roo.each(this.ios_options, function(opts){
15843            
15844            opts.el.dom.removeAttribute('selected');
15845            
15846            if(opts.data[this.valueField] != v){
15847                return;
15848            }
15849            
15850            opts.el.dom.setAttribute('selected', true);
15851            
15852         }, this);
15853     }
15854
15855     /** 
15856     * @cfg {Boolean} grow 
15857     * @hide 
15858     */
15859     /** 
15860     * @cfg {Number} growMin 
15861     * @hide 
15862     */
15863     /** 
15864     * @cfg {Number} growMax 
15865     * @hide 
15866     */
15867     /**
15868      * @hide
15869      * @method autoSize
15870      */
15871 });
15872
15873 Roo.apply(Roo.bootstrap.ComboBox,  {
15874     
15875     header : {
15876         tag: 'div',
15877         cls: 'modal-header',
15878         cn: [
15879             {
15880                 tag: 'h4',
15881                 cls: 'modal-title'
15882             }
15883         ]
15884     },
15885     
15886     body : {
15887         tag: 'div',
15888         cls: 'modal-body',
15889         cn: [
15890             {
15891                 tag: 'ul',
15892                 cls: 'list-group'
15893             }
15894         ]
15895     },
15896     
15897     listItemRadio : {
15898         tag: 'li',
15899         cls: 'list-group-item',
15900         cn: [
15901             {
15902                 tag: 'span',
15903                 cls: 'roo-combobox-list-group-item-value'
15904             },
15905             {
15906                 tag: 'div',
15907                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15908                 cn: [
15909                     {
15910                         tag: 'input',
15911                         type: 'radio'
15912                     },
15913                     {
15914                         tag: 'label'
15915                     }
15916                 ]
15917             }
15918         ]
15919     },
15920     
15921     listItemCheckbox : {
15922         tag: 'li',
15923         cls: 'list-group-item',
15924         cn: [
15925             {
15926                 tag: 'span',
15927                 cls: 'roo-combobox-list-group-item-value'
15928             },
15929             {
15930                 tag: 'div',
15931                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15932                 cn: [
15933                     {
15934                         tag: 'input',
15935                         type: 'checkbox'
15936                     },
15937                     {
15938                         tag: 'label'
15939                     }
15940                 ]
15941             }
15942         ]
15943     },
15944     
15945     emptyResult : {
15946         tag: 'div',
15947         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15948     },
15949     
15950     footer : {
15951         tag: 'div',
15952         cls: 'modal-footer',
15953         cn: [
15954             {
15955                 tag: 'div',
15956                 cls: 'row',
15957                 cn: [
15958                     {
15959                         tag: 'div',
15960                         cls: 'col-xs-6 text-left',
15961                         cn: {
15962                             tag: 'button',
15963                             cls: 'btn btn-danger roo-touch-view-cancel',
15964                             html: 'Cancel'
15965                         }
15966                     },
15967                     {
15968                         tag: 'div',
15969                         cls: 'col-xs-6 text-right',
15970                         cn: {
15971                             tag: 'button',
15972                             cls: 'btn btn-success roo-touch-view-ok',
15973                             html: 'OK'
15974                         }
15975                     }
15976                 ]
15977             }
15978         ]
15979         
15980     }
15981 });
15982
15983 Roo.apply(Roo.bootstrap.ComboBox,  {
15984     
15985     touchViewTemplate : {
15986         tag: 'div',
15987         cls: 'modal fade roo-combobox-touch-view',
15988         cn: [
15989             {
15990                 tag: 'div',
15991                 cls: 'modal-dialog',
15992                 style : 'position:fixed', // we have to fix position....
15993                 cn: [
15994                     {
15995                         tag: 'div',
15996                         cls: 'modal-content',
15997                         cn: [
15998                             Roo.bootstrap.ComboBox.header,
15999                             Roo.bootstrap.ComboBox.body,
16000                             Roo.bootstrap.ComboBox.footer
16001                         ]
16002                     }
16003                 ]
16004             }
16005         ]
16006     }
16007 });/*
16008  * Based on:
16009  * Ext JS Library 1.1.1
16010  * Copyright(c) 2006-2007, Ext JS, LLC.
16011  *
16012  * Originally Released Under LGPL - original licence link has changed is not relivant.
16013  *
16014  * Fork - LGPL
16015  * <script type="text/javascript">
16016  */
16017
16018 /**
16019  * @class Roo.View
16020  * @extends Roo.util.Observable
16021  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16022  * This class also supports single and multi selection modes. <br>
16023  * Create a data model bound view:
16024  <pre><code>
16025  var store = new Roo.data.Store(...);
16026
16027  var view = new Roo.View({
16028     el : "my-element",
16029     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16030  
16031     singleSelect: true,
16032     selectedClass: "ydataview-selected",
16033     store: store
16034  });
16035
16036  // listen for node click?
16037  view.on("click", function(vw, index, node, e){
16038  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16039  });
16040
16041  // load XML data
16042  dataModel.load("foobar.xml");
16043  </code></pre>
16044  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16045  * <br><br>
16046  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16047  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16048  * 
16049  * Note: old style constructor is still suported (container, template, config)
16050  * 
16051  * @constructor
16052  * Create a new View
16053  * @param {Object} config The config object
16054  * 
16055  */
16056 Roo.View = function(config, depreciated_tpl, depreciated_config){
16057     
16058     this.parent = false;
16059     
16060     if (typeof(depreciated_tpl) == 'undefined') {
16061         // new way.. - universal constructor.
16062         Roo.apply(this, config);
16063         this.el  = Roo.get(this.el);
16064     } else {
16065         // old format..
16066         this.el  = Roo.get(config);
16067         this.tpl = depreciated_tpl;
16068         Roo.apply(this, depreciated_config);
16069     }
16070     this.wrapEl  = this.el.wrap().wrap();
16071     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16072     
16073     
16074     if(typeof(this.tpl) == "string"){
16075         this.tpl = new Roo.Template(this.tpl);
16076     } else {
16077         // support xtype ctors..
16078         this.tpl = new Roo.factory(this.tpl, Roo);
16079     }
16080     
16081     
16082     this.tpl.compile();
16083     
16084     /** @private */
16085     this.addEvents({
16086         /**
16087          * @event beforeclick
16088          * Fires before a click is processed. Returns false to cancel the default action.
16089          * @param {Roo.View} this
16090          * @param {Number} index The index of the target node
16091          * @param {HTMLElement} node The target node
16092          * @param {Roo.EventObject} e The raw event object
16093          */
16094             "beforeclick" : true,
16095         /**
16096          * @event click
16097          * Fires when a template node is clicked.
16098          * @param {Roo.View} this
16099          * @param {Number} index The index of the target node
16100          * @param {HTMLElement} node The target node
16101          * @param {Roo.EventObject} e The raw event object
16102          */
16103             "click" : true,
16104         /**
16105          * @event dblclick
16106          * Fires when a template node is double clicked.
16107          * @param {Roo.View} this
16108          * @param {Number} index The index of the target node
16109          * @param {HTMLElement} node The target node
16110          * @param {Roo.EventObject} e The raw event object
16111          */
16112             "dblclick" : true,
16113         /**
16114          * @event contextmenu
16115          * Fires when a template node is right clicked.
16116          * @param {Roo.View} this
16117          * @param {Number} index The index of the target node
16118          * @param {HTMLElement} node The target node
16119          * @param {Roo.EventObject} e The raw event object
16120          */
16121             "contextmenu" : true,
16122         /**
16123          * @event selectionchange
16124          * Fires when the selected nodes change.
16125          * @param {Roo.View} this
16126          * @param {Array} selections Array of the selected nodes
16127          */
16128             "selectionchange" : true,
16129     
16130         /**
16131          * @event beforeselect
16132          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16133          * @param {Roo.View} this
16134          * @param {HTMLElement} node The node to be selected
16135          * @param {Array} selections Array of currently selected nodes
16136          */
16137             "beforeselect" : true,
16138         /**
16139          * @event preparedata
16140          * Fires on every row to render, to allow you to change the data.
16141          * @param {Roo.View} this
16142          * @param {Object} data to be rendered (change this)
16143          */
16144           "preparedata" : true
16145           
16146           
16147         });
16148
16149
16150
16151     this.el.on({
16152         "click": this.onClick,
16153         "dblclick": this.onDblClick,
16154         "contextmenu": this.onContextMenu,
16155         scope:this
16156     });
16157
16158     this.selections = [];
16159     this.nodes = [];
16160     this.cmp = new Roo.CompositeElementLite([]);
16161     if(this.store){
16162         this.store = Roo.factory(this.store, Roo.data);
16163         this.setStore(this.store, true);
16164     }
16165     
16166     if ( this.footer && this.footer.xtype) {
16167            
16168          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16169         
16170         this.footer.dataSource = this.store;
16171         this.footer.container = fctr;
16172         this.footer = Roo.factory(this.footer, Roo);
16173         fctr.insertFirst(this.el);
16174         
16175         // this is a bit insane - as the paging toolbar seems to detach the el..
16176 //        dom.parentNode.parentNode.parentNode
16177          // they get detached?
16178     }
16179     
16180     
16181     Roo.View.superclass.constructor.call(this);
16182     
16183     
16184 };
16185
16186 Roo.extend(Roo.View, Roo.util.Observable, {
16187     
16188      /**
16189      * @cfg {Roo.data.Store} store Data store to load data from.
16190      */
16191     store : false,
16192     
16193     /**
16194      * @cfg {String|Roo.Element} el The container element.
16195      */
16196     el : '',
16197     
16198     /**
16199      * @cfg {String|Roo.Template} tpl The template used by this View 
16200      */
16201     tpl : false,
16202     /**
16203      * @cfg {String} dataName the named area of the template to use as the data area
16204      *                          Works with domtemplates roo-name="name"
16205      */
16206     dataName: false,
16207     /**
16208      * @cfg {String} selectedClass The css class to add to selected nodes
16209      */
16210     selectedClass : "x-view-selected",
16211      /**
16212      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16213      */
16214     emptyText : "",
16215     
16216     /**
16217      * @cfg {String} text to display on mask (default Loading)
16218      */
16219     mask : false,
16220     /**
16221      * @cfg {Boolean} multiSelect Allow multiple selection
16222      */
16223     multiSelect : false,
16224     /**
16225      * @cfg {Boolean} singleSelect Allow single selection
16226      */
16227     singleSelect:  false,
16228     
16229     /**
16230      * @cfg {Boolean} toggleSelect - selecting 
16231      */
16232     toggleSelect : false,
16233     
16234     /**
16235      * @cfg {Boolean} tickable - selecting 
16236      */
16237     tickable : false,
16238     
16239     /**
16240      * Returns the element this view is bound to.
16241      * @return {Roo.Element}
16242      */
16243     getEl : function(){
16244         return this.wrapEl;
16245     },
16246     
16247     
16248
16249     /**
16250      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16251      */
16252     refresh : function(){
16253         //Roo.log('refresh');
16254         var t = this.tpl;
16255         
16256         // if we are using something like 'domtemplate', then
16257         // the what gets used is:
16258         // t.applySubtemplate(NAME, data, wrapping data..)
16259         // the outer template then get' applied with
16260         //     the store 'extra data'
16261         // and the body get's added to the
16262         //      roo-name="data" node?
16263         //      <span class='roo-tpl-{name}'></span> ?????
16264         
16265         
16266         
16267         this.clearSelections();
16268         this.el.update("");
16269         var html = [];
16270         var records = this.store.getRange();
16271         if(records.length < 1) {
16272             
16273             // is this valid??  = should it render a template??
16274             
16275             this.el.update(this.emptyText);
16276             return;
16277         }
16278         var el = this.el;
16279         if (this.dataName) {
16280             this.el.update(t.apply(this.store.meta)); //????
16281             el = this.el.child('.roo-tpl-' + this.dataName);
16282         }
16283         
16284         for(var i = 0, len = records.length; i < len; i++){
16285             var data = this.prepareData(records[i].data, i, records[i]);
16286             this.fireEvent("preparedata", this, data, i, records[i]);
16287             
16288             var d = Roo.apply({}, data);
16289             
16290             if(this.tickable){
16291                 Roo.apply(d, {'roo-id' : Roo.id()});
16292                 
16293                 var _this = this;
16294             
16295                 Roo.each(this.parent.item, function(item){
16296                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16297                         return;
16298                     }
16299                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16300                 });
16301             }
16302             
16303             html[html.length] = Roo.util.Format.trim(
16304                 this.dataName ?
16305                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16306                     t.apply(d)
16307             );
16308         }
16309         
16310         
16311         
16312         el.update(html.join(""));
16313         this.nodes = el.dom.childNodes;
16314         this.updateIndexes(0);
16315     },
16316     
16317
16318     /**
16319      * Function to override to reformat the data that is sent to
16320      * the template for each node.
16321      * DEPRICATED - use the preparedata event handler.
16322      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16323      * a JSON object for an UpdateManager bound view).
16324      */
16325     prepareData : function(data, index, record)
16326     {
16327         this.fireEvent("preparedata", this, data, index, record);
16328         return data;
16329     },
16330
16331     onUpdate : function(ds, record){
16332         // Roo.log('on update');   
16333         this.clearSelections();
16334         var index = this.store.indexOf(record);
16335         var n = this.nodes[index];
16336         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16337         n.parentNode.removeChild(n);
16338         this.updateIndexes(index, index);
16339     },
16340
16341     
16342     
16343 // --------- FIXME     
16344     onAdd : function(ds, records, index)
16345     {
16346         //Roo.log(['on Add', ds, records, index] );        
16347         this.clearSelections();
16348         if(this.nodes.length == 0){
16349             this.refresh();
16350             return;
16351         }
16352         var n = this.nodes[index];
16353         for(var i = 0, len = records.length; i < len; i++){
16354             var d = this.prepareData(records[i].data, i, records[i]);
16355             if(n){
16356                 this.tpl.insertBefore(n, d);
16357             }else{
16358                 
16359                 this.tpl.append(this.el, d);
16360             }
16361         }
16362         this.updateIndexes(index);
16363     },
16364
16365     onRemove : function(ds, record, index){
16366        // Roo.log('onRemove');
16367         this.clearSelections();
16368         var el = this.dataName  ?
16369             this.el.child('.roo-tpl-' + this.dataName) :
16370             this.el; 
16371         
16372         el.dom.removeChild(this.nodes[index]);
16373         this.updateIndexes(index);
16374     },
16375
16376     /**
16377      * Refresh an individual node.
16378      * @param {Number} index
16379      */
16380     refreshNode : function(index){
16381         this.onUpdate(this.store, this.store.getAt(index));
16382     },
16383
16384     updateIndexes : function(startIndex, endIndex){
16385         var ns = this.nodes;
16386         startIndex = startIndex || 0;
16387         endIndex = endIndex || ns.length - 1;
16388         for(var i = startIndex; i <= endIndex; i++){
16389             ns[i].nodeIndex = i;
16390         }
16391     },
16392
16393     /**
16394      * Changes the data store this view uses and refresh the view.
16395      * @param {Store} store
16396      */
16397     setStore : function(store, initial){
16398         if(!initial && this.store){
16399             this.store.un("datachanged", this.refresh);
16400             this.store.un("add", this.onAdd);
16401             this.store.un("remove", this.onRemove);
16402             this.store.un("update", this.onUpdate);
16403             this.store.un("clear", this.refresh);
16404             this.store.un("beforeload", this.onBeforeLoad);
16405             this.store.un("load", this.onLoad);
16406             this.store.un("loadexception", this.onLoad);
16407         }
16408         if(store){
16409           
16410             store.on("datachanged", this.refresh, this);
16411             store.on("add", this.onAdd, this);
16412             store.on("remove", this.onRemove, this);
16413             store.on("update", this.onUpdate, this);
16414             store.on("clear", this.refresh, this);
16415             store.on("beforeload", this.onBeforeLoad, this);
16416             store.on("load", this.onLoad, this);
16417             store.on("loadexception", this.onLoad, this);
16418         }
16419         
16420         if(store){
16421             this.refresh();
16422         }
16423     },
16424     /**
16425      * onbeforeLoad - masks the loading area.
16426      *
16427      */
16428     onBeforeLoad : function(store,opts)
16429     {
16430          //Roo.log('onBeforeLoad');   
16431         if (!opts.add) {
16432             this.el.update("");
16433         }
16434         this.el.mask(this.mask ? this.mask : "Loading" ); 
16435     },
16436     onLoad : function ()
16437     {
16438         this.el.unmask();
16439     },
16440     
16441
16442     /**
16443      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16444      * @param {HTMLElement} node
16445      * @return {HTMLElement} The template node
16446      */
16447     findItemFromChild : function(node){
16448         var el = this.dataName  ?
16449             this.el.child('.roo-tpl-' + this.dataName,true) :
16450             this.el.dom; 
16451         
16452         if(!node || node.parentNode == el){
16453                     return node;
16454             }
16455             var p = node.parentNode;
16456             while(p && p != el){
16457             if(p.parentNode == el){
16458                 return p;
16459             }
16460             p = p.parentNode;
16461         }
16462             return null;
16463     },
16464
16465     /** @ignore */
16466     onClick : function(e){
16467         var item = this.findItemFromChild(e.getTarget());
16468         if(item){
16469             var index = this.indexOf(item);
16470             if(this.onItemClick(item, index, e) !== false){
16471                 this.fireEvent("click", this, index, item, e);
16472             }
16473         }else{
16474             this.clearSelections();
16475         }
16476     },
16477
16478     /** @ignore */
16479     onContextMenu : function(e){
16480         var item = this.findItemFromChild(e.getTarget());
16481         if(item){
16482             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16483         }
16484     },
16485
16486     /** @ignore */
16487     onDblClick : function(e){
16488         var item = this.findItemFromChild(e.getTarget());
16489         if(item){
16490             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16491         }
16492     },
16493
16494     onItemClick : function(item, index, e)
16495     {
16496         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16497             return false;
16498         }
16499         if (this.toggleSelect) {
16500             var m = this.isSelected(item) ? 'unselect' : 'select';
16501             //Roo.log(m);
16502             var _t = this;
16503             _t[m](item, true, false);
16504             return true;
16505         }
16506         if(this.multiSelect || this.singleSelect){
16507             if(this.multiSelect && e.shiftKey && this.lastSelection){
16508                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16509             }else{
16510                 this.select(item, this.multiSelect && e.ctrlKey);
16511                 this.lastSelection = item;
16512             }
16513             
16514             if(!this.tickable){
16515                 e.preventDefault();
16516             }
16517             
16518         }
16519         return true;
16520     },
16521
16522     /**
16523      * Get the number of selected nodes.
16524      * @return {Number}
16525      */
16526     getSelectionCount : function(){
16527         return this.selections.length;
16528     },
16529
16530     /**
16531      * Get the currently selected nodes.
16532      * @return {Array} An array of HTMLElements
16533      */
16534     getSelectedNodes : function(){
16535         return this.selections;
16536     },
16537
16538     /**
16539      * Get the indexes of the selected nodes.
16540      * @return {Array}
16541      */
16542     getSelectedIndexes : function(){
16543         var indexes = [], s = this.selections;
16544         for(var i = 0, len = s.length; i < len; i++){
16545             indexes.push(s[i].nodeIndex);
16546         }
16547         return indexes;
16548     },
16549
16550     /**
16551      * Clear all selections
16552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16553      */
16554     clearSelections : function(suppressEvent){
16555         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16556             this.cmp.elements = this.selections;
16557             this.cmp.removeClass(this.selectedClass);
16558             this.selections = [];
16559             if(!suppressEvent){
16560                 this.fireEvent("selectionchange", this, this.selections);
16561             }
16562         }
16563     },
16564
16565     /**
16566      * Returns true if the passed node is selected
16567      * @param {HTMLElement/Number} node The node or node index
16568      * @return {Boolean}
16569      */
16570     isSelected : function(node){
16571         var s = this.selections;
16572         if(s.length < 1){
16573             return false;
16574         }
16575         node = this.getNode(node);
16576         return s.indexOf(node) !== -1;
16577     },
16578
16579     /**
16580      * Selects nodes.
16581      * @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
16582      * @param {Boolean} keepExisting (optional) true to keep existing selections
16583      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16584      */
16585     select : function(nodeInfo, keepExisting, suppressEvent){
16586         if(nodeInfo instanceof Array){
16587             if(!keepExisting){
16588                 this.clearSelections(true);
16589             }
16590             for(var i = 0, len = nodeInfo.length; i < len; i++){
16591                 this.select(nodeInfo[i], true, true);
16592             }
16593             return;
16594         } 
16595         var node = this.getNode(nodeInfo);
16596         if(!node || this.isSelected(node)){
16597             return; // already selected.
16598         }
16599         if(!keepExisting){
16600             this.clearSelections(true);
16601         }
16602         
16603         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16604             Roo.fly(node).addClass(this.selectedClass);
16605             this.selections.push(node);
16606             if(!suppressEvent){
16607                 this.fireEvent("selectionchange", this, this.selections);
16608             }
16609         }
16610         
16611         
16612     },
16613       /**
16614      * Unselects nodes.
16615      * @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
16616      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16617      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16618      */
16619     unselect : function(nodeInfo, keepExisting, suppressEvent)
16620     {
16621         if(nodeInfo instanceof Array){
16622             Roo.each(this.selections, function(s) {
16623                 this.unselect(s, nodeInfo);
16624             }, this);
16625             return;
16626         }
16627         var node = this.getNode(nodeInfo);
16628         if(!node || !this.isSelected(node)){
16629             //Roo.log("not selected");
16630             return; // not selected.
16631         }
16632         // fireevent???
16633         var ns = [];
16634         Roo.each(this.selections, function(s) {
16635             if (s == node ) {
16636                 Roo.fly(node).removeClass(this.selectedClass);
16637
16638                 return;
16639             }
16640             ns.push(s);
16641         },this);
16642         
16643         this.selections= ns;
16644         this.fireEvent("selectionchange", this, this.selections);
16645     },
16646
16647     /**
16648      * Gets a template node.
16649      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16650      * @return {HTMLElement} The node or null if it wasn't found
16651      */
16652     getNode : function(nodeInfo){
16653         if(typeof nodeInfo == "string"){
16654             return document.getElementById(nodeInfo);
16655         }else if(typeof nodeInfo == "number"){
16656             return this.nodes[nodeInfo];
16657         }
16658         return nodeInfo;
16659     },
16660
16661     /**
16662      * Gets a range template nodes.
16663      * @param {Number} startIndex
16664      * @param {Number} endIndex
16665      * @return {Array} An array of nodes
16666      */
16667     getNodes : function(start, end){
16668         var ns = this.nodes;
16669         start = start || 0;
16670         end = typeof end == "undefined" ? ns.length - 1 : end;
16671         var nodes = [];
16672         if(start <= end){
16673             for(var i = start; i <= end; i++){
16674                 nodes.push(ns[i]);
16675             }
16676         } else{
16677             for(var i = start; i >= end; i--){
16678                 nodes.push(ns[i]);
16679             }
16680         }
16681         return nodes;
16682     },
16683
16684     /**
16685      * Finds the index of the passed node
16686      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16687      * @return {Number} The index of the node or -1
16688      */
16689     indexOf : function(node){
16690         node = this.getNode(node);
16691         if(typeof node.nodeIndex == "number"){
16692             return node.nodeIndex;
16693         }
16694         var ns = this.nodes;
16695         for(var i = 0, len = ns.length; i < len; i++){
16696             if(ns[i] == node){
16697                 return i;
16698             }
16699         }
16700         return -1;
16701     }
16702 });
16703 /*
16704  * - LGPL
16705  *
16706  * based on jquery fullcalendar
16707  * 
16708  */
16709
16710 Roo.bootstrap = Roo.bootstrap || {};
16711 /**
16712  * @class Roo.bootstrap.Calendar
16713  * @extends Roo.bootstrap.Component
16714  * Bootstrap Calendar class
16715  * @cfg {Boolean} loadMask (true|false) default false
16716  * @cfg {Object} header generate the user specific header of the calendar, default false
16717
16718  * @constructor
16719  * Create a new Container
16720  * @param {Object} config The config object
16721  */
16722
16723
16724
16725 Roo.bootstrap.Calendar = function(config){
16726     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16727      this.addEvents({
16728         /**
16729              * @event select
16730              * Fires when a date is selected
16731              * @param {DatePicker} this
16732              * @param {Date} date The selected date
16733              */
16734         'select': true,
16735         /**
16736              * @event monthchange
16737              * Fires when the displayed month changes 
16738              * @param {DatePicker} this
16739              * @param {Date} date The selected month
16740              */
16741         'monthchange': true,
16742         /**
16743              * @event evententer
16744              * Fires when mouse over an event
16745              * @param {Calendar} this
16746              * @param {event} Event
16747              */
16748         'evententer': true,
16749         /**
16750              * @event eventleave
16751              * Fires when the mouse leaves an
16752              * @param {Calendar} this
16753              * @param {event}
16754              */
16755         'eventleave': true,
16756         /**
16757              * @event eventclick
16758              * Fires when the mouse click an
16759              * @param {Calendar} this
16760              * @param {event}
16761              */
16762         'eventclick': true
16763         
16764     });
16765
16766 };
16767
16768 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16769     
16770      /**
16771      * @cfg {Number} startDay
16772      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16773      */
16774     startDay : 0,
16775     
16776     loadMask : false,
16777     
16778     header : false,
16779       
16780     getAutoCreate : function(){
16781         
16782         
16783         var fc_button = function(name, corner, style, content ) {
16784             return Roo.apply({},{
16785                 tag : 'span',
16786                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16787                          (corner.length ?
16788                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16789                             ''
16790                         ),
16791                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16792                 unselectable: 'on'
16793             });
16794         };
16795         
16796         var header = {};
16797         
16798         if(!this.header){
16799             header = {
16800                 tag : 'table',
16801                 cls : 'fc-header',
16802                 style : 'width:100%',
16803                 cn : [
16804                     {
16805                         tag: 'tr',
16806                         cn : [
16807                             {
16808                                 tag : 'td',
16809                                 cls : 'fc-header-left',
16810                                 cn : [
16811                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16812                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16813                                     { tag: 'span', cls: 'fc-header-space' },
16814                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16815
16816
16817                                 ]
16818                             },
16819
16820                             {
16821                                 tag : 'td',
16822                                 cls : 'fc-header-center',
16823                                 cn : [
16824                                     {
16825                                         tag: 'span',
16826                                         cls: 'fc-header-title',
16827                                         cn : {
16828                                             tag: 'H2',
16829                                             html : 'month / year'
16830                                         }
16831                                     }
16832
16833                                 ]
16834                             },
16835                             {
16836                                 tag : 'td',
16837                                 cls : 'fc-header-right',
16838                                 cn : [
16839                               /*      fc_button('month', 'left', '', 'month' ),
16840                                     fc_button('week', '', '', 'week' ),
16841                                     fc_button('day', 'right', '', 'day' )
16842                                 */    
16843
16844                                 ]
16845                             }
16846
16847                         ]
16848                     }
16849                 ]
16850             };
16851         }
16852         
16853         header = this.header;
16854         
16855        
16856         var cal_heads = function() {
16857             var ret = [];
16858             // fixme - handle this.
16859             
16860             for (var i =0; i < Date.dayNames.length; i++) {
16861                 var d = Date.dayNames[i];
16862                 ret.push({
16863                     tag: 'th',
16864                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16865                     html : d.substring(0,3)
16866                 });
16867                 
16868             }
16869             ret[0].cls += ' fc-first';
16870             ret[6].cls += ' fc-last';
16871             return ret;
16872         };
16873         var cal_cell = function(n) {
16874             return  {
16875                 tag: 'td',
16876                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16877                 cn : [
16878                     {
16879                         cn : [
16880                             {
16881                                 cls: 'fc-day-number',
16882                                 html: 'D'
16883                             },
16884                             {
16885                                 cls: 'fc-day-content',
16886                              
16887                                 cn : [
16888                                      {
16889                                         style: 'position: relative;' // height: 17px;
16890                                     }
16891                                 ]
16892                             }
16893                             
16894                             
16895                         ]
16896                     }
16897                 ]
16898                 
16899             }
16900         };
16901         var cal_rows = function() {
16902             
16903             var ret = [];
16904             for (var r = 0; r < 6; r++) {
16905                 var row= {
16906                     tag : 'tr',
16907                     cls : 'fc-week',
16908                     cn : []
16909                 };
16910                 
16911                 for (var i =0; i < Date.dayNames.length; i++) {
16912                     var d = Date.dayNames[i];
16913                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16914
16915                 }
16916                 row.cn[0].cls+=' fc-first';
16917                 row.cn[0].cn[0].style = 'min-height:90px';
16918                 row.cn[6].cls+=' fc-last';
16919                 ret.push(row);
16920                 
16921             }
16922             ret[0].cls += ' fc-first';
16923             ret[4].cls += ' fc-prev-last';
16924             ret[5].cls += ' fc-last';
16925             return ret;
16926             
16927         };
16928         
16929         var cal_table = {
16930             tag: 'table',
16931             cls: 'fc-border-separate',
16932             style : 'width:100%',
16933             cellspacing  : 0,
16934             cn : [
16935                 { 
16936                     tag: 'thead',
16937                     cn : [
16938                         { 
16939                             tag: 'tr',
16940                             cls : 'fc-first fc-last',
16941                             cn : cal_heads()
16942                         }
16943                     ]
16944                 },
16945                 { 
16946                     tag: 'tbody',
16947                     cn : cal_rows()
16948                 }
16949                   
16950             ]
16951         };
16952          
16953          var cfg = {
16954             cls : 'fc fc-ltr',
16955             cn : [
16956                 header,
16957                 {
16958                     cls : 'fc-content',
16959                     style : "position: relative;",
16960                     cn : [
16961                         {
16962                             cls : 'fc-view fc-view-month fc-grid',
16963                             style : 'position: relative',
16964                             unselectable : 'on',
16965                             cn : [
16966                                 {
16967                                     cls : 'fc-event-container',
16968                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16969                                 },
16970                                 cal_table
16971                             ]
16972                         }
16973                     ]
16974     
16975                 }
16976            ] 
16977             
16978         };
16979         
16980          
16981         
16982         return cfg;
16983     },
16984     
16985     
16986     initEvents : function()
16987     {
16988         if(!this.store){
16989             throw "can not find store for calendar";
16990         }
16991         
16992         var mark = {
16993             tag: "div",
16994             cls:"x-dlg-mask",
16995             style: "text-align:center",
16996             cn: [
16997                 {
16998                     tag: "div",
16999                     style: "background-color:white;width:50%;margin:250 auto",
17000                     cn: [
17001                         {
17002                             tag: "img",
17003                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17004                         },
17005                         {
17006                             tag: "span",
17007                             html: "Loading"
17008                         }
17009                         
17010                     ]
17011                 }
17012             ]
17013         };
17014         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17015         
17016         var size = this.el.select('.fc-content', true).first().getSize();
17017         this.maskEl.setSize(size.width, size.height);
17018         this.maskEl.enableDisplayMode("block");
17019         if(!this.loadMask){
17020             this.maskEl.hide();
17021         }
17022         
17023         this.store = Roo.factory(this.store, Roo.data);
17024         this.store.on('load', this.onLoad, this);
17025         this.store.on('beforeload', this.onBeforeLoad, this);
17026         
17027         this.resize();
17028         
17029         this.cells = this.el.select('.fc-day',true);
17030         //Roo.log(this.cells);
17031         this.textNodes = this.el.query('.fc-day-number');
17032         this.cells.addClassOnOver('fc-state-hover');
17033         
17034         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17035         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17036         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17037         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17038         
17039         this.on('monthchange', this.onMonthChange, this);
17040         
17041         this.update(new Date().clearTime());
17042     },
17043     
17044     resize : function() {
17045         var sz  = this.el.getSize();
17046         
17047         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17048         this.el.select('.fc-day-content div',true).setHeight(34);
17049     },
17050     
17051     
17052     // private
17053     showPrevMonth : function(e){
17054         this.update(this.activeDate.add("mo", -1));
17055     },
17056     showToday : function(e){
17057         this.update(new Date().clearTime());
17058     },
17059     // private
17060     showNextMonth : function(e){
17061         this.update(this.activeDate.add("mo", 1));
17062     },
17063
17064     // private
17065     showPrevYear : function(){
17066         this.update(this.activeDate.add("y", -1));
17067     },
17068
17069     // private
17070     showNextYear : function(){
17071         this.update(this.activeDate.add("y", 1));
17072     },
17073
17074     
17075    // private
17076     update : function(date)
17077     {
17078         var vd = this.activeDate;
17079         this.activeDate = date;
17080 //        if(vd && this.el){
17081 //            var t = date.getTime();
17082 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17083 //                Roo.log('using add remove');
17084 //                
17085 //                this.fireEvent('monthchange', this, date);
17086 //                
17087 //                this.cells.removeClass("fc-state-highlight");
17088 //                this.cells.each(function(c){
17089 //                   if(c.dateValue == t){
17090 //                       c.addClass("fc-state-highlight");
17091 //                       setTimeout(function(){
17092 //                            try{c.dom.firstChild.focus();}catch(e){}
17093 //                       }, 50);
17094 //                       return false;
17095 //                   }
17096 //                   return true;
17097 //                });
17098 //                return;
17099 //            }
17100 //        }
17101         
17102         var days = date.getDaysInMonth();
17103         
17104         var firstOfMonth = date.getFirstDateOfMonth();
17105         var startingPos = firstOfMonth.getDay()-this.startDay;
17106         
17107         if(startingPos < this.startDay){
17108             startingPos += 7;
17109         }
17110         
17111         var pm = date.add(Date.MONTH, -1);
17112         var prevStart = pm.getDaysInMonth()-startingPos;
17113 //        
17114         this.cells = this.el.select('.fc-day',true);
17115         this.textNodes = this.el.query('.fc-day-number');
17116         this.cells.addClassOnOver('fc-state-hover');
17117         
17118         var cells = this.cells.elements;
17119         var textEls = this.textNodes;
17120         
17121         Roo.each(cells, function(cell){
17122             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17123         });
17124         
17125         days += startingPos;
17126
17127         // convert everything to numbers so it's fast
17128         var day = 86400000;
17129         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17130         //Roo.log(d);
17131         //Roo.log(pm);
17132         //Roo.log(prevStart);
17133         
17134         var today = new Date().clearTime().getTime();
17135         var sel = date.clearTime().getTime();
17136         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17137         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17138         var ddMatch = this.disabledDatesRE;
17139         var ddText = this.disabledDatesText;
17140         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17141         var ddaysText = this.disabledDaysText;
17142         var format = this.format;
17143         
17144         var setCellClass = function(cal, cell){
17145             cell.row = 0;
17146             cell.events = [];
17147             cell.more = [];
17148             //Roo.log('set Cell Class');
17149             cell.title = "";
17150             var t = d.getTime();
17151             
17152             //Roo.log(d);
17153             
17154             cell.dateValue = t;
17155             if(t == today){
17156                 cell.className += " fc-today";
17157                 cell.className += " fc-state-highlight";
17158                 cell.title = cal.todayText;
17159             }
17160             if(t == sel){
17161                 // disable highlight in other month..
17162                 //cell.className += " fc-state-highlight";
17163                 
17164             }
17165             // disabling
17166             if(t < min) {
17167                 cell.className = " fc-state-disabled";
17168                 cell.title = cal.minText;
17169                 return;
17170             }
17171             if(t > max) {
17172                 cell.className = " fc-state-disabled";
17173                 cell.title = cal.maxText;
17174                 return;
17175             }
17176             if(ddays){
17177                 if(ddays.indexOf(d.getDay()) != -1){
17178                     cell.title = ddaysText;
17179                     cell.className = " fc-state-disabled";
17180                 }
17181             }
17182             if(ddMatch && format){
17183                 var fvalue = d.dateFormat(format);
17184                 if(ddMatch.test(fvalue)){
17185                     cell.title = ddText.replace("%0", fvalue);
17186                     cell.className = " fc-state-disabled";
17187                 }
17188             }
17189             
17190             if (!cell.initialClassName) {
17191                 cell.initialClassName = cell.dom.className;
17192             }
17193             
17194             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17195         };
17196
17197         var i = 0;
17198         
17199         for(; i < startingPos; i++) {
17200             textEls[i].innerHTML = (++prevStart);
17201             d.setDate(d.getDate()+1);
17202             
17203             cells[i].className = "fc-past fc-other-month";
17204             setCellClass(this, cells[i]);
17205         }
17206         
17207         var intDay = 0;
17208         
17209         for(; i < days; i++){
17210             intDay = i - startingPos + 1;
17211             textEls[i].innerHTML = (intDay);
17212             d.setDate(d.getDate()+1);
17213             
17214             cells[i].className = ''; // "x-date-active";
17215             setCellClass(this, cells[i]);
17216         }
17217         var extraDays = 0;
17218         
17219         for(; i < 42; i++) {
17220             textEls[i].innerHTML = (++extraDays);
17221             d.setDate(d.getDate()+1);
17222             
17223             cells[i].className = "fc-future fc-other-month";
17224             setCellClass(this, cells[i]);
17225         }
17226         
17227         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17228         
17229         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17230         
17231         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17232         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17233         
17234         if(totalRows != 6){
17235             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17236             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17237         }
17238         
17239         this.fireEvent('monthchange', this, date);
17240         
17241         
17242         /*
17243         if(!this.internalRender){
17244             var main = this.el.dom.firstChild;
17245             var w = main.offsetWidth;
17246             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17247             Roo.fly(main).setWidth(w);
17248             this.internalRender = true;
17249             // opera does not respect the auto grow header center column
17250             // then, after it gets a width opera refuses to recalculate
17251             // without a second pass
17252             if(Roo.isOpera && !this.secondPass){
17253                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17254                 this.secondPass = true;
17255                 this.update.defer(10, this, [date]);
17256             }
17257         }
17258         */
17259         
17260     },
17261     
17262     findCell : function(dt) {
17263         dt = dt.clearTime().getTime();
17264         var ret = false;
17265         this.cells.each(function(c){
17266             //Roo.log("check " +c.dateValue + '?=' + dt);
17267             if(c.dateValue == dt){
17268                 ret = c;
17269                 return false;
17270             }
17271             return true;
17272         });
17273         
17274         return ret;
17275     },
17276     
17277     findCells : function(ev) {
17278         var s = ev.start.clone().clearTime().getTime();
17279        // Roo.log(s);
17280         var e= ev.end.clone().clearTime().getTime();
17281        // Roo.log(e);
17282         var ret = [];
17283         this.cells.each(function(c){
17284              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17285             
17286             if(c.dateValue > e){
17287                 return ;
17288             }
17289             if(c.dateValue < s){
17290                 return ;
17291             }
17292             ret.push(c);
17293         });
17294         
17295         return ret;    
17296     },
17297     
17298 //    findBestRow: function(cells)
17299 //    {
17300 //        var ret = 0;
17301 //        
17302 //        for (var i =0 ; i < cells.length;i++) {
17303 //            ret  = Math.max(cells[i].rows || 0,ret);
17304 //        }
17305 //        return ret;
17306 //        
17307 //    },
17308     
17309     
17310     addItem : function(ev)
17311     {
17312         // look for vertical location slot in
17313         var cells = this.findCells(ev);
17314         
17315 //        ev.row = this.findBestRow(cells);
17316         
17317         // work out the location.
17318         
17319         var crow = false;
17320         var rows = [];
17321         for(var i =0; i < cells.length; i++) {
17322             
17323             cells[i].row = cells[0].row;
17324             
17325             if(i == 0){
17326                 cells[i].row = cells[i].row + 1;
17327             }
17328             
17329             if (!crow) {
17330                 crow = {
17331                     start : cells[i],
17332                     end :  cells[i]
17333                 };
17334                 continue;
17335             }
17336             if (crow.start.getY() == cells[i].getY()) {
17337                 // on same row.
17338                 crow.end = cells[i];
17339                 continue;
17340             }
17341             // different row.
17342             rows.push(crow);
17343             crow = {
17344                 start: cells[i],
17345                 end : cells[i]
17346             };
17347             
17348         }
17349         
17350         rows.push(crow);
17351         ev.els = [];
17352         ev.rows = rows;
17353         ev.cells = cells;
17354         
17355         cells[0].events.push(ev);
17356         
17357         this.calevents.push(ev);
17358     },
17359     
17360     clearEvents: function() {
17361         
17362         if(!this.calevents){
17363             return;
17364         }
17365         
17366         Roo.each(this.cells.elements, function(c){
17367             c.row = 0;
17368             c.events = [];
17369             c.more = [];
17370         });
17371         
17372         Roo.each(this.calevents, function(e) {
17373             Roo.each(e.els, function(el) {
17374                 el.un('mouseenter' ,this.onEventEnter, this);
17375                 el.un('mouseleave' ,this.onEventLeave, this);
17376                 el.remove();
17377             },this);
17378         },this);
17379         
17380         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17381             e.remove();
17382         });
17383         
17384     },
17385     
17386     renderEvents: function()
17387     {   
17388         var _this = this;
17389         
17390         this.cells.each(function(c) {
17391             
17392             if(c.row < 5){
17393                 return;
17394             }
17395             
17396             var ev = c.events;
17397             
17398             var r = 4;
17399             if(c.row != c.events.length){
17400                 r = 4 - (4 - (c.row - c.events.length));
17401             }
17402             
17403             c.events = ev.slice(0, r);
17404             c.more = ev.slice(r);
17405             
17406             if(c.more.length && c.more.length == 1){
17407                 c.events.push(c.more.pop());
17408             }
17409             
17410             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17411             
17412         });
17413             
17414         this.cells.each(function(c) {
17415             
17416             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17417             
17418             
17419             for (var e = 0; e < c.events.length; e++){
17420                 var ev = c.events[e];
17421                 var rows = ev.rows;
17422                 
17423                 for(var i = 0; i < rows.length; i++) {
17424                 
17425                     // how many rows should it span..
17426
17427                     var  cfg = {
17428                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17429                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17430
17431                         unselectable : "on",
17432                         cn : [
17433                             {
17434                                 cls: 'fc-event-inner',
17435                                 cn : [
17436     //                                {
17437     //                                  tag:'span',
17438     //                                  cls: 'fc-event-time',
17439     //                                  html : cells.length > 1 ? '' : ev.time
17440     //                                },
17441                                     {
17442                                       tag:'span',
17443                                       cls: 'fc-event-title',
17444                                       html : String.format('{0}', ev.title)
17445                                     }
17446
17447
17448                                 ]
17449                             },
17450                             {
17451                                 cls: 'ui-resizable-handle ui-resizable-e',
17452                                 html : '&nbsp;&nbsp;&nbsp'
17453                             }
17454
17455                         ]
17456                     };
17457
17458                     if (i == 0) {
17459                         cfg.cls += ' fc-event-start';
17460                     }
17461                     if ((i+1) == rows.length) {
17462                         cfg.cls += ' fc-event-end';
17463                     }
17464
17465                     var ctr = _this.el.select('.fc-event-container',true).first();
17466                     var cg = ctr.createChild(cfg);
17467
17468                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17469                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17470
17471                     var r = (c.more.length) ? 1 : 0;
17472                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17473                     cg.setWidth(ebox.right - sbox.x -2);
17474
17475                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17476                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17477                     cg.on('click', _this.onEventClick, _this, ev);
17478
17479                     ev.els.push(cg);
17480                     
17481                 }
17482                 
17483             }
17484             
17485             
17486             if(c.more.length){
17487                 var  cfg = {
17488                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17489                     style : 'position: absolute',
17490                     unselectable : "on",
17491                     cn : [
17492                         {
17493                             cls: 'fc-event-inner',
17494                             cn : [
17495                                 {
17496                                   tag:'span',
17497                                   cls: 'fc-event-title',
17498                                   html : 'More'
17499                                 }
17500
17501
17502                             ]
17503                         },
17504                         {
17505                             cls: 'ui-resizable-handle ui-resizable-e',
17506                             html : '&nbsp;&nbsp;&nbsp'
17507                         }
17508
17509                     ]
17510                 };
17511
17512                 var ctr = _this.el.select('.fc-event-container',true).first();
17513                 var cg = ctr.createChild(cfg);
17514
17515                 var sbox = c.select('.fc-day-content',true).first().getBox();
17516                 var ebox = c.select('.fc-day-content',true).first().getBox();
17517                 //Roo.log(cg);
17518                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17519                 cg.setWidth(ebox.right - sbox.x -2);
17520
17521                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17522                 
17523             }
17524             
17525         });
17526         
17527         
17528         
17529     },
17530     
17531     onEventEnter: function (e, el,event,d) {
17532         this.fireEvent('evententer', this, el, event);
17533     },
17534     
17535     onEventLeave: function (e, el,event,d) {
17536         this.fireEvent('eventleave', this, el, event);
17537     },
17538     
17539     onEventClick: function (e, el,event,d) {
17540         this.fireEvent('eventclick', this, el, event);
17541     },
17542     
17543     onMonthChange: function () {
17544         this.store.load();
17545     },
17546     
17547     onMoreEventClick: function(e, el, more)
17548     {
17549         var _this = this;
17550         
17551         this.calpopover.placement = 'right';
17552         this.calpopover.setTitle('More');
17553         
17554         this.calpopover.setContent('');
17555         
17556         var ctr = this.calpopover.el.select('.popover-content', true).first();
17557         
17558         Roo.each(more, function(m){
17559             var cfg = {
17560                 cls : 'fc-event-hori fc-event-draggable',
17561                 html : m.title
17562             };
17563             var cg = ctr.createChild(cfg);
17564             
17565             cg.on('click', _this.onEventClick, _this, m);
17566         });
17567         
17568         this.calpopover.show(el);
17569         
17570         
17571     },
17572     
17573     onLoad: function () 
17574     {   
17575         this.calevents = [];
17576         var cal = this;
17577         
17578         if(this.store.getCount() > 0){
17579             this.store.data.each(function(d){
17580                cal.addItem({
17581                     id : d.data.id,
17582                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17583                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17584                     time : d.data.start_time,
17585                     title : d.data.title,
17586                     description : d.data.description,
17587                     venue : d.data.venue
17588                 });
17589             });
17590         }
17591         
17592         this.renderEvents();
17593         
17594         if(this.calevents.length && this.loadMask){
17595             this.maskEl.hide();
17596         }
17597     },
17598     
17599     onBeforeLoad: function()
17600     {
17601         this.clearEvents();
17602         if(this.loadMask){
17603             this.maskEl.show();
17604         }
17605     }
17606 });
17607
17608  
17609  /*
17610  * - LGPL
17611  *
17612  * element
17613  * 
17614  */
17615
17616 /**
17617  * @class Roo.bootstrap.Popover
17618  * @extends Roo.bootstrap.Component
17619  * Bootstrap Popover class
17620  * @cfg {String} html contents of the popover   (or false to use children..)
17621  * @cfg {String} title of popover (or false to hide)
17622  * @cfg {String} placement how it is placed
17623  * @cfg {String} trigger click || hover (or false to trigger manually)
17624  * @cfg {String} over what (parent or false to trigger manually.)
17625  * @cfg {Number} delay - delay before showing
17626  
17627  * @constructor
17628  * Create a new Popover
17629  * @param {Object} config The config object
17630  */
17631
17632 Roo.bootstrap.Popover = function(config){
17633     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17634     
17635     this.addEvents({
17636         // raw events
17637          /**
17638          * @event show
17639          * After the popover show
17640          * 
17641          * @param {Roo.bootstrap.Popover} this
17642          */
17643         "show" : true,
17644         /**
17645          * @event hide
17646          * After the popover hide
17647          * 
17648          * @param {Roo.bootstrap.Popover} this
17649          */
17650         "hide" : true
17651     });
17652 };
17653
17654 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17655     
17656     title: 'Fill in a title',
17657     html: false,
17658     
17659     placement : 'right',
17660     trigger : 'hover', // hover
17661     
17662     delay : 0,
17663     
17664     over: 'parent',
17665     
17666     can_build_overlaid : false,
17667     
17668     getChildContainer : function()
17669     {
17670         return this.el.select('.popover-content',true).first();
17671     },
17672     
17673     getAutoCreate : function(){
17674          
17675         var cfg = {
17676            cls : 'popover roo-dynamic',
17677            style: 'display:block',
17678            cn : [
17679                 {
17680                     cls : 'arrow'
17681                 },
17682                 {
17683                     cls : 'popover-inner',
17684                     cn : [
17685                         {
17686                             tag: 'h3',
17687                             cls: 'popover-title popover-header',
17688                             html : this.title
17689                         },
17690                         {
17691                             cls : 'popover-content popover-body',
17692                             html : this.html
17693                         }
17694                     ]
17695                     
17696                 }
17697            ]
17698         };
17699         
17700         return cfg;
17701     },
17702     setTitle: function(str)
17703     {
17704         this.title = str;
17705         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17706     },
17707     setContent: function(str)
17708     {
17709         this.html = str;
17710         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17711     },
17712     // as it get's added to the bottom of the page.
17713     onRender : function(ct, position)
17714     {
17715         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17716         if(!this.el){
17717             var cfg = Roo.apply({},  this.getAutoCreate());
17718             cfg.id = Roo.id();
17719             
17720             if (this.cls) {
17721                 cfg.cls += ' ' + this.cls;
17722             }
17723             if (this.style) {
17724                 cfg.style = this.style;
17725             }
17726             //Roo.log("adding to ");
17727             this.el = Roo.get(document.body).createChild(cfg, position);
17728 //            Roo.log(this.el);
17729         }
17730         this.initEvents();
17731     },
17732     
17733     initEvents : function()
17734     {
17735         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17736         this.el.enableDisplayMode('block');
17737         this.el.hide();
17738         if (this.over === false) {
17739             return; 
17740         }
17741         if (this.triggers === false) {
17742             return;
17743         }
17744         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17745         var triggers = this.trigger ? this.trigger.split(' ') : [];
17746         Roo.each(triggers, function(trigger) {
17747         
17748             if (trigger == 'click') {
17749                 on_el.on('click', this.toggle, this);
17750             } else if (trigger != 'manual') {
17751                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17752                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17753       
17754                 on_el.on(eventIn  ,this.enter, this);
17755                 on_el.on(eventOut, this.leave, this);
17756             }
17757         }, this);
17758         
17759     },
17760     
17761     
17762     // private
17763     timeout : null,
17764     hoverState : null,
17765     
17766     toggle : function () {
17767         this.hoverState == 'in' ? this.leave() : this.enter();
17768     },
17769     
17770     enter : function () {
17771         
17772         clearTimeout(this.timeout);
17773     
17774         this.hoverState = 'in';
17775     
17776         if (!this.delay || !this.delay.show) {
17777             this.show();
17778             return;
17779         }
17780         var _t = this;
17781         this.timeout = setTimeout(function () {
17782             if (_t.hoverState == 'in') {
17783                 _t.show();
17784             }
17785         }, this.delay.show)
17786     },
17787     
17788     leave : function() {
17789         clearTimeout(this.timeout);
17790     
17791         this.hoverState = 'out';
17792     
17793         if (!this.delay || !this.delay.hide) {
17794             this.hide();
17795             return;
17796         }
17797         var _t = this;
17798         this.timeout = setTimeout(function () {
17799             if (_t.hoverState == 'out') {
17800                 _t.hide();
17801             }
17802         }, this.delay.hide)
17803     },
17804     
17805     show : function (on_el)
17806     {
17807         if (!on_el) {
17808             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17809         }
17810         
17811         // set content.
17812         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17813         if (this.html !== false) {
17814             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17815         }
17816         this.el.removeClass([
17817             'fade','top','bottom', 'left', 'right','in',
17818             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17819         ]);
17820         if (!this.title.length) {
17821             this.el.select('.popover-title',true).hide();
17822         }
17823         
17824         var placement = typeof this.placement == 'function' ?
17825             this.placement.call(this, this.el, on_el) :
17826             this.placement;
17827             
17828         var autoToken = /\s?auto?\s?/i;
17829         var autoPlace = autoToken.test(placement);
17830         if (autoPlace) {
17831             placement = placement.replace(autoToken, '') || 'top';
17832         }
17833         
17834         //this.el.detach()
17835         //this.el.setXY([0,0]);
17836         this.el.show();
17837         this.el.dom.style.display='block';
17838         this.el.addClass(placement);
17839         
17840         //this.el.appendTo(on_el);
17841         
17842         var p = this.getPosition();
17843         var box = this.el.getBox();
17844         
17845         if (autoPlace) {
17846             // fixme..
17847         }
17848         var align = Roo.bootstrap.Popover.alignment[placement];
17849         
17850 //        Roo.log(align);
17851         this.el.alignTo(on_el, align[0],align[1]);
17852         //var arrow = this.el.select('.arrow',true).first();
17853         //arrow.set(align[2], 
17854         
17855         this.el.addClass('in');
17856         
17857         
17858         if (this.el.hasClass('fade')) {
17859             // fade it?
17860         }
17861         
17862         this.hoverState = 'in';
17863         
17864         this.fireEvent('show', this);
17865         
17866     },
17867     hide : function()
17868     {
17869         this.el.setXY([0,0]);
17870         this.el.removeClass('in');
17871         this.el.hide();
17872         this.hoverState = null;
17873         
17874         this.fireEvent('hide', this);
17875     }
17876     
17877 });
17878
17879 Roo.bootstrap.Popover.alignment = {
17880     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17881     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17882     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17883     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17884 };
17885
17886  /*
17887  * - LGPL
17888  *
17889  * Progress
17890  * 
17891  */
17892
17893 /**
17894  * @class Roo.bootstrap.Progress
17895  * @extends Roo.bootstrap.Component
17896  * Bootstrap Progress class
17897  * @cfg {Boolean} striped striped of the progress bar
17898  * @cfg {Boolean} active animated of the progress bar
17899  * 
17900  * 
17901  * @constructor
17902  * Create a new Progress
17903  * @param {Object} config The config object
17904  */
17905
17906 Roo.bootstrap.Progress = function(config){
17907     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17908 };
17909
17910 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17911     
17912     striped : false,
17913     active: false,
17914     
17915     getAutoCreate : function(){
17916         var cfg = {
17917             tag: 'div',
17918             cls: 'progress'
17919         };
17920         
17921         
17922         if(this.striped){
17923             cfg.cls += ' progress-striped';
17924         }
17925       
17926         if(this.active){
17927             cfg.cls += ' active';
17928         }
17929         
17930         
17931         return cfg;
17932     }
17933    
17934 });
17935
17936  
17937
17938  /*
17939  * - LGPL
17940  *
17941  * ProgressBar
17942  * 
17943  */
17944
17945 /**
17946  * @class Roo.bootstrap.ProgressBar
17947  * @extends Roo.bootstrap.Component
17948  * Bootstrap ProgressBar class
17949  * @cfg {Number} aria_valuenow aria-value now
17950  * @cfg {Number} aria_valuemin aria-value min
17951  * @cfg {Number} aria_valuemax aria-value max
17952  * @cfg {String} label label for the progress bar
17953  * @cfg {String} panel (success | info | warning | danger )
17954  * @cfg {String} role role of the progress bar
17955  * @cfg {String} sr_only text
17956  * 
17957  * 
17958  * @constructor
17959  * Create a new ProgressBar
17960  * @param {Object} config The config object
17961  */
17962
17963 Roo.bootstrap.ProgressBar = function(config){
17964     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17965 };
17966
17967 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17968     
17969     aria_valuenow : 0,
17970     aria_valuemin : 0,
17971     aria_valuemax : 100,
17972     label : false,
17973     panel : false,
17974     role : false,
17975     sr_only: false,
17976     
17977     getAutoCreate : function()
17978     {
17979         
17980         var cfg = {
17981             tag: 'div',
17982             cls: 'progress-bar',
17983             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17984         };
17985         
17986         if(this.sr_only){
17987             cfg.cn = {
17988                 tag: 'span',
17989                 cls: 'sr-only',
17990                 html: this.sr_only
17991             }
17992         }
17993         
17994         if(this.role){
17995             cfg.role = this.role;
17996         }
17997         
17998         if(this.aria_valuenow){
17999             cfg['aria-valuenow'] = this.aria_valuenow;
18000         }
18001         
18002         if(this.aria_valuemin){
18003             cfg['aria-valuemin'] = this.aria_valuemin;
18004         }
18005         
18006         if(this.aria_valuemax){
18007             cfg['aria-valuemax'] = this.aria_valuemax;
18008         }
18009         
18010         if(this.label && !this.sr_only){
18011             cfg.html = this.label;
18012         }
18013         
18014         if(this.panel){
18015             cfg.cls += ' progress-bar-' + this.panel;
18016         }
18017         
18018         return cfg;
18019     },
18020     
18021     update : function(aria_valuenow)
18022     {
18023         this.aria_valuenow = aria_valuenow;
18024         
18025         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18026     }
18027    
18028 });
18029
18030  
18031
18032  /*
18033  * - LGPL
18034  *
18035  * column
18036  * 
18037  */
18038
18039 /**
18040  * @class Roo.bootstrap.TabGroup
18041  * @extends Roo.bootstrap.Column
18042  * Bootstrap Column class
18043  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18044  * @cfg {Boolean} carousel true to make the group behave like a carousel
18045  * @cfg {Boolean} bullets show bullets for the panels
18046  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18047  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18048  * @cfg {Boolean} showarrow (true|false) show arrow default true
18049  * 
18050  * @constructor
18051  * Create a new TabGroup
18052  * @param {Object} config The config object
18053  */
18054
18055 Roo.bootstrap.TabGroup = function(config){
18056     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18057     if (!this.navId) {
18058         this.navId = Roo.id();
18059     }
18060     this.tabs = [];
18061     Roo.bootstrap.TabGroup.register(this);
18062     
18063 };
18064
18065 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18066     
18067     carousel : false,
18068     transition : false,
18069     bullets : 0,
18070     timer : 0,
18071     autoslide : false,
18072     slideFn : false,
18073     slideOnTouch : false,
18074     showarrow : true,
18075     
18076     getAutoCreate : function()
18077     {
18078         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18079         
18080         cfg.cls += ' tab-content';
18081         
18082         if (this.carousel) {
18083             cfg.cls += ' carousel slide';
18084             
18085             cfg.cn = [{
18086                cls : 'carousel-inner',
18087                cn : []
18088             }];
18089         
18090             if(this.bullets  && !Roo.isTouch){
18091                 
18092                 var bullets = {
18093                     cls : 'carousel-bullets',
18094                     cn : []
18095                 };
18096                
18097                 if(this.bullets_cls){
18098                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18099                 }
18100                 
18101                 bullets.cn.push({
18102                     cls : 'clear'
18103                 });
18104                 
18105                 cfg.cn[0].cn.push(bullets);
18106             }
18107             
18108             if(this.showarrow){
18109                 cfg.cn[0].cn.push({
18110                     tag : 'div',
18111                     class : 'carousel-arrow',
18112                     cn : [
18113                         {
18114                             tag : 'div',
18115                             class : 'carousel-prev',
18116                             cn : [
18117                                 {
18118                                     tag : 'i',
18119                                     class : 'fa fa-chevron-left'
18120                                 }
18121                             ]
18122                         },
18123                         {
18124                             tag : 'div',
18125                             class : 'carousel-next',
18126                             cn : [
18127                                 {
18128                                     tag : 'i',
18129                                     class : 'fa fa-chevron-right'
18130                                 }
18131                             ]
18132                         }
18133                     ]
18134                 });
18135             }
18136             
18137         }
18138         
18139         return cfg;
18140     },
18141     
18142     initEvents:  function()
18143     {
18144 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18145 //            this.el.on("touchstart", this.onTouchStart, this);
18146 //        }
18147         
18148         if(this.autoslide){
18149             var _this = this;
18150             
18151             this.slideFn = window.setInterval(function() {
18152                 _this.showPanelNext();
18153             }, this.timer);
18154         }
18155         
18156         if(this.showarrow){
18157             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18158             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18159         }
18160         
18161         
18162     },
18163     
18164 //    onTouchStart : function(e, el, o)
18165 //    {
18166 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18167 //            return;
18168 //        }
18169 //        
18170 //        this.showPanelNext();
18171 //    },
18172     
18173     
18174     getChildContainer : function()
18175     {
18176         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18177     },
18178     
18179     /**
18180     * register a Navigation item
18181     * @param {Roo.bootstrap.NavItem} the navitem to add
18182     */
18183     register : function(item)
18184     {
18185         this.tabs.push( item);
18186         item.navId = this.navId; // not really needed..
18187         this.addBullet();
18188     
18189     },
18190     
18191     getActivePanel : function()
18192     {
18193         var r = false;
18194         Roo.each(this.tabs, function(t) {
18195             if (t.active) {
18196                 r = t;
18197                 return false;
18198             }
18199             return null;
18200         });
18201         return r;
18202         
18203     },
18204     getPanelByName : function(n)
18205     {
18206         var r = false;
18207         Roo.each(this.tabs, function(t) {
18208             if (t.tabId == n) {
18209                 r = t;
18210                 return false;
18211             }
18212             return null;
18213         });
18214         return r;
18215     },
18216     indexOfPanel : function(p)
18217     {
18218         var r = false;
18219         Roo.each(this.tabs, function(t,i) {
18220             if (t.tabId == p.tabId) {
18221                 r = i;
18222                 return false;
18223             }
18224             return null;
18225         });
18226         return r;
18227     },
18228     /**
18229      * show a specific panel
18230      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18231      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18232      */
18233     showPanel : function (pan)
18234     {
18235         if(this.transition || typeof(pan) == 'undefined'){
18236             Roo.log("waiting for the transitionend");
18237             return;
18238         }
18239         
18240         if (typeof(pan) == 'number') {
18241             pan = this.tabs[pan];
18242         }
18243         
18244         if (typeof(pan) == 'string') {
18245             pan = this.getPanelByName(pan);
18246         }
18247         
18248         var cur = this.getActivePanel();
18249         
18250         if(!pan || !cur){
18251             Roo.log('pan or acitve pan is undefined');
18252             return false;
18253         }
18254         
18255         if (pan.tabId == this.getActivePanel().tabId) {
18256             return true;
18257         }
18258         
18259         if (false === cur.fireEvent('beforedeactivate')) {
18260             return false;
18261         }
18262         
18263         if(this.bullets > 0 && !Roo.isTouch){
18264             this.setActiveBullet(this.indexOfPanel(pan));
18265         }
18266         
18267         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18268             
18269             this.transition = true;
18270             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18271             var lr = dir == 'next' ? 'left' : 'right';
18272             pan.el.addClass(dir); // or prev
18273             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18274             cur.el.addClass(lr); // or right
18275             pan.el.addClass(lr);
18276             
18277             var _this = this;
18278             cur.el.on('transitionend', function() {
18279                 Roo.log("trans end?");
18280                 
18281                 pan.el.removeClass([lr,dir]);
18282                 pan.setActive(true);
18283                 
18284                 cur.el.removeClass([lr]);
18285                 cur.setActive(false);
18286                 
18287                 _this.transition = false;
18288                 
18289             }, this, { single:  true } );
18290             
18291             return true;
18292         }
18293         
18294         cur.setActive(false);
18295         pan.setActive(true);
18296         
18297         return true;
18298         
18299     },
18300     showPanelNext : function()
18301     {
18302         var i = this.indexOfPanel(this.getActivePanel());
18303         
18304         if (i >= this.tabs.length - 1 && !this.autoslide) {
18305             return;
18306         }
18307         
18308         if (i >= this.tabs.length - 1 && this.autoslide) {
18309             i = -1;
18310         }
18311         
18312         this.showPanel(this.tabs[i+1]);
18313     },
18314     
18315     showPanelPrev : function()
18316     {
18317         var i = this.indexOfPanel(this.getActivePanel());
18318         
18319         if (i  < 1 && !this.autoslide) {
18320             return;
18321         }
18322         
18323         if (i < 1 && this.autoslide) {
18324             i = this.tabs.length;
18325         }
18326         
18327         this.showPanel(this.tabs[i-1]);
18328     },
18329     
18330     
18331     addBullet: function()
18332     {
18333         if(!this.bullets || Roo.isTouch){
18334             return;
18335         }
18336         var ctr = this.el.select('.carousel-bullets',true).first();
18337         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18338         var bullet = ctr.createChild({
18339             cls : 'bullet bullet-' + i
18340         },ctr.dom.lastChild);
18341         
18342         
18343         var _this = this;
18344         
18345         bullet.on('click', (function(e, el, o, ii, t){
18346
18347             e.preventDefault();
18348
18349             this.showPanel(ii);
18350
18351             if(this.autoslide && this.slideFn){
18352                 clearInterval(this.slideFn);
18353                 this.slideFn = window.setInterval(function() {
18354                     _this.showPanelNext();
18355                 }, this.timer);
18356             }
18357
18358         }).createDelegate(this, [i, bullet], true));
18359                 
18360         
18361     },
18362      
18363     setActiveBullet : function(i)
18364     {
18365         if(Roo.isTouch){
18366             return;
18367         }
18368         
18369         Roo.each(this.el.select('.bullet', true).elements, function(el){
18370             el.removeClass('selected');
18371         });
18372
18373         var bullet = this.el.select('.bullet-' + i, true).first();
18374         
18375         if(!bullet){
18376             return;
18377         }
18378         
18379         bullet.addClass('selected');
18380     }
18381     
18382     
18383   
18384 });
18385
18386  
18387
18388  
18389  
18390 Roo.apply(Roo.bootstrap.TabGroup, {
18391     
18392     groups: {},
18393      /**
18394     * register a Navigation Group
18395     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18396     */
18397     register : function(navgrp)
18398     {
18399         this.groups[navgrp.navId] = navgrp;
18400         
18401     },
18402     /**
18403     * fetch a Navigation Group based on the navigation ID
18404     * if one does not exist , it will get created.
18405     * @param {string} the navgroup to add
18406     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18407     */
18408     get: function(navId) {
18409         if (typeof(this.groups[navId]) == 'undefined') {
18410             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18411         }
18412         return this.groups[navId] ;
18413     }
18414     
18415     
18416     
18417 });
18418
18419  /*
18420  * - LGPL
18421  *
18422  * TabPanel
18423  * 
18424  */
18425
18426 /**
18427  * @class Roo.bootstrap.TabPanel
18428  * @extends Roo.bootstrap.Component
18429  * Bootstrap TabPanel class
18430  * @cfg {Boolean} active panel active
18431  * @cfg {String} html panel content
18432  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18433  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18434  * @cfg {String} href click to link..
18435  * 
18436  * 
18437  * @constructor
18438  * Create a new TabPanel
18439  * @param {Object} config The config object
18440  */
18441
18442 Roo.bootstrap.TabPanel = function(config){
18443     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18444     this.addEvents({
18445         /**
18446              * @event changed
18447              * Fires when the active status changes
18448              * @param {Roo.bootstrap.TabPanel} this
18449              * @param {Boolean} state the new state
18450             
18451          */
18452         'changed': true,
18453         /**
18454              * @event beforedeactivate
18455              * Fires before a tab is de-activated - can be used to do validation on a form.
18456              * @param {Roo.bootstrap.TabPanel} this
18457              * @return {Boolean} false if there is an error
18458             
18459          */
18460         'beforedeactivate': true
18461      });
18462     
18463     this.tabId = this.tabId || Roo.id();
18464   
18465 };
18466
18467 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18468     
18469     active: false,
18470     html: false,
18471     tabId: false,
18472     navId : false,
18473     href : '',
18474     
18475     getAutoCreate : function(){
18476         var cfg = {
18477             tag: 'div',
18478             // item is needed for carousel - not sure if it has any effect otherwise
18479             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18480             html: this.html || ''
18481         };
18482         
18483         if(this.active){
18484             cfg.cls += ' active';
18485         }
18486         
18487         if(this.tabId){
18488             cfg.tabId = this.tabId;
18489         }
18490         
18491         
18492         return cfg;
18493     },
18494     
18495     initEvents:  function()
18496     {
18497         var p = this.parent();
18498         
18499         this.navId = this.navId || p.navId;
18500         
18501         if (typeof(this.navId) != 'undefined') {
18502             // not really needed.. but just in case.. parent should be a NavGroup.
18503             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18504             
18505             tg.register(this);
18506             
18507             var i = tg.tabs.length - 1;
18508             
18509             if(this.active && tg.bullets > 0 && i < tg.bullets){
18510                 tg.setActiveBullet(i);
18511             }
18512         }
18513         
18514         this.el.on('click', this.onClick, this);
18515         
18516         if(Roo.isTouch){
18517             this.el.on("touchstart", this.onTouchStart, this);
18518             this.el.on("touchmove", this.onTouchMove, this);
18519             this.el.on("touchend", this.onTouchEnd, this);
18520         }
18521         
18522     },
18523     
18524     onRender : function(ct, position)
18525     {
18526         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18527     },
18528     
18529     setActive : function(state)
18530     {
18531         Roo.log("panel - set active " + this.tabId + "=" + state);
18532         
18533         this.active = state;
18534         if (!state) {
18535             this.el.removeClass('active');
18536             
18537         } else  if (!this.el.hasClass('active')) {
18538             this.el.addClass('active');
18539         }
18540         
18541         this.fireEvent('changed', this, state);
18542     },
18543     
18544     onClick : function(e)
18545     {
18546         e.preventDefault();
18547         
18548         if(!this.href.length){
18549             return;
18550         }
18551         
18552         window.location.href = this.href;
18553     },
18554     
18555     startX : 0,
18556     startY : 0,
18557     endX : 0,
18558     endY : 0,
18559     swiping : false,
18560     
18561     onTouchStart : function(e)
18562     {
18563         this.swiping = false;
18564         
18565         this.startX = e.browserEvent.touches[0].clientX;
18566         this.startY = e.browserEvent.touches[0].clientY;
18567     },
18568     
18569     onTouchMove : function(e)
18570     {
18571         this.swiping = true;
18572         
18573         this.endX = e.browserEvent.touches[0].clientX;
18574         this.endY = e.browserEvent.touches[0].clientY;
18575     },
18576     
18577     onTouchEnd : function(e)
18578     {
18579         if(!this.swiping){
18580             this.onClick(e);
18581             return;
18582         }
18583         
18584         var tabGroup = this.parent();
18585         
18586         if(this.endX > this.startX){ // swiping right
18587             tabGroup.showPanelPrev();
18588             return;
18589         }
18590         
18591         if(this.startX > this.endX){ // swiping left
18592             tabGroup.showPanelNext();
18593             return;
18594         }
18595     }
18596     
18597     
18598 });
18599  
18600
18601  
18602
18603  /*
18604  * - LGPL
18605  *
18606  * DateField
18607  * 
18608  */
18609
18610 /**
18611  * @class Roo.bootstrap.DateField
18612  * @extends Roo.bootstrap.Input
18613  * Bootstrap DateField class
18614  * @cfg {Number} weekStart default 0
18615  * @cfg {String} viewMode default empty, (months|years)
18616  * @cfg {String} minViewMode default empty, (months|years)
18617  * @cfg {Number} startDate default -Infinity
18618  * @cfg {Number} endDate default Infinity
18619  * @cfg {Boolean} todayHighlight default false
18620  * @cfg {Boolean} todayBtn default false
18621  * @cfg {Boolean} calendarWeeks default false
18622  * @cfg {Object} daysOfWeekDisabled default empty
18623  * @cfg {Boolean} singleMode default false (true | false)
18624  * 
18625  * @cfg {Boolean} keyboardNavigation default true
18626  * @cfg {String} language default en
18627  * 
18628  * @constructor
18629  * Create a new DateField
18630  * @param {Object} config The config object
18631  */
18632
18633 Roo.bootstrap.DateField = function(config){
18634     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18635      this.addEvents({
18636             /**
18637              * @event show
18638              * Fires when this field show.
18639              * @param {Roo.bootstrap.DateField} this
18640              * @param {Mixed} date The date value
18641              */
18642             show : true,
18643             /**
18644              * @event show
18645              * Fires when this field hide.
18646              * @param {Roo.bootstrap.DateField} this
18647              * @param {Mixed} date The date value
18648              */
18649             hide : true,
18650             /**
18651              * @event select
18652              * Fires when select a date.
18653              * @param {Roo.bootstrap.DateField} this
18654              * @param {Mixed} date The date value
18655              */
18656             select : true,
18657             /**
18658              * @event beforeselect
18659              * Fires when before select a date.
18660              * @param {Roo.bootstrap.DateField} this
18661              * @param {Mixed} date The date value
18662              */
18663             beforeselect : true
18664         });
18665 };
18666
18667 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18668     
18669     /**
18670      * @cfg {String} format
18671      * The default date format string which can be overriden for localization support.  The format must be
18672      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18673      */
18674     format : "m/d/y",
18675     /**
18676      * @cfg {String} altFormats
18677      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18678      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18679      */
18680     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18681     
18682     weekStart : 0,
18683     
18684     viewMode : '',
18685     
18686     minViewMode : '',
18687     
18688     todayHighlight : false,
18689     
18690     todayBtn: false,
18691     
18692     language: 'en',
18693     
18694     keyboardNavigation: true,
18695     
18696     calendarWeeks: false,
18697     
18698     startDate: -Infinity,
18699     
18700     endDate: Infinity,
18701     
18702     daysOfWeekDisabled: [],
18703     
18704     _events: [],
18705     
18706     singleMode : false,
18707     
18708     UTCDate: function()
18709     {
18710         return new Date(Date.UTC.apply(Date, arguments));
18711     },
18712     
18713     UTCToday: function()
18714     {
18715         var today = new Date();
18716         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18717     },
18718     
18719     getDate: function() {
18720             var d = this.getUTCDate();
18721             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18722     },
18723     
18724     getUTCDate: function() {
18725             return this.date;
18726     },
18727     
18728     setDate: function(d) {
18729             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18730     },
18731     
18732     setUTCDate: function(d) {
18733             this.date = d;
18734             this.setValue(this.formatDate(this.date));
18735     },
18736         
18737     onRender: function(ct, position)
18738     {
18739         
18740         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18741         
18742         this.language = this.language || 'en';
18743         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18744         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18745         
18746         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18747         this.format = this.format || 'm/d/y';
18748         this.isInline = false;
18749         this.isInput = true;
18750         this.component = this.el.select('.add-on', true).first() || false;
18751         this.component = (this.component && this.component.length === 0) ? false : this.component;
18752         this.hasInput = this.component && this.inputEl().length;
18753         
18754         if (typeof(this.minViewMode === 'string')) {
18755             switch (this.minViewMode) {
18756                 case 'months':
18757                     this.minViewMode = 1;
18758                     break;
18759                 case 'years':
18760                     this.minViewMode = 2;
18761                     break;
18762                 default:
18763                     this.minViewMode = 0;
18764                     break;
18765             }
18766         }
18767         
18768         if (typeof(this.viewMode === 'string')) {
18769             switch (this.viewMode) {
18770                 case 'months':
18771                     this.viewMode = 1;
18772                     break;
18773                 case 'years':
18774                     this.viewMode = 2;
18775                     break;
18776                 default:
18777                     this.viewMode = 0;
18778                     break;
18779             }
18780         }
18781                 
18782         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18783         
18784 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18785         
18786         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18787         
18788         this.picker().on('mousedown', this.onMousedown, this);
18789         this.picker().on('click', this.onClick, this);
18790         
18791         this.picker().addClass('datepicker-dropdown');
18792         
18793         this.startViewMode = this.viewMode;
18794         
18795         if(this.singleMode){
18796             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18797                 v.setVisibilityMode(Roo.Element.DISPLAY);
18798                 v.hide();
18799             });
18800             
18801             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18802                 v.setStyle('width', '189px');
18803             });
18804         }
18805         
18806         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18807             if(!this.calendarWeeks){
18808                 v.remove();
18809                 return;
18810             }
18811             
18812             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18813             v.attr('colspan', function(i, val){
18814                 return parseInt(val) + 1;
18815             });
18816         });
18817                         
18818         
18819         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18820         
18821         this.setStartDate(this.startDate);
18822         this.setEndDate(this.endDate);
18823         
18824         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18825         
18826         this.fillDow();
18827         this.fillMonths();
18828         this.update();
18829         this.showMode();
18830         
18831         if(this.isInline) {
18832             this.showPopup();
18833         }
18834     },
18835     
18836     picker : function()
18837     {
18838         return this.pickerEl;
18839 //        return this.el.select('.datepicker', true).first();
18840     },
18841     
18842     fillDow: function()
18843     {
18844         var dowCnt = this.weekStart;
18845         
18846         var dow = {
18847             tag: 'tr',
18848             cn: [
18849                 
18850             ]
18851         };
18852         
18853         if(this.calendarWeeks){
18854             dow.cn.push({
18855                 tag: 'th',
18856                 cls: 'cw',
18857                 html: '&nbsp;'
18858             })
18859         }
18860         
18861         while (dowCnt < this.weekStart + 7) {
18862             dow.cn.push({
18863                 tag: 'th',
18864                 cls: 'dow',
18865                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18866             });
18867         }
18868         
18869         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18870     },
18871     
18872     fillMonths: function()
18873     {    
18874         var i = 0;
18875         var months = this.picker().select('>.datepicker-months td', true).first();
18876         
18877         months.dom.innerHTML = '';
18878         
18879         while (i < 12) {
18880             var month = {
18881                 tag: 'span',
18882                 cls: 'month',
18883                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18884             };
18885             
18886             months.createChild(month);
18887         }
18888         
18889     },
18890     
18891     update: function()
18892     {
18893         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;
18894         
18895         if (this.date < this.startDate) {
18896             this.viewDate = new Date(this.startDate);
18897         } else if (this.date > this.endDate) {
18898             this.viewDate = new Date(this.endDate);
18899         } else {
18900             this.viewDate = new Date(this.date);
18901         }
18902         
18903         this.fill();
18904     },
18905     
18906     fill: function() 
18907     {
18908         var d = new Date(this.viewDate),
18909                 year = d.getUTCFullYear(),
18910                 month = d.getUTCMonth(),
18911                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18912                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18913                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18914                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18915                 currentDate = this.date && this.date.valueOf(),
18916                 today = this.UTCToday();
18917         
18918         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18919         
18920 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18921         
18922 //        this.picker.select('>tfoot th.today').
18923 //                                              .text(dates[this.language].today)
18924 //                                              .toggle(this.todayBtn !== false);
18925     
18926         this.updateNavArrows();
18927         this.fillMonths();
18928                                                 
18929         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18930         
18931         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18932          
18933         prevMonth.setUTCDate(day);
18934         
18935         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18936         
18937         var nextMonth = new Date(prevMonth);
18938         
18939         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18940         
18941         nextMonth = nextMonth.valueOf();
18942         
18943         var fillMonths = false;
18944         
18945         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18946         
18947         while(prevMonth.valueOf() <= nextMonth) {
18948             var clsName = '';
18949             
18950             if (prevMonth.getUTCDay() === this.weekStart) {
18951                 if(fillMonths){
18952                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18953                 }
18954                     
18955                 fillMonths = {
18956                     tag: 'tr',
18957                     cn: []
18958                 };
18959                 
18960                 if(this.calendarWeeks){
18961                     // ISO 8601: First week contains first thursday.
18962                     // ISO also states week starts on Monday, but we can be more abstract here.
18963                     var
18964                     // Start of current week: based on weekstart/current date
18965                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18966                     // Thursday of this week
18967                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18968                     // First Thursday of year, year from thursday
18969                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18970                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18971                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18972                     
18973                     fillMonths.cn.push({
18974                         tag: 'td',
18975                         cls: 'cw',
18976                         html: calWeek
18977                     });
18978                 }
18979             }
18980             
18981             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18982                 clsName += ' old';
18983             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18984                 clsName += ' new';
18985             }
18986             if (this.todayHighlight &&
18987                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18988                 prevMonth.getUTCMonth() == today.getMonth() &&
18989                 prevMonth.getUTCDate() == today.getDate()) {
18990                 clsName += ' today';
18991             }
18992             
18993             if (currentDate && prevMonth.valueOf() === currentDate) {
18994                 clsName += ' active';
18995             }
18996             
18997             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18998                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18999                     clsName += ' disabled';
19000             }
19001             
19002             fillMonths.cn.push({
19003                 tag: 'td',
19004                 cls: 'day ' + clsName,
19005                 html: prevMonth.getDate()
19006             });
19007             
19008             prevMonth.setDate(prevMonth.getDate()+1);
19009         }
19010           
19011         var currentYear = this.date && this.date.getUTCFullYear();
19012         var currentMonth = this.date && this.date.getUTCMonth();
19013         
19014         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19015         
19016         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19017             v.removeClass('active');
19018             
19019             if(currentYear === year && k === currentMonth){
19020                 v.addClass('active');
19021             }
19022             
19023             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19024                 v.addClass('disabled');
19025             }
19026             
19027         });
19028         
19029         
19030         year = parseInt(year/10, 10) * 10;
19031         
19032         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19033         
19034         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19035         
19036         year -= 1;
19037         for (var i = -1; i < 11; i++) {
19038             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19039                 tag: 'span',
19040                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19041                 html: year
19042             });
19043             
19044             year += 1;
19045         }
19046     },
19047     
19048     showMode: function(dir) 
19049     {
19050         if (dir) {
19051             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19052         }
19053         
19054         Roo.each(this.picker().select('>div',true).elements, function(v){
19055             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19056             v.hide();
19057         });
19058         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19059     },
19060     
19061     place: function()
19062     {
19063         if(this.isInline) {
19064             return;
19065         }
19066         
19067         this.picker().removeClass(['bottom', 'top']);
19068         
19069         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19070             /*
19071              * place to the top of element!
19072              *
19073              */
19074             
19075             this.picker().addClass('top');
19076             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19077             
19078             return;
19079         }
19080         
19081         this.picker().addClass('bottom');
19082         
19083         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19084     },
19085     
19086     parseDate : function(value)
19087     {
19088         if(!value || value instanceof Date){
19089             return value;
19090         }
19091         var v = Date.parseDate(value, this.format);
19092         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19093             v = Date.parseDate(value, 'Y-m-d');
19094         }
19095         if(!v && this.altFormats){
19096             if(!this.altFormatsArray){
19097                 this.altFormatsArray = this.altFormats.split("|");
19098             }
19099             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19100                 v = Date.parseDate(value, this.altFormatsArray[i]);
19101             }
19102         }
19103         return v;
19104     },
19105     
19106     formatDate : function(date, fmt)
19107     {   
19108         return (!date || !(date instanceof Date)) ?
19109         date : date.dateFormat(fmt || this.format);
19110     },
19111     
19112     onFocus : function()
19113     {
19114         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19115         this.showPopup();
19116     },
19117     
19118     onBlur : function()
19119     {
19120         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19121         
19122         var d = this.inputEl().getValue();
19123         
19124         this.setValue(d);
19125                 
19126         this.hidePopup();
19127     },
19128     
19129     showPopup : function()
19130     {
19131         this.picker().show();
19132         this.update();
19133         this.place();
19134         
19135         this.fireEvent('showpopup', this, this.date);
19136     },
19137     
19138     hidePopup : function()
19139     {
19140         if(this.isInline) {
19141             return;
19142         }
19143         this.picker().hide();
19144         this.viewMode = this.startViewMode;
19145         this.showMode();
19146         
19147         this.fireEvent('hidepopup', this, this.date);
19148         
19149     },
19150     
19151     onMousedown: function(e)
19152     {
19153         e.stopPropagation();
19154         e.preventDefault();
19155     },
19156     
19157     keyup: function(e)
19158     {
19159         Roo.bootstrap.DateField.superclass.keyup.call(this);
19160         this.update();
19161     },
19162
19163     setValue: function(v)
19164     {
19165         if(this.fireEvent('beforeselect', this, v) !== false){
19166             var d = new Date(this.parseDate(v) ).clearTime();
19167         
19168             if(isNaN(d.getTime())){
19169                 this.date = this.viewDate = '';
19170                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19171                 return;
19172             }
19173
19174             v = this.formatDate(d);
19175
19176             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19177
19178             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19179
19180             this.update();
19181
19182             this.fireEvent('select', this, this.date);
19183         }
19184     },
19185     
19186     getValue: function()
19187     {
19188         return this.formatDate(this.date);
19189     },
19190     
19191     fireKey: function(e)
19192     {
19193         if (!this.picker().isVisible()){
19194             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19195                 this.showPopup();
19196             }
19197             return;
19198         }
19199         
19200         var dateChanged = false,
19201         dir, day, month,
19202         newDate, newViewDate;
19203         
19204         switch(e.keyCode){
19205             case 27: // escape
19206                 this.hidePopup();
19207                 e.preventDefault();
19208                 break;
19209             case 37: // left
19210             case 39: // right
19211                 if (!this.keyboardNavigation) {
19212                     break;
19213                 }
19214                 dir = e.keyCode == 37 ? -1 : 1;
19215                 
19216                 if (e.ctrlKey){
19217                     newDate = this.moveYear(this.date, dir);
19218                     newViewDate = this.moveYear(this.viewDate, dir);
19219                 } else if (e.shiftKey){
19220                     newDate = this.moveMonth(this.date, dir);
19221                     newViewDate = this.moveMonth(this.viewDate, dir);
19222                 } else {
19223                     newDate = new Date(this.date);
19224                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19225                     newViewDate = new Date(this.viewDate);
19226                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19227                 }
19228                 if (this.dateWithinRange(newDate)){
19229                     this.date = newDate;
19230                     this.viewDate = newViewDate;
19231                     this.setValue(this.formatDate(this.date));
19232 //                    this.update();
19233                     e.preventDefault();
19234                     dateChanged = true;
19235                 }
19236                 break;
19237             case 38: // up
19238             case 40: // down
19239                 if (!this.keyboardNavigation) {
19240                     break;
19241                 }
19242                 dir = e.keyCode == 38 ? -1 : 1;
19243                 if (e.ctrlKey){
19244                     newDate = this.moveYear(this.date, dir);
19245                     newViewDate = this.moveYear(this.viewDate, dir);
19246                 } else if (e.shiftKey){
19247                     newDate = this.moveMonth(this.date, dir);
19248                     newViewDate = this.moveMonth(this.viewDate, dir);
19249                 } else {
19250                     newDate = new Date(this.date);
19251                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19252                     newViewDate = new Date(this.viewDate);
19253                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19254                 }
19255                 if (this.dateWithinRange(newDate)){
19256                     this.date = newDate;
19257                     this.viewDate = newViewDate;
19258                     this.setValue(this.formatDate(this.date));
19259 //                    this.update();
19260                     e.preventDefault();
19261                     dateChanged = true;
19262                 }
19263                 break;
19264             case 13: // enter
19265                 this.setValue(this.formatDate(this.date));
19266                 this.hidePopup();
19267                 e.preventDefault();
19268                 break;
19269             case 9: // tab
19270                 this.setValue(this.formatDate(this.date));
19271                 this.hidePopup();
19272                 break;
19273             case 16: // shift
19274             case 17: // ctrl
19275             case 18: // alt
19276                 break;
19277             default :
19278                 this.hidePopup();
19279                 
19280         }
19281     },
19282     
19283     
19284     onClick: function(e) 
19285     {
19286         e.stopPropagation();
19287         e.preventDefault();
19288         
19289         var target = e.getTarget();
19290         
19291         if(target.nodeName.toLowerCase() === 'i'){
19292             target = Roo.get(target).dom.parentNode;
19293         }
19294         
19295         var nodeName = target.nodeName;
19296         var className = target.className;
19297         var html = target.innerHTML;
19298         //Roo.log(nodeName);
19299         
19300         switch(nodeName.toLowerCase()) {
19301             case 'th':
19302                 switch(className) {
19303                     case 'switch':
19304                         this.showMode(1);
19305                         break;
19306                     case 'prev':
19307                     case 'next':
19308                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19309                         switch(this.viewMode){
19310                                 case 0:
19311                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19312                                         break;
19313                                 case 1:
19314                                 case 2:
19315                                         this.viewDate = this.moveYear(this.viewDate, dir);
19316                                         break;
19317                         }
19318                         this.fill();
19319                         break;
19320                     case 'today':
19321                         var date = new Date();
19322                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19323 //                        this.fill()
19324                         this.setValue(this.formatDate(this.date));
19325                         
19326                         this.hidePopup();
19327                         break;
19328                 }
19329                 break;
19330             case 'span':
19331                 if (className.indexOf('disabled') < 0) {
19332                     this.viewDate.setUTCDate(1);
19333                     if (className.indexOf('month') > -1) {
19334                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19335                     } else {
19336                         var year = parseInt(html, 10) || 0;
19337                         this.viewDate.setUTCFullYear(year);
19338                         
19339                     }
19340                     
19341                     if(this.singleMode){
19342                         this.setValue(this.formatDate(this.viewDate));
19343                         this.hidePopup();
19344                         return;
19345                     }
19346                     
19347                     this.showMode(-1);
19348                     this.fill();
19349                 }
19350                 break;
19351                 
19352             case 'td':
19353                 //Roo.log(className);
19354                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19355                     var day = parseInt(html, 10) || 1;
19356                     var year = this.viewDate.getUTCFullYear(),
19357                         month = this.viewDate.getUTCMonth();
19358
19359                     if (className.indexOf('old') > -1) {
19360                         if(month === 0 ){
19361                             month = 11;
19362                             year -= 1;
19363                         }else{
19364                             month -= 1;
19365                         }
19366                     } else if (className.indexOf('new') > -1) {
19367                         if (month == 11) {
19368                             month = 0;
19369                             year += 1;
19370                         } else {
19371                             month += 1;
19372                         }
19373                     }
19374                     //Roo.log([year,month,day]);
19375                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19376                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19377 //                    this.fill();
19378                     //Roo.log(this.formatDate(this.date));
19379                     this.setValue(this.formatDate(this.date));
19380                     this.hidePopup();
19381                 }
19382                 break;
19383         }
19384     },
19385     
19386     setStartDate: function(startDate)
19387     {
19388         this.startDate = startDate || -Infinity;
19389         if (this.startDate !== -Infinity) {
19390             this.startDate = this.parseDate(this.startDate);
19391         }
19392         this.update();
19393         this.updateNavArrows();
19394     },
19395
19396     setEndDate: function(endDate)
19397     {
19398         this.endDate = endDate || Infinity;
19399         if (this.endDate !== Infinity) {
19400             this.endDate = this.parseDate(this.endDate);
19401         }
19402         this.update();
19403         this.updateNavArrows();
19404     },
19405     
19406     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19407     {
19408         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19409         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19410             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19411         }
19412         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19413             return parseInt(d, 10);
19414         });
19415         this.update();
19416         this.updateNavArrows();
19417     },
19418     
19419     updateNavArrows: function() 
19420     {
19421         if(this.singleMode){
19422             return;
19423         }
19424         
19425         var d = new Date(this.viewDate),
19426         year = d.getUTCFullYear(),
19427         month = d.getUTCMonth();
19428         
19429         Roo.each(this.picker().select('.prev', true).elements, function(v){
19430             v.show();
19431             switch (this.viewMode) {
19432                 case 0:
19433
19434                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19435                         v.hide();
19436                     }
19437                     break;
19438                 case 1:
19439                 case 2:
19440                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19441                         v.hide();
19442                     }
19443                     break;
19444             }
19445         });
19446         
19447         Roo.each(this.picker().select('.next', true).elements, function(v){
19448             v.show();
19449             switch (this.viewMode) {
19450                 case 0:
19451
19452                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19453                         v.hide();
19454                     }
19455                     break;
19456                 case 1:
19457                 case 2:
19458                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19459                         v.hide();
19460                     }
19461                     break;
19462             }
19463         })
19464     },
19465     
19466     moveMonth: function(date, dir)
19467     {
19468         if (!dir) {
19469             return date;
19470         }
19471         var new_date = new Date(date.valueOf()),
19472         day = new_date.getUTCDate(),
19473         month = new_date.getUTCMonth(),
19474         mag = Math.abs(dir),
19475         new_month, test;
19476         dir = dir > 0 ? 1 : -1;
19477         if (mag == 1){
19478             test = dir == -1
19479             // If going back one month, make sure month is not current month
19480             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19481             ? function(){
19482                 return new_date.getUTCMonth() == month;
19483             }
19484             // If going forward one month, make sure month is as expected
19485             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19486             : function(){
19487                 return new_date.getUTCMonth() != new_month;
19488             };
19489             new_month = month + dir;
19490             new_date.setUTCMonth(new_month);
19491             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19492             if (new_month < 0 || new_month > 11) {
19493                 new_month = (new_month + 12) % 12;
19494             }
19495         } else {
19496             // For magnitudes >1, move one month at a time...
19497             for (var i=0; i<mag; i++) {
19498                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19499                 new_date = this.moveMonth(new_date, dir);
19500             }
19501             // ...then reset the day, keeping it in the new month
19502             new_month = new_date.getUTCMonth();
19503             new_date.setUTCDate(day);
19504             test = function(){
19505                 return new_month != new_date.getUTCMonth();
19506             };
19507         }
19508         // Common date-resetting loop -- if date is beyond end of month, make it
19509         // end of month
19510         while (test()){
19511             new_date.setUTCDate(--day);
19512             new_date.setUTCMonth(new_month);
19513         }
19514         return new_date;
19515     },
19516
19517     moveYear: function(date, dir)
19518     {
19519         return this.moveMonth(date, dir*12);
19520     },
19521
19522     dateWithinRange: function(date)
19523     {
19524         return date >= this.startDate && date <= this.endDate;
19525     },
19526
19527     
19528     remove: function() 
19529     {
19530         this.picker().remove();
19531     },
19532     
19533     validateValue : function(value)
19534     {
19535         if(this.getVisibilityEl().hasClass('hidden')){
19536             return true;
19537         }
19538         
19539         if(value.length < 1)  {
19540             if(this.allowBlank){
19541                 return true;
19542             }
19543             return false;
19544         }
19545         
19546         if(value.length < this.minLength){
19547             return false;
19548         }
19549         if(value.length > this.maxLength){
19550             return false;
19551         }
19552         if(this.vtype){
19553             var vt = Roo.form.VTypes;
19554             if(!vt[this.vtype](value, this)){
19555                 return false;
19556             }
19557         }
19558         if(typeof this.validator == "function"){
19559             var msg = this.validator(value);
19560             if(msg !== true){
19561                 return false;
19562             }
19563         }
19564         
19565         if(this.regex && !this.regex.test(value)){
19566             return false;
19567         }
19568         
19569         if(typeof(this.parseDate(value)) == 'undefined'){
19570             return false;
19571         }
19572         
19573         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19574             return false;
19575         }      
19576         
19577         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19578             return false;
19579         } 
19580         
19581         
19582         return true;
19583     },
19584     
19585     reset : function()
19586     {
19587         this.date = this.viewDate = '';
19588         
19589         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19590     }
19591    
19592 });
19593
19594 Roo.apply(Roo.bootstrap.DateField,  {
19595     
19596     head : {
19597         tag: 'thead',
19598         cn: [
19599         {
19600             tag: 'tr',
19601             cn: [
19602             {
19603                 tag: 'th',
19604                 cls: 'prev',
19605                 html: '<i class="fa fa-arrow-left"/>'
19606             },
19607             {
19608                 tag: 'th',
19609                 cls: 'switch',
19610                 colspan: '5'
19611             },
19612             {
19613                 tag: 'th',
19614                 cls: 'next',
19615                 html: '<i class="fa fa-arrow-right"/>'
19616             }
19617
19618             ]
19619         }
19620         ]
19621     },
19622     
19623     content : {
19624         tag: 'tbody',
19625         cn: [
19626         {
19627             tag: 'tr',
19628             cn: [
19629             {
19630                 tag: 'td',
19631                 colspan: '7'
19632             }
19633             ]
19634         }
19635         ]
19636     },
19637     
19638     footer : {
19639         tag: 'tfoot',
19640         cn: [
19641         {
19642             tag: 'tr',
19643             cn: [
19644             {
19645                 tag: 'th',
19646                 colspan: '7',
19647                 cls: 'today'
19648             }
19649                     
19650             ]
19651         }
19652         ]
19653     },
19654     
19655     dates:{
19656         en: {
19657             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19658             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19659             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19660             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19661             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19662             today: "Today"
19663         }
19664     },
19665     
19666     modes: [
19667     {
19668         clsName: 'days',
19669         navFnc: 'Month',
19670         navStep: 1
19671     },
19672     {
19673         clsName: 'months',
19674         navFnc: 'FullYear',
19675         navStep: 1
19676     },
19677     {
19678         clsName: 'years',
19679         navFnc: 'FullYear',
19680         navStep: 10
19681     }]
19682 });
19683
19684 Roo.apply(Roo.bootstrap.DateField,  {
19685   
19686     template : {
19687         tag: 'div',
19688         cls: 'datepicker dropdown-menu roo-dynamic',
19689         cn: [
19690         {
19691             tag: 'div',
19692             cls: 'datepicker-days',
19693             cn: [
19694             {
19695                 tag: 'table',
19696                 cls: 'table-condensed',
19697                 cn:[
19698                 Roo.bootstrap.DateField.head,
19699                 {
19700                     tag: 'tbody'
19701                 },
19702                 Roo.bootstrap.DateField.footer
19703                 ]
19704             }
19705             ]
19706         },
19707         {
19708             tag: 'div',
19709             cls: 'datepicker-months',
19710             cn: [
19711             {
19712                 tag: 'table',
19713                 cls: 'table-condensed',
19714                 cn:[
19715                 Roo.bootstrap.DateField.head,
19716                 Roo.bootstrap.DateField.content,
19717                 Roo.bootstrap.DateField.footer
19718                 ]
19719             }
19720             ]
19721         },
19722         {
19723             tag: 'div',
19724             cls: 'datepicker-years',
19725             cn: [
19726             {
19727                 tag: 'table',
19728                 cls: 'table-condensed',
19729                 cn:[
19730                 Roo.bootstrap.DateField.head,
19731                 Roo.bootstrap.DateField.content,
19732                 Roo.bootstrap.DateField.footer
19733                 ]
19734             }
19735             ]
19736         }
19737         ]
19738     }
19739 });
19740
19741  
19742
19743  /*
19744  * - LGPL
19745  *
19746  * TimeField
19747  * 
19748  */
19749
19750 /**
19751  * @class Roo.bootstrap.TimeField
19752  * @extends Roo.bootstrap.Input
19753  * Bootstrap DateField class
19754  * 
19755  * 
19756  * @constructor
19757  * Create a new TimeField
19758  * @param {Object} config The config object
19759  */
19760
19761 Roo.bootstrap.TimeField = function(config){
19762     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19763     this.addEvents({
19764             /**
19765              * @event show
19766              * Fires when this field show.
19767              * @param {Roo.bootstrap.DateField} thisthis
19768              * @param {Mixed} date The date value
19769              */
19770             show : true,
19771             /**
19772              * @event show
19773              * Fires when this field hide.
19774              * @param {Roo.bootstrap.DateField} this
19775              * @param {Mixed} date The date value
19776              */
19777             hide : true,
19778             /**
19779              * @event select
19780              * Fires when select a date.
19781              * @param {Roo.bootstrap.DateField} this
19782              * @param {Mixed} date The date value
19783              */
19784             select : true
19785         });
19786 };
19787
19788 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19789     
19790     /**
19791      * @cfg {String} format
19792      * The default time format string which can be overriden for localization support.  The format must be
19793      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19794      */
19795     format : "H:i",
19796        
19797     onRender: function(ct, position)
19798     {
19799         
19800         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19801                 
19802         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19803         
19804         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19805         
19806         this.pop = this.picker().select('>.datepicker-time',true).first();
19807         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19808         
19809         this.picker().on('mousedown', this.onMousedown, this);
19810         this.picker().on('click', this.onClick, this);
19811         
19812         this.picker().addClass('datepicker-dropdown');
19813     
19814         this.fillTime();
19815         this.update();
19816             
19817         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19818         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19819         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19820         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19821         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19822         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19823
19824     },
19825     
19826     fireKey: function(e){
19827         if (!this.picker().isVisible()){
19828             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19829                 this.show();
19830             }
19831             return;
19832         }
19833
19834         e.preventDefault();
19835         
19836         switch(e.keyCode){
19837             case 27: // escape
19838                 this.hide();
19839                 break;
19840             case 37: // left
19841             case 39: // right
19842                 this.onTogglePeriod();
19843                 break;
19844             case 38: // up
19845                 this.onIncrementMinutes();
19846                 break;
19847             case 40: // down
19848                 this.onDecrementMinutes();
19849                 break;
19850             case 13: // enter
19851             case 9: // tab
19852                 this.setTime();
19853                 break;
19854         }
19855     },
19856     
19857     onClick: function(e) {
19858         e.stopPropagation();
19859         e.preventDefault();
19860     },
19861     
19862     picker : function()
19863     {
19864         return this.el.select('.datepicker', true).first();
19865     },
19866     
19867     fillTime: function()
19868     {    
19869         var time = this.pop.select('tbody', true).first();
19870         
19871         time.dom.innerHTML = '';
19872         
19873         time.createChild({
19874             tag: 'tr',
19875             cn: [
19876                 {
19877                     tag: 'td',
19878                     cn: [
19879                         {
19880                             tag: 'a',
19881                             href: '#',
19882                             cls: 'btn',
19883                             cn: [
19884                                 {
19885                                     tag: 'span',
19886                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19887                                 }
19888                             ]
19889                         } 
19890                     ]
19891                 },
19892                 {
19893                     tag: 'td',
19894                     cls: 'separator'
19895                 },
19896                 {
19897                     tag: 'td',
19898                     cn: [
19899                         {
19900                             tag: 'a',
19901                             href: '#',
19902                             cls: 'btn',
19903                             cn: [
19904                                 {
19905                                     tag: 'span',
19906                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19907                                 }
19908                             ]
19909                         }
19910                     ]
19911                 },
19912                 {
19913                     tag: 'td',
19914                     cls: 'separator'
19915                 }
19916             ]
19917         });
19918         
19919         time.createChild({
19920             tag: 'tr',
19921             cn: [
19922                 {
19923                     tag: 'td',
19924                     cn: [
19925                         {
19926                             tag: 'span',
19927                             cls: 'timepicker-hour',
19928                             html: '00'
19929                         }  
19930                     ]
19931                 },
19932                 {
19933                     tag: 'td',
19934                     cls: 'separator',
19935                     html: ':'
19936                 },
19937                 {
19938                     tag: 'td',
19939                     cn: [
19940                         {
19941                             tag: 'span',
19942                             cls: 'timepicker-minute',
19943                             html: '00'
19944                         }  
19945                     ]
19946                 },
19947                 {
19948                     tag: 'td',
19949                     cls: 'separator'
19950                 },
19951                 {
19952                     tag: 'td',
19953                     cn: [
19954                         {
19955                             tag: 'button',
19956                             type: 'button',
19957                             cls: 'btn btn-primary period',
19958                             html: 'AM'
19959                             
19960                         }
19961                     ]
19962                 }
19963             ]
19964         });
19965         
19966         time.createChild({
19967             tag: 'tr',
19968             cn: [
19969                 {
19970                     tag: 'td',
19971                     cn: [
19972                         {
19973                             tag: 'a',
19974                             href: '#',
19975                             cls: 'btn',
19976                             cn: [
19977                                 {
19978                                     tag: 'span',
19979                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19980                                 }
19981                             ]
19982                         }
19983                     ]
19984                 },
19985                 {
19986                     tag: 'td',
19987                     cls: 'separator'
19988                 },
19989                 {
19990                     tag: 'td',
19991                     cn: [
19992                         {
19993                             tag: 'a',
19994                             href: '#',
19995                             cls: 'btn',
19996                             cn: [
19997                                 {
19998                                     tag: 'span',
19999                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20000                                 }
20001                             ]
20002                         }
20003                     ]
20004                 },
20005                 {
20006                     tag: 'td',
20007                     cls: 'separator'
20008                 }
20009             ]
20010         });
20011         
20012     },
20013     
20014     update: function()
20015     {
20016         
20017         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20018         
20019         this.fill();
20020     },
20021     
20022     fill: function() 
20023     {
20024         var hours = this.time.getHours();
20025         var minutes = this.time.getMinutes();
20026         var period = 'AM';
20027         
20028         if(hours > 11){
20029             period = 'PM';
20030         }
20031         
20032         if(hours == 0){
20033             hours = 12;
20034         }
20035         
20036         
20037         if(hours > 12){
20038             hours = hours - 12;
20039         }
20040         
20041         if(hours < 10){
20042             hours = '0' + hours;
20043         }
20044         
20045         if(minutes < 10){
20046             minutes = '0' + minutes;
20047         }
20048         
20049         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20050         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20051         this.pop.select('button', true).first().dom.innerHTML = period;
20052         
20053     },
20054     
20055     place: function()
20056     {   
20057         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20058         
20059         var cls = ['bottom'];
20060         
20061         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20062             cls.pop();
20063             cls.push('top');
20064         }
20065         
20066         cls.push('right');
20067         
20068         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20069             cls.pop();
20070             cls.push('left');
20071         }
20072         
20073         this.picker().addClass(cls.join('-'));
20074         
20075         var _this = this;
20076         
20077         Roo.each(cls, function(c){
20078             if(c == 'bottom'){
20079                 _this.picker().setTop(_this.inputEl().getHeight());
20080                 return;
20081             }
20082             if(c == 'top'){
20083                 _this.picker().setTop(0 - _this.picker().getHeight());
20084                 return;
20085             }
20086             
20087             if(c == 'left'){
20088                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20089                 return;
20090             }
20091             if(c == 'right'){
20092                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20093                 return;
20094             }
20095         });
20096         
20097     },
20098   
20099     onFocus : function()
20100     {
20101         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20102         this.show();
20103     },
20104     
20105     onBlur : function()
20106     {
20107         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20108         this.hide();
20109     },
20110     
20111     show : function()
20112     {
20113         this.picker().show();
20114         this.pop.show();
20115         this.update();
20116         this.place();
20117         
20118         this.fireEvent('show', this, this.date);
20119     },
20120     
20121     hide : function()
20122     {
20123         this.picker().hide();
20124         this.pop.hide();
20125         
20126         this.fireEvent('hide', this, this.date);
20127     },
20128     
20129     setTime : function()
20130     {
20131         this.hide();
20132         this.setValue(this.time.format(this.format));
20133         
20134         this.fireEvent('select', this, this.date);
20135         
20136         
20137     },
20138     
20139     onMousedown: function(e){
20140         e.stopPropagation();
20141         e.preventDefault();
20142     },
20143     
20144     onIncrementHours: function()
20145     {
20146         Roo.log('onIncrementHours');
20147         this.time = this.time.add(Date.HOUR, 1);
20148         this.update();
20149         
20150     },
20151     
20152     onDecrementHours: function()
20153     {
20154         Roo.log('onDecrementHours');
20155         this.time = this.time.add(Date.HOUR, -1);
20156         this.update();
20157     },
20158     
20159     onIncrementMinutes: function()
20160     {
20161         Roo.log('onIncrementMinutes');
20162         this.time = this.time.add(Date.MINUTE, 1);
20163         this.update();
20164     },
20165     
20166     onDecrementMinutes: function()
20167     {
20168         Roo.log('onDecrementMinutes');
20169         this.time = this.time.add(Date.MINUTE, -1);
20170         this.update();
20171     },
20172     
20173     onTogglePeriod: function()
20174     {
20175         Roo.log('onTogglePeriod');
20176         this.time = this.time.add(Date.HOUR, 12);
20177         this.update();
20178     }
20179     
20180    
20181 });
20182
20183 Roo.apply(Roo.bootstrap.TimeField,  {
20184     
20185     content : {
20186         tag: 'tbody',
20187         cn: [
20188             {
20189                 tag: 'tr',
20190                 cn: [
20191                 {
20192                     tag: 'td',
20193                     colspan: '7'
20194                 }
20195                 ]
20196             }
20197         ]
20198     },
20199     
20200     footer : {
20201         tag: 'tfoot',
20202         cn: [
20203             {
20204                 tag: 'tr',
20205                 cn: [
20206                 {
20207                     tag: 'th',
20208                     colspan: '7',
20209                     cls: '',
20210                     cn: [
20211                         {
20212                             tag: 'button',
20213                             cls: 'btn btn-info ok',
20214                             html: 'OK'
20215                         }
20216                     ]
20217                 }
20218
20219                 ]
20220             }
20221         ]
20222     }
20223 });
20224
20225 Roo.apply(Roo.bootstrap.TimeField,  {
20226   
20227     template : {
20228         tag: 'div',
20229         cls: 'datepicker dropdown-menu',
20230         cn: [
20231             {
20232                 tag: 'div',
20233                 cls: 'datepicker-time',
20234                 cn: [
20235                 {
20236                     tag: 'table',
20237                     cls: 'table-condensed',
20238                     cn:[
20239                     Roo.bootstrap.TimeField.content,
20240                     Roo.bootstrap.TimeField.footer
20241                     ]
20242                 }
20243                 ]
20244             }
20245         ]
20246     }
20247 });
20248
20249  
20250
20251  /*
20252  * - LGPL
20253  *
20254  * MonthField
20255  * 
20256  */
20257
20258 /**
20259  * @class Roo.bootstrap.MonthField
20260  * @extends Roo.bootstrap.Input
20261  * Bootstrap MonthField class
20262  * 
20263  * @cfg {String} language default en
20264  * 
20265  * @constructor
20266  * Create a new MonthField
20267  * @param {Object} config The config object
20268  */
20269
20270 Roo.bootstrap.MonthField = function(config){
20271     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20272     
20273     this.addEvents({
20274         /**
20275          * @event show
20276          * Fires when this field show.
20277          * @param {Roo.bootstrap.MonthField} this
20278          * @param {Mixed} date The date value
20279          */
20280         show : true,
20281         /**
20282          * @event show
20283          * Fires when this field hide.
20284          * @param {Roo.bootstrap.MonthField} this
20285          * @param {Mixed} date The date value
20286          */
20287         hide : true,
20288         /**
20289          * @event select
20290          * Fires when select a date.
20291          * @param {Roo.bootstrap.MonthField} this
20292          * @param {String} oldvalue The old value
20293          * @param {String} newvalue The new value
20294          */
20295         select : true
20296     });
20297 };
20298
20299 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20300     
20301     onRender: function(ct, position)
20302     {
20303         
20304         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20305         
20306         this.language = this.language || 'en';
20307         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20308         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20309         
20310         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20311         this.isInline = false;
20312         this.isInput = true;
20313         this.component = this.el.select('.add-on', true).first() || false;
20314         this.component = (this.component && this.component.length === 0) ? false : this.component;
20315         this.hasInput = this.component && this.inputEL().length;
20316         
20317         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20318         
20319         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20320         
20321         this.picker().on('mousedown', this.onMousedown, this);
20322         this.picker().on('click', this.onClick, this);
20323         
20324         this.picker().addClass('datepicker-dropdown');
20325         
20326         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20327             v.setStyle('width', '189px');
20328         });
20329         
20330         this.fillMonths();
20331         
20332         this.update();
20333         
20334         if(this.isInline) {
20335             this.show();
20336         }
20337         
20338     },
20339     
20340     setValue: function(v, suppressEvent)
20341     {   
20342         var o = this.getValue();
20343         
20344         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20345         
20346         this.update();
20347
20348         if(suppressEvent !== true){
20349             this.fireEvent('select', this, o, v);
20350         }
20351         
20352     },
20353     
20354     getValue: function()
20355     {
20356         return this.value;
20357     },
20358     
20359     onClick: function(e) 
20360     {
20361         e.stopPropagation();
20362         e.preventDefault();
20363         
20364         var target = e.getTarget();
20365         
20366         if(target.nodeName.toLowerCase() === 'i'){
20367             target = Roo.get(target).dom.parentNode;
20368         }
20369         
20370         var nodeName = target.nodeName;
20371         var className = target.className;
20372         var html = target.innerHTML;
20373         
20374         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20375             return;
20376         }
20377         
20378         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20379         
20380         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20381         
20382         this.hide();
20383                         
20384     },
20385     
20386     picker : function()
20387     {
20388         return this.pickerEl;
20389     },
20390     
20391     fillMonths: function()
20392     {    
20393         var i = 0;
20394         var months = this.picker().select('>.datepicker-months td', true).first();
20395         
20396         months.dom.innerHTML = '';
20397         
20398         while (i < 12) {
20399             var month = {
20400                 tag: 'span',
20401                 cls: 'month',
20402                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20403             };
20404             
20405             months.createChild(month);
20406         }
20407         
20408     },
20409     
20410     update: function()
20411     {
20412         var _this = this;
20413         
20414         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20415             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20416         }
20417         
20418         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20419             e.removeClass('active');
20420             
20421             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20422                 e.addClass('active');
20423             }
20424         })
20425     },
20426     
20427     place: function()
20428     {
20429         if(this.isInline) {
20430             return;
20431         }
20432         
20433         this.picker().removeClass(['bottom', 'top']);
20434         
20435         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20436             /*
20437              * place to the top of element!
20438              *
20439              */
20440             
20441             this.picker().addClass('top');
20442             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20443             
20444             return;
20445         }
20446         
20447         this.picker().addClass('bottom');
20448         
20449         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20450     },
20451     
20452     onFocus : function()
20453     {
20454         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20455         this.show();
20456     },
20457     
20458     onBlur : function()
20459     {
20460         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20461         
20462         var d = this.inputEl().getValue();
20463         
20464         this.setValue(d);
20465                 
20466         this.hide();
20467     },
20468     
20469     show : function()
20470     {
20471         this.picker().show();
20472         this.picker().select('>.datepicker-months', true).first().show();
20473         this.update();
20474         this.place();
20475         
20476         this.fireEvent('show', this, this.date);
20477     },
20478     
20479     hide : function()
20480     {
20481         if(this.isInline) {
20482             return;
20483         }
20484         this.picker().hide();
20485         this.fireEvent('hide', this, this.date);
20486         
20487     },
20488     
20489     onMousedown: function(e)
20490     {
20491         e.stopPropagation();
20492         e.preventDefault();
20493     },
20494     
20495     keyup: function(e)
20496     {
20497         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20498         this.update();
20499     },
20500
20501     fireKey: function(e)
20502     {
20503         if (!this.picker().isVisible()){
20504             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20505                 this.show();
20506             }
20507             return;
20508         }
20509         
20510         var dir;
20511         
20512         switch(e.keyCode){
20513             case 27: // escape
20514                 this.hide();
20515                 e.preventDefault();
20516                 break;
20517             case 37: // left
20518             case 39: // right
20519                 dir = e.keyCode == 37 ? -1 : 1;
20520                 
20521                 this.vIndex = this.vIndex + dir;
20522                 
20523                 if(this.vIndex < 0){
20524                     this.vIndex = 0;
20525                 }
20526                 
20527                 if(this.vIndex > 11){
20528                     this.vIndex = 11;
20529                 }
20530                 
20531                 if(isNaN(this.vIndex)){
20532                     this.vIndex = 0;
20533                 }
20534                 
20535                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20536                 
20537                 break;
20538             case 38: // up
20539             case 40: // down
20540                 
20541                 dir = e.keyCode == 38 ? -1 : 1;
20542                 
20543                 this.vIndex = this.vIndex + dir * 4;
20544                 
20545                 if(this.vIndex < 0){
20546                     this.vIndex = 0;
20547                 }
20548                 
20549                 if(this.vIndex > 11){
20550                     this.vIndex = 11;
20551                 }
20552                 
20553                 if(isNaN(this.vIndex)){
20554                     this.vIndex = 0;
20555                 }
20556                 
20557                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20558                 break;
20559                 
20560             case 13: // enter
20561                 
20562                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20563                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20564                 }
20565                 
20566                 this.hide();
20567                 e.preventDefault();
20568                 break;
20569             case 9: // tab
20570                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20571                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20572                 }
20573                 this.hide();
20574                 break;
20575             case 16: // shift
20576             case 17: // ctrl
20577             case 18: // alt
20578                 break;
20579             default :
20580                 this.hide();
20581                 
20582         }
20583     },
20584     
20585     remove: function() 
20586     {
20587         this.picker().remove();
20588     }
20589    
20590 });
20591
20592 Roo.apply(Roo.bootstrap.MonthField,  {
20593     
20594     content : {
20595         tag: 'tbody',
20596         cn: [
20597         {
20598             tag: 'tr',
20599             cn: [
20600             {
20601                 tag: 'td',
20602                 colspan: '7'
20603             }
20604             ]
20605         }
20606         ]
20607     },
20608     
20609     dates:{
20610         en: {
20611             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20612             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20613         }
20614     }
20615 });
20616
20617 Roo.apply(Roo.bootstrap.MonthField,  {
20618   
20619     template : {
20620         tag: 'div',
20621         cls: 'datepicker dropdown-menu roo-dynamic',
20622         cn: [
20623             {
20624                 tag: 'div',
20625                 cls: 'datepicker-months',
20626                 cn: [
20627                 {
20628                     tag: 'table',
20629                     cls: 'table-condensed',
20630                     cn:[
20631                         Roo.bootstrap.DateField.content
20632                     ]
20633                 }
20634                 ]
20635             }
20636         ]
20637     }
20638 });
20639
20640  
20641
20642  
20643  /*
20644  * - LGPL
20645  *
20646  * CheckBox
20647  * 
20648  */
20649
20650 /**
20651  * @class Roo.bootstrap.CheckBox
20652  * @extends Roo.bootstrap.Input
20653  * Bootstrap CheckBox class
20654  * 
20655  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20656  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20657  * @cfg {String} boxLabel The text that appears beside the checkbox
20658  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20659  * @cfg {Boolean} checked initnal the element
20660  * @cfg {Boolean} inline inline the element (default false)
20661  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20662  * @cfg {String} tooltip label tooltip
20663  * 
20664  * @constructor
20665  * Create a new CheckBox
20666  * @param {Object} config The config object
20667  */
20668
20669 Roo.bootstrap.CheckBox = function(config){
20670     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20671    
20672     this.addEvents({
20673         /**
20674         * @event check
20675         * Fires when the element is checked or unchecked.
20676         * @param {Roo.bootstrap.CheckBox} this This input
20677         * @param {Boolean} checked The new checked value
20678         */
20679        check : true,
20680        /**
20681         * @event click
20682         * Fires when the element is click.
20683         * @param {Roo.bootstrap.CheckBox} this This input
20684         */
20685        click : true
20686     });
20687     
20688 };
20689
20690 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20691   
20692     inputType: 'checkbox',
20693     inputValue: 1,
20694     valueOff: 0,
20695     boxLabel: false,
20696     checked: false,
20697     weight : false,
20698     inline: false,
20699     tooltip : '',
20700     
20701     getAutoCreate : function()
20702     {
20703         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20704         
20705         var id = Roo.id();
20706         
20707         var cfg = {};
20708         
20709         cfg.cls = 'form-group ' + this.inputType; //input-group
20710         
20711         if(this.inline){
20712             cfg.cls += ' ' + this.inputType + '-inline';
20713         }
20714         
20715         var input =  {
20716             tag: 'input',
20717             id : id,
20718             type : this.inputType,
20719             value : this.inputValue,
20720             cls : 'roo-' + this.inputType, //'form-box',
20721             placeholder : this.placeholder || ''
20722             
20723         };
20724         
20725         if(this.inputType != 'radio'){
20726             var hidden =  {
20727                 tag: 'input',
20728                 type : 'hidden',
20729                 cls : 'roo-hidden-value',
20730                 value : this.checked ? this.inputValue : this.valueOff
20731             };
20732         }
20733         
20734             
20735         if (this.weight) { // Validity check?
20736             cfg.cls += " " + this.inputType + "-" + this.weight;
20737         }
20738         
20739         if (this.disabled) {
20740             input.disabled=true;
20741         }
20742         
20743         if(this.checked){
20744             input.checked = this.checked;
20745         }
20746         
20747         if (this.name) {
20748             
20749             input.name = this.name;
20750             
20751             if(this.inputType != 'radio'){
20752                 hidden.name = this.name;
20753                 input.name = '_hidden_' + this.name;
20754             }
20755         }
20756         
20757         if (this.size) {
20758             input.cls += ' input-' + this.size;
20759         }
20760         
20761         var settings=this;
20762         
20763         ['xs','sm','md','lg'].map(function(size){
20764             if (settings[size]) {
20765                 cfg.cls += ' col-' + size + '-' + settings[size];
20766             }
20767         });
20768         
20769         var inputblock = input;
20770          
20771         if (this.before || this.after) {
20772             
20773             inputblock = {
20774                 cls : 'input-group',
20775                 cn :  [] 
20776             };
20777             
20778             if (this.before) {
20779                 inputblock.cn.push({
20780                     tag :'span',
20781                     cls : 'input-group-addon',
20782                     html : this.before
20783                 });
20784             }
20785             
20786             inputblock.cn.push(input);
20787             
20788             if(this.inputType != 'radio'){
20789                 inputblock.cn.push(hidden);
20790             }
20791             
20792             if (this.after) {
20793                 inputblock.cn.push({
20794                     tag :'span',
20795                     cls : 'input-group-addon',
20796                     html : this.after
20797                 });
20798             }
20799             
20800         }
20801         
20802         if (align ==='left' && this.fieldLabel.length) {
20803 //                Roo.log("left and has label");
20804             cfg.cn = [
20805                 {
20806                     tag: 'label',
20807                     'for' :  id,
20808                     cls : 'control-label',
20809                     html : this.fieldLabel
20810                 },
20811                 {
20812                     cls : "", 
20813                     cn: [
20814                         inputblock
20815                     ]
20816                 }
20817             ];
20818             
20819             if(this.labelWidth > 12){
20820                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20821             }
20822             
20823             if(this.labelWidth < 13 && this.labelmd == 0){
20824                 this.labelmd = this.labelWidth;
20825             }
20826             
20827             if(this.labellg > 0){
20828                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20829                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20830             }
20831             
20832             if(this.labelmd > 0){
20833                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20834                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20835             }
20836             
20837             if(this.labelsm > 0){
20838                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20839                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20840             }
20841             
20842             if(this.labelxs > 0){
20843                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20844                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20845             }
20846             
20847         } else if ( this.fieldLabel.length) {
20848 //                Roo.log(" label");
20849                 cfg.cn = [
20850                    
20851                     {
20852                         tag: this.boxLabel ? 'span' : 'label',
20853                         'for': id,
20854                         cls: 'control-label box-input-label',
20855                         //cls : 'input-group-addon',
20856                         html : this.fieldLabel
20857                     },
20858                     
20859                     inputblock
20860                     
20861                 ];
20862
20863         } else {
20864             
20865 //                Roo.log(" no label && no align");
20866                 cfg.cn = [  inputblock ] ;
20867                 
20868                 
20869         }
20870         
20871         if(this.boxLabel){
20872              var boxLabelCfg = {
20873                 tag: 'label',
20874                 //'for': id, // box label is handled by onclick - so no for...
20875                 cls: 'box-label',
20876                 html: this.boxLabel
20877             };
20878             
20879             if(this.tooltip){
20880                 boxLabelCfg.tooltip = this.tooltip;
20881             }
20882              
20883             cfg.cn.push(boxLabelCfg);
20884         }
20885         
20886         if(this.inputType != 'radio'){
20887             cfg.cn.push(hidden);
20888         }
20889         
20890         return cfg;
20891         
20892     },
20893     
20894     /**
20895      * return the real input element.
20896      */
20897     inputEl: function ()
20898     {
20899         return this.el.select('input.roo-' + this.inputType,true).first();
20900     },
20901     hiddenEl: function ()
20902     {
20903         return this.el.select('input.roo-hidden-value',true).first();
20904     },
20905     
20906     labelEl: function()
20907     {
20908         return this.el.select('label.control-label',true).first();
20909     },
20910     /* depricated... */
20911     
20912     label: function()
20913     {
20914         return this.labelEl();
20915     },
20916     
20917     boxLabelEl: function()
20918     {
20919         return this.el.select('label.box-label',true).first();
20920     },
20921     
20922     initEvents : function()
20923     {
20924 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20925         
20926         this.inputEl().on('click', this.onClick,  this);
20927         
20928         if (this.boxLabel) { 
20929             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20930         }
20931         
20932         this.startValue = this.getValue();
20933         
20934         if(this.groupId){
20935             Roo.bootstrap.CheckBox.register(this);
20936         }
20937     },
20938     
20939     onClick : function(e)
20940     {   
20941         if(this.fireEvent('click', this, e) !== false){
20942             this.setChecked(!this.checked);
20943         }
20944         
20945     },
20946     
20947     setChecked : function(state,suppressEvent)
20948     {
20949         this.startValue = this.getValue();
20950
20951         if(this.inputType == 'radio'){
20952             
20953             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20954                 e.dom.checked = false;
20955             });
20956             
20957             this.inputEl().dom.checked = true;
20958             
20959             this.inputEl().dom.value = this.inputValue;
20960             
20961             if(suppressEvent !== true){
20962                 this.fireEvent('check', this, true);
20963             }
20964             
20965             this.validate();
20966             
20967             return;
20968         }
20969         
20970         this.checked = state;
20971         
20972         this.inputEl().dom.checked = state;
20973         
20974         
20975         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20976         
20977         if(suppressEvent !== true){
20978             this.fireEvent('check', this, state);
20979         }
20980         
20981         this.validate();
20982     },
20983     
20984     getValue : function()
20985     {
20986         if(this.inputType == 'radio'){
20987             return this.getGroupValue();
20988         }
20989         
20990         return this.hiddenEl().dom.value;
20991         
20992     },
20993     
20994     getGroupValue : function()
20995     {
20996         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20997             return '';
20998         }
20999         
21000         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21001     },
21002     
21003     setValue : function(v,suppressEvent)
21004     {
21005         if(this.inputType == 'radio'){
21006             this.setGroupValue(v, suppressEvent);
21007             return;
21008         }
21009         
21010         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21011         
21012         this.validate();
21013     },
21014     
21015     setGroupValue : function(v, suppressEvent)
21016     {
21017         this.startValue = this.getValue();
21018         
21019         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21020             e.dom.checked = false;
21021             
21022             if(e.dom.value == v){
21023                 e.dom.checked = true;
21024             }
21025         });
21026         
21027         if(suppressEvent !== true){
21028             this.fireEvent('check', this, true);
21029         }
21030
21031         this.validate();
21032         
21033         return;
21034     },
21035     
21036     validate : function()
21037     {
21038         if(this.getVisibilityEl().hasClass('hidden')){
21039             return true;
21040         }
21041         
21042         if(
21043                 this.disabled || 
21044                 (this.inputType == 'radio' && this.validateRadio()) ||
21045                 (this.inputType == 'checkbox' && this.validateCheckbox())
21046         ){
21047             this.markValid();
21048             return true;
21049         }
21050         
21051         this.markInvalid();
21052         return false;
21053     },
21054     
21055     validateRadio : function()
21056     {
21057         if(this.getVisibilityEl().hasClass('hidden')){
21058             return true;
21059         }
21060         
21061         if(this.allowBlank){
21062             return true;
21063         }
21064         
21065         var valid = false;
21066         
21067         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21068             if(!e.dom.checked){
21069                 return;
21070             }
21071             
21072             valid = true;
21073             
21074             return false;
21075         });
21076         
21077         return valid;
21078     },
21079     
21080     validateCheckbox : function()
21081     {
21082         if(!this.groupId){
21083             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21084             //return (this.getValue() == this.inputValue) ? true : false;
21085         }
21086         
21087         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21088         
21089         if(!group){
21090             return false;
21091         }
21092         
21093         var r = false;
21094         
21095         for(var i in group){
21096             if(group[i].el.isVisible(true)){
21097                 r = false;
21098                 break;
21099             }
21100             
21101             r = true;
21102         }
21103         
21104         for(var i in group){
21105             if(r){
21106                 break;
21107             }
21108             
21109             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21110         }
21111         
21112         return r;
21113     },
21114     
21115     /**
21116      * Mark this field as valid
21117      */
21118     markValid : function()
21119     {
21120         var _this = this;
21121         
21122         this.fireEvent('valid', this);
21123         
21124         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21125         
21126         if(this.groupId){
21127             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21128         }
21129         
21130         if(label){
21131             label.markValid();
21132         }
21133
21134         if(this.inputType == 'radio'){
21135             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21136                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21137                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21138             });
21139             
21140             return;
21141         }
21142
21143         if(!this.groupId){
21144             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21145             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21146             return;
21147         }
21148         
21149         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21150         
21151         if(!group){
21152             return;
21153         }
21154         
21155         for(var i in group){
21156             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21157             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21158         }
21159     },
21160     
21161      /**
21162      * Mark this field as invalid
21163      * @param {String} msg The validation message
21164      */
21165     markInvalid : function(msg)
21166     {
21167         if(this.allowBlank){
21168             return;
21169         }
21170         
21171         var _this = this;
21172         
21173         this.fireEvent('invalid', this, msg);
21174         
21175         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21176         
21177         if(this.groupId){
21178             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21179         }
21180         
21181         if(label){
21182             label.markInvalid();
21183         }
21184             
21185         if(this.inputType == 'radio'){
21186             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21187                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21188                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21189             });
21190             
21191             return;
21192         }
21193         
21194         if(!this.groupId){
21195             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21196             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21197             return;
21198         }
21199         
21200         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21201         
21202         if(!group){
21203             return;
21204         }
21205         
21206         for(var i in group){
21207             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21208             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21209         }
21210         
21211     },
21212     
21213     clearInvalid : function()
21214     {
21215         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21216         
21217         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21218         
21219         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21220         
21221         if (label && label.iconEl) {
21222             label.iconEl.removeClass(label.validClass);
21223             label.iconEl.removeClass(label.invalidClass);
21224         }
21225     },
21226     
21227     disable : function()
21228     {
21229         if(this.inputType != 'radio'){
21230             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21231             return;
21232         }
21233         
21234         var _this = this;
21235         
21236         if(this.rendered){
21237             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21238                 _this.getActionEl().addClass(this.disabledClass);
21239                 e.dom.disabled = true;
21240             });
21241         }
21242         
21243         this.disabled = true;
21244         this.fireEvent("disable", this);
21245         return this;
21246     },
21247
21248     enable : function()
21249     {
21250         if(this.inputType != 'radio'){
21251             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21252             return;
21253         }
21254         
21255         var _this = this;
21256         
21257         if(this.rendered){
21258             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21259                 _this.getActionEl().removeClass(this.disabledClass);
21260                 e.dom.disabled = false;
21261             });
21262         }
21263         
21264         this.disabled = false;
21265         this.fireEvent("enable", this);
21266         return this;
21267     },
21268     
21269     setBoxLabel : function(v)
21270     {
21271         this.boxLabel = v;
21272         
21273         if(this.rendered){
21274             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21275         }
21276     }
21277
21278 });
21279
21280 Roo.apply(Roo.bootstrap.CheckBox, {
21281     
21282     groups: {},
21283     
21284      /**
21285     * register a CheckBox Group
21286     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21287     */
21288     register : function(checkbox)
21289     {
21290         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21291             this.groups[checkbox.groupId] = {};
21292         }
21293         
21294         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21295             return;
21296         }
21297         
21298         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21299         
21300     },
21301     /**
21302     * fetch a CheckBox Group based on the group ID
21303     * @param {string} the group ID
21304     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21305     */
21306     get: function(groupId) {
21307         if (typeof(this.groups[groupId]) == 'undefined') {
21308             return false;
21309         }
21310         
21311         return this.groups[groupId] ;
21312     }
21313     
21314     
21315 });
21316 /*
21317  * - LGPL
21318  *
21319  * RadioItem
21320  * 
21321  */
21322
21323 /**
21324  * @class Roo.bootstrap.Radio
21325  * @extends Roo.bootstrap.Component
21326  * Bootstrap Radio class
21327  * @cfg {String} boxLabel - the label associated
21328  * @cfg {String} value - the value of radio
21329  * 
21330  * @constructor
21331  * Create a new Radio
21332  * @param {Object} config The config object
21333  */
21334 Roo.bootstrap.Radio = function(config){
21335     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21336     
21337 };
21338
21339 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21340     
21341     boxLabel : '',
21342     
21343     value : '',
21344     
21345     getAutoCreate : function()
21346     {
21347         var cfg = {
21348             tag : 'div',
21349             cls : 'form-group radio',
21350             cn : [
21351                 {
21352                     tag : 'label',
21353                     cls : 'box-label',
21354                     html : this.boxLabel
21355                 }
21356             ]
21357         };
21358         
21359         return cfg;
21360     },
21361     
21362     initEvents : function() 
21363     {
21364         this.parent().register(this);
21365         
21366         this.el.on('click', this.onClick, this);
21367         
21368     },
21369     
21370     onClick : function(e)
21371     {
21372         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21373             this.setChecked(true);
21374         }
21375     },
21376     
21377     setChecked : function(state, suppressEvent)
21378     {
21379         this.parent().setValue(this.value, suppressEvent);
21380         
21381     },
21382     
21383     setBoxLabel : function(v)
21384     {
21385         this.boxLabel = v;
21386         
21387         if(this.rendered){
21388             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21389         }
21390     }
21391     
21392 });
21393  
21394
21395  /*
21396  * - LGPL
21397  *
21398  * Input
21399  * 
21400  */
21401
21402 /**
21403  * @class Roo.bootstrap.SecurePass
21404  * @extends Roo.bootstrap.Input
21405  * Bootstrap SecurePass class
21406  *
21407  * 
21408  * @constructor
21409  * Create a new SecurePass
21410  * @param {Object} config The config object
21411  */
21412  
21413 Roo.bootstrap.SecurePass = function (config) {
21414     // these go here, so the translation tool can replace them..
21415     this.errors = {
21416         PwdEmpty: "Please type a password, and then retype it to confirm.",
21417         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21418         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21419         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21420         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21421         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21422         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21423         TooWeak: "Your password is Too Weak."
21424     },
21425     this.meterLabel = "Password strength:";
21426     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21427     this.meterClass = [
21428         "roo-password-meter-tooweak", 
21429         "roo-password-meter-weak", 
21430         "roo-password-meter-medium", 
21431         "roo-password-meter-strong", 
21432         "roo-password-meter-grey"
21433     ];
21434     
21435     this.errors = {};
21436     
21437     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21438 }
21439
21440 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21441     /**
21442      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21443      * {
21444      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21445      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21446      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21447      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21448      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21449      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21450      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21451      * })
21452      */
21453     // private
21454     
21455     meterWidth: 300,
21456     errorMsg :'',    
21457     errors: false,
21458     imageRoot: '/',
21459     /**
21460      * @cfg {String/Object} Label for the strength meter (defaults to
21461      * 'Password strength:')
21462      */
21463     // private
21464     meterLabel: '',
21465     /**
21466      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21467      * ['Weak', 'Medium', 'Strong'])
21468      */
21469     // private    
21470     pwdStrengths: false,    
21471     // private
21472     strength: 0,
21473     // private
21474     _lastPwd: null,
21475     // private
21476     kCapitalLetter: 0,
21477     kSmallLetter: 1,
21478     kDigit: 2,
21479     kPunctuation: 3,
21480     
21481     insecure: false,
21482     // private
21483     initEvents: function ()
21484     {
21485         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21486
21487         if (this.el.is('input[type=password]') && Roo.isSafari) {
21488             this.el.on('keydown', this.SafariOnKeyDown, this);
21489         }
21490
21491         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21492     },
21493     // private
21494     onRender: function (ct, position)
21495     {
21496         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21497         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21498         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21499
21500         this.trigger.createChild({
21501                    cn: [
21502                     {
21503                     //id: 'PwdMeter',
21504                     tag: 'div',
21505                     cls: 'roo-password-meter-grey col-xs-12',
21506                     style: {
21507                         //width: 0,
21508                         //width: this.meterWidth + 'px'                                                
21509                         }
21510                     },
21511                     {                            
21512                          cls: 'roo-password-meter-text'                          
21513                     }
21514                 ]            
21515         });
21516
21517          
21518         if (this.hideTrigger) {
21519             this.trigger.setDisplayed(false);
21520         }
21521         this.setSize(this.width || '', this.height || '');
21522     },
21523     // private
21524     onDestroy: function ()
21525     {
21526         if (this.trigger) {
21527             this.trigger.removeAllListeners();
21528             this.trigger.remove();
21529         }
21530         if (this.wrap) {
21531             this.wrap.remove();
21532         }
21533         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21534     },
21535     // private
21536     checkStrength: function ()
21537     {
21538         var pwd = this.inputEl().getValue();
21539         if (pwd == this._lastPwd) {
21540             return;
21541         }
21542
21543         var strength;
21544         if (this.ClientSideStrongPassword(pwd)) {
21545             strength = 3;
21546         } else if (this.ClientSideMediumPassword(pwd)) {
21547             strength = 2;
21548         } else if (this.ClientSideWeakPassword(pwd)) {
21549             strength = 1;
21550         } else {
21551             strength = 0;
21552         }
21553         
21554         Roo.log('strength1: ' + strength);
21555         
21556         //var pm = this.trigger.child('div/div/div').dom;
21557         var pm = this.trigger.child('div/div');
21558         pm.removeClass(this.meterClass);
21559         pm.addClass(this.meterClass[strength]);
21560                 
21561         
21562         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21563                 
21564         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21565         
21566         this._lastPwd = pwd;
21567     },
21568     reset: function ()
21569     {
21570         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21571         
21572         this._lastPwd = '';
21573         
21574         var pm = this.trigger.child('div/div');
21575         pm.removeClass(this.meterClass);
21576         pm.addClass('roo-password-meter-grey');        
21577         
21578         
21579         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21580         
21581         pt.innerHTML = '';
21582         this.inputEl().dom.type='password';
21583     },
21584     // private
21585     validateValue: function (value)
21586     {
21587         
21588         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21589             return false;
21590         }
21591         if (value.length == 0) {
21592             if (this.allowBlank) {
21593                 this.clearInvalid();
21594                 return true;
21595             }
21596
21597             this.markInvalid(this.errors.PwdEmpty);
21598             this.errorMsg = this.errors.PwdEmpty;
21599             return false;
21600         }
21601         
21602         if(this.insecure){
21603             return true;
21604         }
21605         
21606         if ('[\x21-\x7e]*'.match(value)) {
21607             this.markInvalid(this.errors.PwdBadChar);
21608             this.errorMsg = this.errors.PwdBadChar;
21609             return false;
21610         }
21611         if (value.length < 6) {
21612             this.markInvalid(this.errors.PwdShort);
21613             this.errorMsg = this.errors.PwdShort;
21614             return false;
21615         }
21616         if (value.length > 16) {
21617             this.markInvalid(this.errors.PwdLong);
21618             this.errorMsg = this.errors.PwdLong;
21619             return false;
21620         }
21621         var strength;
21622         if (this.ClientSideStrongPassword(value)) {
21623             strength = 3;
21624         } else if (this.ClientSideMediumPassword(value)) {
21625             strength = 2;
21626         } else if (this.ClientSideWeakPassword(value)) {
21627             strength = 1;
21628         } else {
21629             strength = 0;
21630         }
21631
21632         
21633         if (strength < 2) {
21634             //this.markInvalid(this.errors.TooWeak);
21635             this.errorMsg = this.errors.TooWeak;
21636             //return false;
21637         }
21638         
21639         
21640         console.log('strength2: ' + strength);
21641         
21642         //var pm = this.trigger.child('div/div/div').dom;
21643         
21644         var pm = this.trigger.child('div/div');
21645         pm.removeClass(this.meterClass);
21646         pm.addClass(this.meterClass[strength]);
21647                 
21648         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21649                 
21650         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21651         
21652         this.errorMsg = ''; 
21653         return true;
21654     },
21655     // private
21656     CharacterSetChecks: function (type)
21657     {
21658         this.type = type;
21659         this.fResult = false;
21660     },
21661     // private
21662     isctype: function (character, type)
21663     {
21664         switch (type) {  
21665             case this.kCapitalLetter:
21666                 if (character >= 'A' && character <= 'Z') {
21667                     return true;
21668                 }
21669                 break;
21670             
21671             case this.kSmallLetter:
21672                 if (character >= 'a' && character <= 'z') {
21673                     return true;
21674                 }
21675                 break;
21676             
21677             case this.kDigit:
21678                 if (character >= '0' && character <= '9') {
21679                     return true;
21680                 }
21681                 break;
21682             
21683             case this.kPunctuation:
21684                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21685                     return true;
21686                 }
21687                 break;
21688             
21689             default:
21690                 return false;
21691         }
21692
21693     },
21694     // private
21695     IsLongEnough: function (pwd, size)
21696     {
21697         return !(pwd == null || isNaN(size) || pwd.length < size);
21698     },
21699     // private
21700     SpansEnoughCharacterSets: function (word, nb)
21701     {
21702         if (!this.IsLongEnough(word, nb))
21703         {
21704             return false;
21705         }
21706
21707         var characterSetChecks = new Array(
21708             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21709             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21710         );
21711         
21712         for (var index = 0; index < word.length; ++index) {
21713             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21714                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21715                     characterSetChecks[nCharSet].fResult = true;
21716                     break;
21717                 }
21718             }
21719         }
21720
21721         var nCharSets = 0;
21722         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21723             if (characterSetChecks[nCharSet].fResult) {
21724                 ++nCharSets;
21725             }
21726         }
21727
21728         if (nCharSets < nb) {
21729             return false;
21730         }
21731         return true;
21732     },
21733     // private
21734     ClientSideStrongPassword: function (pwd)
21735     {
21736         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21737     },
21738     // private
21739     ClientSideMediumPassword: function (pwd)
21740     {
21741         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21742     },
21743     // private
21744     ClientSideWeakPassword: function (pwd)
21745     {
21746         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21747     }
21748           
21749 })//<script type="text/javascript">
21750
21751 /*
21752  * Based  Ext JS Library 1.1.1
21753  * Copyright(c) 2006-2007, Ext JS, LLC.
21754  * LGPL
21755  *
21756  */
21757  
21758 /**
21759  * @class Roo.HtmlEditorCore
21760  * @extends Roo.Component
21761  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21762  *
21763  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21764  */
21765
21766 Roo.HtmlEditorCore = function(config){
21767     
21768     
21769     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21770     
21771     
21772     this.addEvents({
21773         /**
21774          * @event initialize
21775          * Fires when the editor is fully initialized (including the iframe)
21776          * @param {Roo.HtmlEditorCore} this
21777          */
21778         initialize: true,
21779         /**
21780          * @event activate
21781          * Fires when the editor is first receives the focus. Any insertion must wait
21782          * until after this event.
21783          * @param {Roo.HtmlEditorCore} this
21784          */
21785         activate: true,
21786          /**
21787          * @event beforesync
21788          * Fires before the textarea is updated with content from the editor iframe. Return false
21789          * to cancel the sync.
21790          * @param {Roo.HtmlEditorCore} this
21791          * @param {String} html
21792          */
21793         beforesync: true,
21794          /**
21795          * @event beforepush
21796          * Fires before the iframe editor is updated with content from the textarea. Return false
21797          * to cancel the push.
21798          * @param {Roo.HtmlEditorCore} this
21799          * @param {String} html
21800          */
21801         beforepush: true,
21802          /**
21803          * @event sync
21804          * Fires when the textarea is updated with content from the editor iframe.
21805          * @param {Roo.HtmlEditorCore} this
21806          * @param {String} html
21807          */
21808         sync: true,
21809          /**
21810          * @event push
21811          * Fires when the iframe editor is updated with content from the textarea.
21812          * @param {Roo.HtmlEditorCore} this
21813          * @param {String} html
21814          */
21815         push: true,
21816         
21817         /**
21818          * @event editorevent
21819          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21820          * @param {Roo.HtmlEditorCore} this
21821          */
21822         editorevent: true
21823         
21824     });
21825     
21826     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21827     
21828     // defaults : white / black...
21829     this.applyBlacklists();
21830     
21831     
21832     
21833 };
21834
21835
21836 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21837
21838
21839      /**
21840      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21841      */
21842     
21843     owner : false,
21844     
21845      /**
21846      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21847      *                        Roo.resizable.
21848      */
21849     resizable : false,
21850      /**
21851      * @cfg {Number} height (in pixels)
21852      */   
21853     height: 300,
21854    /**
21855      * @cfg {Number} width (in pixels)
21856      */   
21857     width: 500,
21858     
21859     /**
21860      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21861      * 
21862      */
21863     stylesheets: false,
21864     
21865     // id of frame..
21866     frameId: false,
21867     
21868     // private properties
21869     validationEvent : false,
21870     deferHeight: true,
21871     initialized : false,
21872     activated : false,
21873     sourceEditMode : false,
21874     onFocus : Roo.emptyFn,
21875     iframePad:3,
21876     hideMode:'offsets',
21877     
21878     clearUp: true,
21879     
21880     // blacklist + whitelisted elements..
21881     black: false,
21882     white: false,
21883      
21884     bodyCls : '',
21885
21886     /**
21887      * Protected method that will not generally be called directly. It
21888      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21889      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21890      */
21891     getDocMarkup : function(){
21892         // body styles..
21893         var st = '';
21894         
21895         // inherit styels from page...?? 
21896         if (this.stylesheets === false) {
21897             
21898             Roo.get(document.head).select('style').each(function(node) {
21899                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21900             });
21901             
21902             Roo.get(document.head).select('link').each(function(node) { 
21903                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21904             });
21905             
21906         } else if (!this.stylesheets.length) {
21907                 // simple..
21908                 st = '<style type="text/css">' +
21909                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21910                    '</style>';
21911         } else { 
21912             st = '<style type="text/css">' +
21913                     this.stylesheets +
21914                 '</style>';
21915         }
21916         
21917         st +=  '<style type="text/css">' +
21918             'IMG { cursor: pointer } ' +
21919         '</style>';
21920
21921         var cls = 'roo-htmleditor-body';
21922         
21923         if(this.bodyCls.length){
21924             cls += ' ' + this.bodyCls;
21925         }
21926         
21927         return '<html><head>' + st  +
21928             //<style type="text/css">' +
21929             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21930             //'</style>' +
21931             ' </head><body class="' +  cls + '"></body></html>';
21932     },
21933
21934     // private
21935     onRender : function(ct, position)
21936     {
21937         var _t = this;
21938         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21939         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21940         
21941         
21942         this.el.dom.style.border = '0 none';
21943         this.el.dom.setAttribute('tabIndex', -1);
21944         this.el.addClass('x-hidden hide');
21945         
21946         
21947         
21948         if(Roo.isIE){ // fix IE 1px bogus margin
21949             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21950         }
21951        
21952         
21953         this.frameId = Roo.id();
21954         
21955          
21956         
21957         var iframe = this.owner.wrap.createChild({
21958             tag: 'iframe',
21959             cls: 'form-control', // bootstrap..
21960             id: this.frameId,
21961             name: this.frameId,
21962             frameBorder : 'no',
21963             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21964         }, this.el
21965         );
21966         
21967         
21968         this.iframe = iframe.dom;
21969
21970          this.assignDocWin();
21971         
21972         this.doc.designMode = 'on';
21973        
21974         this.doc.open();
21975         this.doc.write(this.getDocMarkup());
21976         this.doc.close();
21977
21978         
21979         var task = { // must defer to wait for browser to be ready
21980             run : function(){
21981                 //console.log("run task?" + this.doc.readyState);
21982                 this.assignDocWin();
21983                 if(this.doc.body || this.doc.readyState == 'complete'){
21984                     try {
21985                         this.doc.designMode="on";
21986                     } catch (e) {
21987                         return;
21988                     }
21989                     Roo.TaskMgr.stop(task);
21990                     this.initEditor.defer(10, this);
21991                 }
21992             },
21993             interval : 10,
21994             duration: 10000,
21995             scope: this
21996         };
21997         Roo.TaskMgr.start(task);
21998
21999     },
22000
22001     // private
22002     onResize : function(w, h)
22003     {
22004          Roo.log('resize: ' +w + ',' + h );
22005         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22006         if(!this.iframe){
22007             return;
22008         }
22009         if(typeof w == 'number'){
22010             
22011             this.iframe.style.width = w + 'px';
22012         }
22013         if(typeof h == 'number'){
22014             
22015             this.iframe.style.height = h + 'px';
22016             if(this.doc){
22017                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22018             }
22019         }
22020         
22021     },
22022
22023     /**
22024      * Toggles the editor between standard and source edit mode.
22025      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22026      */
22027     toggleSourceEdit : function(sourceEditMode){
22028         
22029         this.sourceEditMode = sourceEditMode === true;
22030         
22031         if(this.sourceEditMode){
22032  
22033             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22034             
22035         }else{
22036             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22037             //this.iframe.className = '';
22038             this.deferFocus();
22039         }
22040         //this.setSize(this.owner.wrap.getSize());
22041         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22042     },
22043
22044     
22045   
22046
22047     /**
22048      * Protected method that will not generally be called directly. If you need/want
22049      * custom HTML cleanup, this is the method you should override.
22050      * @param {String} html The HTML to be cleaned
22051      * return {String} The cleaned HTML
22052      */
22053     cleanHtml : function(html){
22054         html = String(html);
22055         if(html.length > 5){
22056             if(Roo.isSafari){ // strip safari nonsense
22057                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22058             }
22059         }
22060         if(html == '&nbsp;'){
22061             html = '';
22062         }
22063         return html;
22064     },
22065
22066     /**
22067      * HTML Editor -> Textarea
22068      * Protected method that will not generally be called directly. Syncs the contents
22069      * of the editor iframe with the textarea.
22070      */
22071     syncValue : function(){
22072         if(this.initialized){
22073             var bd = (this.doc.body || this.doc.documentElement);
22074             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22075             var html = bd.innerHTML;
22076             if(Roo.isSafari){
22077                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22078                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22079                 if(m && m[1]){
22080                     html = '<div style="'+m[0]+'">' + html + '</div>';
22081                 }
22082             }
22083             html = this.cleanHtml(html);
22084             // fix up the special chars.. normaly like back quotes in word...
22085             // however we do not want to do this with chinese..
22086             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22087                 var cc = b.charCodeAt();
22088                 if (
22089                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22090                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22091                     (cc >= 0xf900 && cc < 0xfb00 )
22092                 ) {
22093                         return b;
22094                 }
22095                 return "&#"+cc+";" 
22096             });
22097             if(this.owner.fireEvent('beforesync', this, html) !== false){
22098                 this.el.dom.value = html;
22099                 this.owner.fireEvent('sync', this, html);
22100             }
22101         }
22102     },
22103
22104     /**
22105      * Protected method that will not generally be called directly. Pushes the value of the textarea
22106      * into the iframe editor.
22107      */
22108     pushValue : function(){
22109         if(this.initialized){
22110             var v = this.el.dom.value.trim();
22111             
22112 //            if(v.length < 1){
22113 //                v = '&#160;';
22114 //            }
22115             
22116             if(this.owner.fireEvent('beforepush', this, v) !== false){
22117                 var d = (this.doc.body || this.doc.documentElement);
22118                 d.innerHTML = v;
22119                 this.cleanUpPaste();
22120                 this.el.dom.value = d.innerHTML;
22121                 this.owner.fireEvent('push', this, v);
22122             }
22123         }
22124     },
22125
22126     // private
22127     deferFocus : function(){
22128         this.focus.defer(10, this);
22129     },
22130
22131     // doc'ed in Field
22132     focus : function(){
22133         if(this.win && !this.sourceEditMode){
22134             this.win.focus();
22135         }else{
22136             this.el.focus();
22137         }
22138     },
22139     
22140     assignDocWin: function()
22141     {
22142         var iframe = this.iframe;
22143         
22144          if(Roo.isIE){
22145             this.doc = iframe.contentWindow.document;
22146             this.win = iframe.contentWindow;
22147         } else {
22148 //            if (!Roo.get(this.frameId)) {
22149 //                return;
22150 //            }
22151 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22152 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22153             
22154             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22155                 return;
22156             }
22157             
22158             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22159             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22160         }
22161     },
22162     
22163     // private
22164     initEditor : function(){
22165         //console.log("INIT EDITOR");
22166         this.assignDocWin();
22167         
22168         
22169         
22170         this.doc.designMode="on";
22171         this.doc.open();
22172         this.doc.write(this.getDocMarkup());
22173         this.doc.close();
22174         
22175         var dbody = (this.doc.body || this.doc.documentElement);
22176         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22177         // this copies styles from the containing element into thsi one..
22178         // not sure why we need all of this..
22179         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22180         
22181         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22182         //ss['background-attachment'] = 'fixed'; // w3c
22183         dbody.bgProperties = 'fixed'; // ie
22184         //Roo.DomHelper.applyStyles(dbody, ss);
22185         Roo.EventManager.on(this.doc, {
22186             //'mousedown': this.onEditorEvent,
22187             'mouseup': this.onEditorEvent,
22188             'dblclick': this.onEditorEvent,
22189             'click': this.onEditorEvent,
22190             'keyup': this.onEditorEvent,
22191             buffer:100,
22192             scope: this
22193         });
22194         if(Roo.isGecko){
22195             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22196         }
22197         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22198             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22199         }
22200         this.initialized = true;
22201
22202         this.owner.fireEvent('initialize', this);
22203         this.pushValue();
22204     },
22205
22206     // private
22207     onDestroy : function(){
22208         
22209         
22210         
22211         if(this.rendered){
22212             
22213             //for (var i =0; i < this.toolbars.length;i++) {
22214             //    // fixme - ask toolbars for heights?
22215             //    this.toolbars[i].onDestroy();
22216            // }
22217             
22218             //this.wrap.dom.innerHTML = '';
22219             //this.wrap.remove();
22220         }
22221     },
22222
22223     // private
22224     onFirstFocus : function(){
22225         
22226         this.assignDocWin();
22227         
22228         
22229         this.activated = true;
22230          
22231     
22232         if(Roo.isGecko){ // prevent silly gecko errors
22233             this.win.focus();
22234             var s = this.win.getSelection();
22235             if(!s.focusNode || s.focusNode.nodeType != 3){
22236                 var r = s.getRangeAt(0);
22237                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22238                 r.collapse(true);
22239                 this.deferFocus();
22240             }
22241             try{
22242                 this.execCmd('useCSS', true);
22243                 this.execCmd('styleWithCSS', false);
22244             }catch(e){}
22245         }
22246         this.owner.fireEvent('activate', this);
22247     },
22248
22249     // private
22250     adjustFont: function(btn){
22251         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22252         //if(Roo.isSafari){ // safari
22253         //    adjust *= 2;
22254        // }
22255         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22256         if(Roo.isSafari){ // safari
22257             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22258             v =  (v < 10) ? 10 : v;
22259             v =  (v > 48) ? 48 : v;
22260             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22261             
22262         }
22263         
22264         
22265         v = Math.max(1, v+adjust);
22266         
22267         this.execCmd('FontSize', v  );
22268     },
22269
22270     onEditorEvent : function(e)
22271     {
22272         this.owner.fireEvent('editorevent', this, e);
22273       //  this.updateToolbar();
22274         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22275     },
22276
22277     insertTag : function(tg)
22278     {
22279         // could be a bit smarter... -> wrap the current selected tRoo..
22280         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22281             
22282             range = this.createRange(this.getSelection());
22283             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22284             wrappingNode.appendChild(range.extractContents());
22285             range.insertNode(wrappingNode);
22286
22287             return;
22288             
22289             
22290             
22291         }
22292         this.execCmd("formatblock",   tg);
22293         
22294     },
22295     
22296     insertText : function(txt)
22297     {
22298         
22299         
22300         var range = this.createRange();
22301         range.deleteContents();
22302                //alert(Sender.getAttribute('label'));
22303                
22304         range.insertNode(this.doc.createTextNode(txt));
22305     } ,
22306     
22307      
22308
22309     /**
22310      * Executes a Midas editor command on the editor document and performs necessary focus and
22311      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22312      * @param {String} cmd The Midas command
22313      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22314      */
22315     relayCmd : function(cmd, value){
22316         this.win.focus();
22317         this.execCmd(cmd, value);
22318         this.owner.fireEvent('editorevent', this);
22319         //this.updateToolbar();
22320         this.owner.deferFocus();
22321     },
22322
22323     /**
22324      * Executes a Midas editor command directly on the editor document.
22325      * For visual commands, you should use {@link #relayCmd} instead.
22326      * <b>This should only be called after the editor is initialized.</b>
22327      * @param {String} cmd The Midas command
22328      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22329      */
22330     execCmd : function(cmd, value){
22331         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22332         this.syncValue();
22333     },
22334  
22335  
22336    
22337     /**
22338      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22339      * to insert tRoo.
22340      * @param {String} text | dom node.. 
22341      */
22342     insertAtCursor : function(text)
22343     {
22344         
22345         if(!this.activated){
22346             return;
22347         }
22348         /*
22349         if(Roo.isIE){
22350             this.win.focus();
22351             var r = this.doc.selection.createRange();
22352             if(r){
22353                 r.collapse(true);
22354                 r.pasteHTML(text);
22355                 this.syncValue();
22356                 this.deferFocus();
22357             
22358             }
22359             return;
22360         }
22361         */
22362         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22363             this.win.focus();
22364             
22365             
22366             // from jquery ui (MIT licenced)
22367             var range, node;
22368             var win = this.win;
22369             
22370             if (win.getSelection && win.getSelection().getRangeAt) {
22371                 range = win.getSelection().getRangeAt(0);
22372                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22373                 range.insertNode(node);
22374             } else if (win.document.selection && win.document.selection.createRange) {
22375                 // no firefox support
22376                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22377                 win.document.selection.createRange().pasteHTML(txt);
22378             } else {
22379                 // no firefox support
22380                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22381                 this.execCmd('InsertHTML', txt);
22382             } 
22383             
22384             this.syncValue();
22385             
22386             this.deferFocus();
22387         }
22388     },
22389  // private
22390     mozKeyPress : function(e){
22391         if(e.ctrlKey){
22392             var c = e.getCharCode(), cmd;
22393           
22394             if(c > 0){
22395                 c = String.fromCharCode(c).toLowerCase();
22396                 switch(c){
22397                     case 'b':
22398                         cmd = 'bold';
22399                         break;
22400                     case 'i':
22401                         cmd = 'italic';
22402                         break;
22403                     
22404                     case 'u':
22405                         cmd = 'underline';
22406                         break;
22407                     
22408                     case 'v':
22409                         this.cleanUpPaste.defer(100, this);
22410                         return;
22411                         
22412                 }
22413                 if(cmd){
22414                     this.win.focus();
22415                     this.execCmd(cmd);
22416                     this.deferFocus();
22417                     e.preventDefault();
22418                 }
22419                 
22420             }
22421         }
22422     },
22423
22424     // private
22425     fixKeys : function(){ // load time branching for fastest keydown performance
22426         if(Roo.isIE){
22427             return function(e){
22428                 var k = e.getKey(), r;
22429                 if(k == e.TAB){
22430                     e.stopEvent();
22431                     r = this.doc.selection.createRange();
22432                     if(r){
22433                         r.collapse(true);
22434                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22435                         this.deferFocus();
22436                     }
22437                     return;
22438                 }
22439                 
22440                 if(k == e.ENTER){
22441                     r = this.doc.selection.createRange();
22442                     if(r){
22443                         var target = r.parentElement();
22444                         if(!target || target.tagName.toLowerCase() != 'li'){
22445                             e.stopEvent();
22446                             r.pasteHTML('<br />');
22447                             r.collapse(false);
22448                             r.select();
22449                         }
22450                     }
22451                 }
22452                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22453                     this.cleanUpPaste.defer(100, this);
22454                     return;
22455                 }
22456                 
22457                 
22458             };
22459         }else if(Roo.isOpera){
22460             return function(e){
22461                 var k = e.getKey();
22462                 if(k == e.TAB){
22463                     e.stopEvent();
22464                     this.win.focus();
22465                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22466                     this.deferFocus();
22467                 }
22468                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22469                     this.cleanUpPaste.defer(100, this);
22470                     return;
22471                 }
22472                 
22473             };
22474         }else if(Roo.isSafari){
22475             return function(e){
22476                 var k = e.getKey();
22477                 
22478                 if(k == e.TAB){
22479                     e.stopEvent();
22480                     this.execCmd('InsertText','\t');
22481                     this.deferFocus();
22482                     return;
22483                 }
22484                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22485                     this.cleanUpPaste.defer(100, this);
22486                     return;
22487                 }
22488                 
22489              };
22490         }
22491     }(),
22492     
22493     getAllAncestors: function()
22494     {
22495         var p = this.getSelectedNode();
22496         var a = [];
22497         if (!p) {
22498             a.push(p); // push blank onto stack..
22499             p = this.getParentElement();
22500         }
22501         
22502         
22503         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22504             a.push(p);
22505             p = p.parentNode;
22506         }
22507         a.push(this.doc.body);
22508         return a;
22509     },
22510     lastSel : false,
22511     lastSelNode : false,
22512     
22513     
22514     getSelection : function() 
22515     {
22516         this.assignDocWin();
22517         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22518     },
22519     
22520     getSelectedNode: function() 
22521     {
22522         // this may only work on Gecko!!!
22523         
22524         // should we cache this!!!!
22525         
22526         
22527         
22528          
22529         var range = this.createRange(this.getSelection()).cloneRange();
22530         
22531         if (Roo.isIE) {
22532             var parent = range.parentElement();
22533             while (true) {
22534                 var testRange = range.duplicate();
22535                 testRange.moveToElementText(parent);
22536                 if (testRange.inRange(range)) {
22537                     break;
22538                 }
22539                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22540                     break;
22541                 }
22542                 parent = parent.parentElement;
22543             }
22544             return parent;
22545         }
22546         
22547         // is ancestor a text element.
22548         var ac =  range.commonAncestorContainer;
22549         if (ac.nodeType == 3) {
22550             ac = ac.parentNode;
22551         }
22552         
22553         var ar = ac.childNodes;
22554          
22555         var nodes = [];
22556         var other_nodes = [];
22557         var has_other_nodes = false;
22558         for (var i=0;i<ar.length;i++) {
22559             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22560                 continue;
22561             }
22562             // fullly contained node.
22563             
22564             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22565                 nodes.push(ar[i]);
22566                 continue;
22567             }
22568             
22569             // probably selected..
22570             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22571                 other_nodes.push(ar[i]);
22572                 continue;
22573             }
22574             // outer..
22575             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22576                 continue;
22577             }
22578             
22579             
22580             has_other_nodes = true;
22581         }
22582         if (!nodes.length && other_nodes.length) {
22583             nodes= other_nodes;
22584         }
22585         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22586             return false;
22587         }
22588         
22589         return nodes[0];
22590     },
22591     createRange: function(sel)
22592     {
22593         // this has strange effects when using with 
22594         // top toolbar - not sure if it's a great idea.
22595         //this.editor.contentWindow.focus();
22596         if (typeof sel != "undefined") {
22597             try {
22598                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22599             } catch(e) {
22600                 return this.doc.createRange();
22601             }
22602         } else {
22603             return this.doc.createRange();
22604         }
22605     },
22606     getParentElement: function()
22607     {
22608         
22609         this.assignDocWin();
22610         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22611         
22612         var range = this.createRange(sel);
22613          
22614         try {
22615             var p = range.commonAncestorContainer;
22616             while (p.nodeType == 3) { // text node
22617                 p = p.parentNode;
22618             }
22619             return p;
22620         } catch (e) {
22621             return null;
22622         }
22623     
22624     },
22625     /***
22626      *
22627      * Range intersection.. the hard stuff...
22628      *  '-1' = before
22629      *  '0' = hits..
22630      *  '1' = after.
22631      *         [ -- selected range --- ]
22632      *   [fail]                        [fail]
22633      *
22634      *    basically..
22635      *      if end is before start or  hits it. fail.
22636      *      if start is after end or hits it fail.
22637      *
22638      *   if either hits (but other is outside. - then it's not 
22639      *   
22640      *    
22641      **/
22642     
22643     
22644     // @see http://www.thismuchiknow.co.uk/?p=64.
22645     rangeIntersectsNode : function(range, node)
22646     {
22647         var nodeRange = node.ownerDocument.createRange();
22648         try {
22649             nodeRange.selectNode(node);
22650         } catch (e) {
22651             nodeRange.selectNodeContents(node);
22652         }
22653     
22654         var rangeStartRange = range.cloneRange();
22655         rangeStartRange.collapse(true);
22656     
22657         var rangeEndRange = range.cloneRange();
22658         rangeEndRange.collapse(false);
22659     
22660         var nodeStartRange = nodeRange.cloneRange();
22661         nodeStartRange.collapse(true);
22662     
22663         var nodeEndRange = nodeRange.cloneRange();
22664         nodeEndRange.collapse(false);
22665     
22666         return rangeStartRange.compareBoundaryPoints(
22667                  Range.START_TO_START, nodeEndRange) == -1 &&
22668                rangeEndRange.compareBoundaryPoints(
22669                  Range.START_TO_START, nodeStartRange) == 1;
22670         
22671          
22672     },
22673     rangeCompareNode : function(range, node)
22674     {
22675         var nodeRange = node.ownerDocument.createRange();
22676         try {
22677             nodeRange.selectNode(node);
22678         } catch (e) {
22679             nodeRange.selectNodeContents(node);
22680         }
22681         
22682         
22683         range.collapse(true);
22684     
22685         nodeRange.collapse(true);
22686      
22687         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22688         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22689          
22690         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22691         
22692         var nodeIsBefore   =  ss == 1;
22693         var nodeIsAfter    = ee == -1;
22694         
22695         if (nodeIsBefore && nodeIsAfter) {
22696             return 0; // outer
22697         }
22698         if (!nodeIsBefore && nodeIsAfter) {
22699             return 1; //right trailed.
22700         }
22701         
22702         if (nodeIsBefore && !nodeIsAfter) {
22703             return 2;  // left trailed.
22704         }
22705         // fully contined.
22706         return 3;
22707     },
22708
22709     // private? - in a new class?
22710     cleanUpPaste :  function()
22711     {
22712         // cleans up the whole document..
22713         Roo.log('cleanuppaste');
22714         
22715         this.cleanUpChildren(this.doc.body);
22716         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22717         if (clean != this.doc.body.innerHTML) {
22718             this.doc.body.innerHTML = clean;
22719         }
22720         
22721     },
22722     
22723     cleanWordChars : function(input) {// change the chars to hex code
22724         var he = Roo.HtmlEditorCore;
22725         
22726         var output = input;
22727         Roo.each(he.swapCodes, function(sw) { 
22728             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22729             
22730             output = output.replace(swapper, sw[1]);
22731         });
22732         
22733         return output;
22734     },
22735     
22736     
22737     cleanUpChildren : function (n)
22738     {
22739         if (!n.childNodes.length) {
22740             return;
22741         }
22742         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22743            this.cleanUpChild(n.childNodes[i]);
22744         }
22745     },
22746     
22747     
22748         
22749     
22750     cleanUpChild : function (node)
22751     {
22752         var ed = this;
22753         //console.log(node);
22754         if (node.nodeName == "#text") {
22755             // clean up silly Windows -- stuff?
22756             return; 
22757         }
22758         if (node.nodeName == "#comment") {
22759             node.parentNode.removeChild(node);
22760             // clean up silly Windows -- stuff?
22761             return; 
22762         }
22763         var lcname = node.tagName.toLowerCase();
22764         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22765         // whitelist of tags..
22766         
22767         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22768             // remove node.
22769             node.parentNode.removeChild(node);
22770             return;
22771             
22772         }
22773         
22774         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22775         
22776         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22777         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22778         
22779         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22780         //    remove_keep_children = true;
22781         //}
22782         
22783         if (remove_keep_children) {
22784             this.cleanUpChildren(node);
22785             // inserts everything just before this node...
22786             while (node.childNodes.length) {
22787                 var cn = node.childNodes[0];
22788                 node.removeChild(cn);
22789                 node.parentNode.insertBefore(cn, node);
22790             }
22791             node.parentNode.removeChild(node);
22792             return;
22793         }
22794         
22795         if (!node.attributes || !node.attributes.length) {
22796             this.cleanUpChildren(node);
22797             return;
22798         }
22799         
22800         function cleanAttr(n,v)
22801         {
22802             
22803             if (v.match(/^\./) || v.match(/^\//)) {
22804                 return;
22805             }
22806             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22807                 return;
22808             }
22809             if (v.match(/^#/)) {
22810                 return;
22811             }
22812 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22813             node.removeAttribute(n);
22814             
22815         }
22816         
22817         var cwhite = this.cwhite;
22818         var cblack = this.cblack;
22819             
22820         function cleanStyle(n,v)
22821         {
22822             if (v.match(/expression/)) { //XSS?? should we even bother..
22823                 node.removeAttribute(n);
22824                 return;
22825             }
22826             
22827             var parts = v.split(/;/);
22828             var clean = [];
22829             
22830             Roo.each(parts, function(p) {
22831                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22832                 if (!p.length) {
22833                     return true;
22834                 }
22835                 var l = p.split(':').shift().replace(/\s+/g,'');
22836                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22837                 
22838                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22839 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22840                     //node.removeAttribute(n);
22841                     return true;
22842                 }
22843                 //Roo.log()
22844                 // only allow 'c whitelisted system attributes'
22845                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22846 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22847                     //node.removeAttribute(n);
22848                     return true;
22849                 }
22850                 
22851                 
22852                  
22853                 
22854                 clean.push(p);
22855                 return true;
22856             });
22857             if (clean.length) { 
22858                 node.setAttribute(n, clean.join(';'));
22859             } else {
22860                 node.removeAttribute(n);
22861             }
22862             
22863         }
22864         
22865         
22866         for (var i = node.attributes.length-1; i > -1 ; i--) {
22867             var a = node.attributes[i];
22868             //console.log(a);
22869             
22870             if (a.name.toLowerCase().substr(0,2)=='on')  {
22871                 node.removeAttribute(a.name);
22872                 continue;
22873             }
22874             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22875                 node.removeAttribute(a.name);
22876                 continue;
22877             }
22878             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22879                 cleanAttr(a.name,a.value); // fixme..
22880                 continue;
22881             }
22882             if (a.name == 'style') {
22883                 cleanStyle(a.name,a.value);
22884                 continue;
22885             }
22886             /// clean up MS crap..
22887             // tecnically this should be a list of valid class'es..
22888             
22889             
22890             if (a.name == 'class') {
22891                 if (a.value.match(/^Mso/)) {
22892                     node.className = '';
22893                 }
22894                 
22895                 if (a.value.match(/^body$/)) {
22896                     node.className = '';
22897                 }
22898                 continue;
22899             }
22900             
22901             // style cleanup!?
22902             // class cleanup?
22903             
22904         }
22905         
22906         
22907         this.cleanUpChildren(node);
22908         
22909         
22910     },
22911     
22912     /**
22913      * Clean up MS wordisms...
22914      */
22915     cleanWord : function(node)
22916     {
22917         
22918         
22919         if (!node) {
22920             this.cleanWord(this.doc.body);
22921             return;
22922         }
22923         if (node.nodeName == "#text") {
22924             // clean up silly Windows -- stuff?
22925             return; 
22926         }
22927         if (node.nodeName == "#comment") {
22928             node.parentNode.removeChild(node);
22929             // clean up silly Windows -- stuff?
22930             return; 
22931         }
22932         
22933         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22934             node.parentNode.removeChild(node);
22935             return;
22936         }
22937         
22938         // remove - but keep children..
22939         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22940             while (node.childNodes.length) {
22941                 var cn = node.childNodes[0];
22942                 node.removeChild(cn);
22943                 node.parentNode.insertBefore(cn, node);
22944             }
22945             node.parentNode.removeChild(node);
22946             this.iterateChildren(node, this.cleanWord);
22947             return;
22948         }
22949         // clean styles
22950         if (node.className.length) {
22951             
22952             var cn = node.className.split(/\W+/);
22953             var cna = [];
22954             Roo.each(cn, function(cls) {
22955                 if (cls.match(/Mso[a-zA-Z]+/)) {
22956                     return;
22957                 }
22958                 cna.push(cls);
22959             });
22960             node.className = cna.length ? cna.join(' ') : '';
22961             if (!cna.length) {
22962                 node.removeAttribute("class");
22963             }
22964         }
22965         
22966         if (node.hasAttribute("lang")) {
22967             node.removeAttribute("lang");
22968         }
22969         
22970         if (node.hasAttribute("style")) {
22971             
22972             var styles = node.getAttribute("style").split(";");
22973             var nstyle = [];
22974             Roo.each(styles, function(s) {
22975                 if (!s.match(/:/)) {
22976                     return;
22977                 }
22978                 var kv = s.split(":");
22979                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22980                     return;
22981                 }
22982                 // what ever is left... we allow.
22983                 nstyle.push(s);
22984             });
22985             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22986             if (!nstyle.length) {
22987                 node.removeAttribute('style');
22988             }
22989         }
22990         this.iterateChildren(node, this.cleanWord);
22991         
22992         
22993         
22994     },
22995     /**
22996      * iterateChildren of a Node, calling fn each time, using this as the scole..
22997      * @param {DomNode} node node to iterate children of.
22998      * @param {Function} fn method of this class to call on each item.
22999      */
23000     iterateChildren : function(node, fn)
23001     {
23002         if (!node.childNodes.length) {
23003                 return;
23004         }
23005         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23006            fn.call(this, node.childNodes[i])
23007         }
23008     },
23009     
23010     
23011     /**
23012      * cleanTableWidths.
23013      *
23014      * Quite often pasting from word etc.. results in tables with column and widths.
23015      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23016      *
23017      */
23018     cleanTableWidths : function(node)
23019     {
23020          
23021          
23022         if (!node) {
23023             this.cleanTableWidths(this.doc.body);
23024             return;
23025         }
23026         
23027         // ignore list...
23028         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23029             return; 
23030         }
23031         Roo.log(node.tagName);
23032         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23033             this.iterateChildren(node, this.cleanTableWidths);
23034             return;
23035         }
23036         if (node.hasAttribute('width')) {
23037             node.removeAttribute('width');
23038         }
23039         
23040          
23041         if (node.hasAttribute("style")) {
23042             // pretty basic...
23043             
23044             var styles = node.getAttribute("style").split(";");
23045             var nstyle = [];
23046             Roo.each(styles, function(s) {
23047                 if (!s.match(/:/)) {
23048                     return;
23049                 }
23050                 var kv = s.split(":");
23051                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23052                     return;
23053                 }
23054                 // what ever is left... we allow.
23055                 nstyle.push(s);
23056             });
23057             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23058             if (!nstyle.length) {
23059                 node.removeAttribute('style');
23060             }
23061         }
23062         
23063         this.iterateChildren(node, this.cleanTableWidths);
23064         
23065         
23066     },
23067     
23068     
23069     
23070     
23071     domToHTML : function(currentElement, depth, nopadtext) {
23072         
23073         depth = depth || 0;
23074         nopadtext = nopadtext || false;
23075     
23076         if (!currentElement) {
23077             return this.domToHTML(this.doc.body);
23078         }
23079         
23080         //Roo.log(currentElement);
23081         var j;
23082         var allText = false;
23083         var nodeName = currentElement.nodeName;
23084         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23085         
23086         if  (nodeName == '#text') {
23087             
23088             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23089         }
23090         
23091         
23092         var ret = '';
23093         if (nodeName != 'BODY') {
23094              
23095             var i = 0;
23096             // Prints the node tagName, such as <A>, <IMG>, etc
23097             if (tagName) {
23098                 var attr = [];
23099                 for(i = 0; i < currentElement.attributes.length;i++) {
23100                     // quoting?
23101                     var aname = currentElement.attributes.item(i).name;
23102                     if (!currentElement.attributes.item(i).value.length) {
23103                         continue;
23104                     }
23105                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23106                 }
23107                 
23108                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23109             } 
23110             else {
23111                 
23112                 // eack
23113             }
23114         } else {
23115             tagName = false;
23116         }
23117         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23118             return ret;
23119         }
23120         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23121             nopadtext = true;
23122         }
23123         
23124         
23125         // Traverse the tree
23126         i = 0;
23127         var currentElementChild = currentElement.childNodes.item(i);
23128         var allText = true;
23129         var innerHTML  = '';
23130         lastnode = '';
23131         while (currentElementChild) {
23132             // Formatting code (indent the tree so it looks nice on the screen)
23133             var nopad = nopadtext;
23134             if (lastnode == 'SPAN') {
23135                 nopad  = true;
23136             }
23137             // text
23138             if  (currentElementChild.nodeName == '#text') {
23139                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23140                 toadd = nopadtext ? toadd : toadd.trim();
23141                 if (!nopad && toadd.length > 80) {
23142                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23143                 }
23144                 innerHTML  += toadd;
23145                 
23146                 i++;
23147                 currentElementChild = currentElement.childNodes.item(i);
23148                 lastNode = '';
23149                 continue;
23150             }
23151             allText = false;
23152             
23153             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23154                 
23155             // Recursively traverse the tree structure of the child node
23156             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23157             lastnode = currentElementChild.nodeName;
23158             i++;
23159             currentElementChild=currentElement.childNodes.item(i);
23160         }
23161         
23162         ret += innerHTML;
23163         
23164         if (!allText) {
23165                 // The remaining code is mostly for formatting the tree
23166             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23167         }
23168         
23169         
23170         if (tagName) {
23171             ret+= "</"+tagName+">";
23172         }
23173         return ret;
23174         
23175     },
23176         
23177     applyBlacklists : function()
23178     {
23179         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23180         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23181         
23182         this.white = [];
23183         this.black = [];
23184         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23185             if (b.indexOf(tag) > -1) {
23186                 return;
23187             }
23188             this.white.push(tag);
23189             
23190         }, this);
23191         
23192         Roo.each(w, function(tag) {
23193             if (b.indexOf(tag) > -1) {
23194                 return;
23195             }
23196             if (this.white.indexOf(tag) > -1) {
23197                 return;
23198             }
23199             this.white.push(tag);
23200             
23201         }, this);
23202         
23203         
23204         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23205             if (w.indexOf(tag) > -1) {
23206                 return;
23207             }
23208             this.black.push(tag);
23209             
23210         }, this);
23211         
23212         Roo.each(b, function(tag) {
23213             if (w.indexOf(tag) > -1) {
23214                 return;
23215             }
23216             if (this.black.indexOf(tag) > -1) {
23217                 return;
23218             }
23219             this.black.push(tag);
23220             
23221         }, this);
23222         
23223         
23224         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23225         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23226         
23227         this.cwhite = [];
23228         this.cblack = [];
23229         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23230             if (b.indexOf(tag) > -1) {
23231                 return;
23232             }
23233             this.cwhite.push(tag);
23234             
23235         }, this);
23236         
23237         Roo.each(w, function(tag) {
23238             if (b.indexOf(tag) > -1) {
23239                 return;
23240             }
23241             if (this.cwhite.indexOf(tag) > -1) {
23242                 return;
23243             }
23244             this.cwhite.push(tag);
23245             
23246         }, this);
23247         
23248         
23249         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23250             if (w.indexOf(tag) > -1) {
23251                 return;
23252             }
23253             this.cblack.push(tag);
23254             
23255         }, this);
23256         
23257         Roo.each(b, function(tag) {
23258             if (w.indexOf(tag) > -1) {
23259                 return;
23260             }
23261             if (this.cblack.indexOf(tag) > -1) {
23262                 return;
23263             }
23264             this.cblack.push(tag);
23265             
23266         }, this);
23267     },
23268     
23269     setStylesheets : function(stylesheets)
23270     {
23271         if(typeof(stylesheets) == 'string'){
23272             Roo.get(this.iframe.contentDocument.head).createChild({
23273                 tag : 'link',
23274                 rel : 'stylesheet',
23275                 type : 'text/css',
23276                 href : stylesheets
23277             });
23278             
23279             return;
23280         }
23281         var _this = this;
23282      
23283         Roo.each(stylesheets, function(s) {
23284             if(!s.length){
23285                 return;
23286             }
23287             
23288             Roo.get(_this.iframe.contentDocument.head).createChild({
23289                 tag : 'link',
23290                 rel : 'stylesheet',
23291                 type : 'text/css',
23292                 href : s
23293             });
23294         });
23295
23296         
23297     },
23298     
23299     removeStylesheets : function()
23300     {
23301         var _this = this;
23302         
23303         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23304             s.remove();
23305         });
23306     },
23307     
23308     setStyle : function(style)
23309     {
23310         Roo.get(this.iframe.contentDocument.head).createChild({
23311             tag : 'style',
23312             type : 'text/css',
23313             html : style
23314         });
23315
23316         return;
23317     }
23318     
23319     // hide stuff that is not compatible
23320     /**
23321      * @event blur
23322      * @hide
23323      */
23324     /**
23325      * @event change
23326      * @hide
23327      */
23328     /**
23329      * @event focus
23330      * @hide
23331      */
23332     /**
23333      * @event specialkey
23334      * @hide
23335      */
23336     /**
23337      * @cfg {String} fieldClass @hide
23338      */
23339     /**
23340      * @cfg {String} focusClass @hide
23341      */
23342     /**
23343      * @cfg {String} autoCreate @hide
23344      */
23345     /**
23346      * @cfg {String} inputType @hide
23347      */
23348     /**
23349      * @cfg {String} invalidClass @hide
23350      */
23351     /**
23352      * @cfg {String} invalidText @hide
23353      */
23354     /**
23355      * @cfg {String} msgFx @hide
23356      */
23357     /**
23358      * @cfg {String} validateOnBlur @hide
23359      */
23360 });
23361
23362 Roo.HtmlEditorCore.white = [
23363         'area', 'br', 'img', 'input', 'hr', 'wbr',
23364         
23365        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23366        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23367        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23368        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23369        'table',   'ul',         'xmp', 
23370        
23371        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23372       'thead',   'tr', 
23373      
23374       'dir', 'menu', 'ol', 'ul', 'dl',
23375        
23376       'embed',  'object'
23377 ];
23378
23379
23380 Roo.HtmlEditorCore.black = [
23381     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23382         'applet', // 
23383         'base',   'basefont', 'bgsound', 'blink',  'body', 
23384         'frame',  'frameset', 'head',    'html',   'ilayer', 
23385         'iframe', 'layer',  'link',     'meta',    'object',   
23386         'script', 'style' ,'title',  'xml' // clean later..
23387 ];
23388 Roo.HtmlEditorCore.clean = [
23389     'script', 'style', 'title', 'xml'
23390 ];
23391 Roo.HtmlEditorCore.remove = [
23392     'font'
23393 ];
23394 // attributes..
23395
23396 Roo.HtmlEditorCore.ablack = [
23397     'on'
23398 ];
23399     
23400 Roo.HtmlEditorCore.aclean = [ 
23401     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23402 ];
23403
23404 // protocols..
23405 Roo.HtmlEditorCore.pwhite= [
23406         'http',  'https',  'mailto'
23407 ];
23408
23409 // white listed style attributes.
23410 Roo.HtmlEditorCore.cwhite= [
23411       //  'text-align', /// default is to allow most things..
23412       
23413          
23414 //        'font-size'//??
23415 ];
23416
23417 // black listed style attributes.
23418 Roo.HtmlEditorCore.cblack= [
23419       //  'font-size' -- this can be set by the project 
23420 ];
23421
23422
23423 Roo.HtmlEditorCore.swapCodes   =[ 
23424     [    8211, "--" ], 
23425     [    8212, "--" ], 
23426     [    8216,  "'" ],  
23427     [    8217, "'" ],  
23428     [    8220, '"' ],  
23429     [    8221, '"' ],  
23430     [    8226, "*" ],  
23431     [    8230, "..." ]
23432 ]; 
23433
23434     /*
23435  * - LGPL
23436  *
23437  * HtmlEditor
23438  * 
23439  */
23440
23441 /**
23442  * @class Roo.bootstrap.HtmlEditor
23443  * @extends Roo.bootstrap.TextArea
23444  * Bootstrap HtmlEditor class
23445
23446  * @constructor
23447  * Create a new HtmlEditor
23448  * @param {Object} config The config object
23449  */
23450
23451 Roo.bootstrap.HtmlEditor = function(config){
23452     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23453     if (!this.toolbars) {
23454         this.toolbars = [];
23455     }
23456     
23457     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23458     this.addEvents({
23459             /**
23460              * @event initialize
23461              * Fires when the editor is fully initialized (including the iframe)
23462              * @param {HtmlEditor} this
23463              */
23464             initialize: true,
23465             /**
23466              * @event activate
23467              * Fires when the editor is first receives the focus. Any insertion must wait
23468              * until after this event.
23469              * @param {HtmlEditor} this
23470              */
23471             activate: true,
23472              /**
23473              * @event beforesync
23474              * Fires before the textarea is updated with content from the editor iframe. Return false
23475              * to cancel the sync.
23476              * @param {HtmlEditor} this
23477              * @param {String} html
23478              */
23479             beforesync: true,
23480              /**
23481              * @event beforepush
23482              * Fires before the iframe editor is updated with content from the textarea. Return false
23483              * to cancel the push.
23484              * @param {HtmlEditor} this
23485              * @param {String} html
23486              */
23487             beforepush: true,
23488              /**
23489              * @event sync
23490              * Fires when the textarea is updated with content from the editor iframe.
23491              * @param {HtmlEditor} this
23492              * @param {String} html
23493              */
23494             sync: true,
23495              /**
23496              * @event push
23497              * Fires when the iframe editor is updated with content from the textarea.
23498              * @param {HtmlEditor} this
23499              * @param {String} html
23500              */
23501             push: true,
23502              /**
23503              * @event editmodechange
23504              * Fires when the editor switches edit modes
23505              * @param {HtmlEditor} this
23506              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23507              */
23508             editmodechange: true,
23509             /**
23510              * @event editorevent
23511              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23512              * @param {HtmlEditor} this
23513              */
23514             editorevent: true,
23515             /**
23516              * @event firstfocus
23517              * Fires when on first focus - needed by toolbars..
23518              * @param {HtmlEditor} this
23519              */
23520             firstfocus: true,
23521             /**
23522              * @event autosave
23523              * Auto save the htmlEditor value as a file into Events
23524              * @param {HtmlEditor} this
23525              */
23526             autosave: true,
23527             /**
23528              * @event savedpreview
23529              * preview the saved version of htmlEditor
23530              * @param {HtmlEditor} this
23531              */
23532             savedpreview: true
23533         });
23534 };
23535
23536
23537 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23538     
23539     
23540       /**
23541      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23542      */
23543     toolbars : false,
23544     
23545      /**
23546     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23547     */
23548     btns : [],
23549    
23550      /**
23551      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23552      *                        Roo.resizable.
23553      */
23554     resizable : false,
23555      /**
23556      * @cfg {Number} height (in pixels)
23557      */   
23558     height: 300,
23559    /**
23560      * @cfg {Number} width (in pixels)
23561      */   
23562     width: false,
23563     
23564     /**
23565      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23566      * 
23567      */
23568     stylesheets: false,
23569     
23570     // id of frame..
23571     frameId: false,
23572     
23573     // private properties
23574     validationEvent : false,
23575     deferHeight: true,
23576     initialized : false,
23577     activated : false,
23578     
23579     onFocus : Roo.emptyFn,
23580     iframePad:3,
23581     hideMode:'offsets',
23582     
23583     tbContainer : false,
23584     
23585     bodyCls : '',
23586     
23587     toolbarContainer :function() {
23588         return this.wrap.select('.x-html-editor-tb',true).first();
23589     },
23590
23591     /**
23592      * Protected method that will not generally be called directly. It
23593      * is called when the editor creates its toolbar. Override this method if you need to
23594      * add custom toolbar buttons.
23595      * @param {HtmlEditor} editor
23596      */
23597     createToolbar : function(){
23598         Roo.log('renewing');
23599         Roo.log("create toolbars");
23600         
23601         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23602         this.toolbars[0].render(this.toolbarContainer());
23603         
23604         return;
23605         
23606 //        if (!editor.toolbars || !editor.toolbars.length) {
23607 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23608 //        }
23609 //        
23610 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23611 //            editor.toolbars[i] = Roo.factory(
23612 //                    typeof(editor.toolbars[i]) == 'string' ?
23613 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23614 //                Roo.bootstrap.HtmlEditor);
23615 //            editor.toolbars[i].init(editor);
23616 //        }
23617     },
23618
23619      
23620     // private
23621     onRender : function(ct, position)
23622     {
23623        // Roo.log("Call onRender: " + this.xtype);
23624         var _t = this;
23625         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23626       
23627         this.wrap = this.inputEl().wrap({
23628             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23629         });
23630         
23631         this.editorcore.onRender(ct, position);
23632          
23633         if (this.resizable) {
23634             this.resizeEl = new Roo.Resizable(this.wrap, {
23635                 pinned : true,
23636                 wrap: true,
23637                 dynamic : true,
23638                 minHeight : this.height,
23639                 height: this.height,
23640                 handles : this.resizable,
23641                 width: this.width,
23642                 listeners : {
23643                     resize : function(r, w, h) {
23644                         _t.onResize(w,h); // -something
23645                     }
23646                 }
23647             });
23648             
23649         }
23650         this.createToolbar(this);
23651        
23652         
23653         if(!this.width && this.resizable){
23654             this.setSize(this.wrap.getSize());
23655         }
23656         if (this.resizeEl) {
23657             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23658             // should trigger onReize..
23659         }
23660         
23661     },
23662
23663     // private
23664     onResize : function(w, h)
23665     {
23666         Roo.log('resize: ' +w + ',' + h );
23667         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23668         var ew = false;
23669         var eh = false;
23670         
23671         if(this.inputEl() ){
23672             if(typeof w == 'number'){
23673                 var aw = w - this.wrap.getFrameWidth('lr');
23674                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23675                 ew = aw;
23676             }
23677             if(typeof h == 'number'){
23678                  var tbh = -11;  // fixme it needs to tool bar size!
23679                 for (var i =0; i < this.toolbars.length;i++) {
23680                     // fixme - ask toolbars for heights?
23681                     tbh += this.toolbars[i].el.getHeight();
23682                     //if (this.toolbars[i].footer) {
23683                     //    tbh += this.toolbars[i].footer.el.getHeight();
23684                     //}
23685                 }
23686               
23687                 
23688                 
23689                 
23690                 
23691                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23692                 ah -= 5; // knock a few pixes off for look..
23693                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23694                 var eh = ah;
23695             }
23696         }
23697         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23698         this.editorcore.onResize(ew,eh);
23699         
23700     },
23701
23702     /**
23703      * Toggles the editor between standard and source edit mode.
23704      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23705      */
23706     toggleSourceEdit : function(sourceEditMode)
23707     {
23708         this.editorcore.toggleSourceEdit(sourceEditMode);
23709         
23710         if(this.editorcore.sourceEditMode){
23711             Roo.log('editor - showing textarea');
23712             
23713 //            Roo.log('in');
23714 //            Roo.log(this.syncValue());
23715             this.syncValue();
23716             this.inputEl().removeClass(['hide', 'x-hidden']);
23717             this.inputEl().dom.removeAttribute('tabIndex');
23718             this.inputEl().focus();
23719         }else{
23720             Roo.log('editor - hiding textarea');
23721 //            Roo.log('out')
23722 //            Roo.log(this.pushValue()); 
23723             this.pushValue();
23724             
23725             this.inputEl().addClass(['hide', 'x-hidden']);
23726             this.inputEl().dom.setAttribute('tabIndex', -1);
23727             //this.deferFocus();
23728         }
23729          
23730         if(this.resizable){
23731             this.setSize(this.wrap.getSize());
23732         }
23733         
23734         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23735     },
23736  
23737     // private (for BoxComponent)
23738     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23739
23740     // private (for BoxComponent)
23741     getResizeEl : function(){
23742         return this.wrap;
23743     },
23744
23745     // private (for BoxComponent)
23746     getPositionEl : function(){
23747         return this.wrap;
23748     },
23749
23750     // private
23751     initEvents : function(){
23752         this.originalValue = this.getValue();
23753     },
23754
23755 //    /**
23756 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23757 //     * @method
23758 //     */
23759 //    markInvalid : Roo.emptyFn,
23760 //    /**
23761 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23762 //     * @method
23763 //     */
23764 //    clearInvalid : Roo.emptyFn,
23765
23766     setValue : function(v){
23767         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23768         this.editorcore.pushValue();
23769     },
23770
23771      
23772     // private
23773     deferFocus : function(){
23774         this.focus.defer(10, this);
23775     },
23776
23777     // doc'ed in Field
23778     focus : function(){
23779         this.editorcore.focus();
23780         
23781     },
23782       
23783
23784     // private
23785     onDestroy : function(){
23786         
23787         
23788         
23789         if(this.rendered){
23790             
23791             for (var i =0; i < this.toolbars.length;i++) {
23792                 // fixme - ask toolbars for heights?
23793                 this.toolbars[i].onDestroy();
23794             }
23795             
23796             this.wrap.dom.innerHTML = '';
23797             this.wrap.remove();
23798         }
23799     },
23800
23801     // private
23802     onFirstFocus : function(){
23803         //Roo.log("onFirstFocus");
23804         this.editorcore.onFirstFocus();
23805          for (var i =0; i < this.toolbars.length;i++) {
23806             this.toolbars[i].onFirstFocus();
23807         }
23808         
23809     },
23810     
23811     // private
23812     syncValue : function()
23813     {   
23814         this.editorcore.syncValue();
23815     },
23816     
23817     pushValue : function()
23818     {   
23819         this.editorcore.pushValue();
23820     }
23821      
23822     
23823     // hide stuff that is not compatible
23824     /**
23825      * @event blur
23826      * @hide
23827      */
23828     /**
23829      * @event change
23830      * @hide
23831      */
23832     /**
23833      * @event focus
23834      * @hide
23835      */
23836     /**
23837      * @event specialkey
23838      * @hide
23839      */
23840     /**
23841      * @cfg {String} fieldClass @hide
23842      */
23843     /**
23844      * @cfg {String} focusClass @hide
23845      */
23846     /**
23847      * @cfg {String} autoCreate @hide
23848      */
23849     /**
23850      * @cfg {String} inputType @hide
23851      */
23852     /**
23853      * @cfg {String} invalidClass @hide
23854      */
23855     /**
23856      * @cfg {String} invalidText @hide
23857      */
23858     /**
23859      * @cfg {String} msgFx @hide
23860      */
23861     /**
23862      * @cfg {String} validateOnBlur @hide
23863      */
23864 });
23865  
23866     
23867    
23868    
23869    
23870       
23871 Roo.namespace('Roo.bootstrap.htmleditor');
23872 /**
23873  * @class Roo.bootstrap.HtmlEditorToolbar1
23874  * Basic Toolbar
23875  * 
23876  * Usage:
23877  *
23878  new Roo.bootstrap.HtmlEditor({
23879     ....
23880     toolbars : [
23881         new Roo.bootstrap.HtmlEditorToolbar1({
23882             disable : { fonts: 1 , format: 1, ..., ... , ...],
23883             btns : [ .... ]
23884         })
23885     }
23886      
23887  * 
23888  * @cfg {Object} disable List of elements to disable..
23889  * @cfg {Array} btns List of additional buttons.
23890  * 
23891  * 
23892  * NEEDS Extra CSS? 
23893  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23894  */
23895  
23896 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23897 {
23898     
23899     Roo.apply(this, config);
23900     
23901     // default disabled, based on 'good practice'..
23902     this.disable = this.disable || {};
23903     Roo.applyIf(this.disable, {
23904         fontSize : true,
23905         colors : true,
23906         specialElements : true
23907     });
23908     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23909     
23910     this.editor = config.editor;
23911     this.editorcore = config.editor.editorcore;
23912     
23913     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23914     
23915     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23916     // dont call parent... till later.
23917 }
23918 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23919      
23920     bar : true,
23921     
23922     editor : false,
23923     editorcore : false,
23924     
23925     
23926     formats : [
23927         "p" ,  
23928         "h1","h2","h3","h4","h5","h6", 
23929         "pre", "code", 
23930         "abbr", "acronym", "address", "cite", "samp", "var",
23931         'div','span'
23932     ],
23933     
23934     onRender : function(ct, position)
23935     {
23936        // Roo.log("Call onRender: " + this.xtype);
23937         
23938        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23939        Roo.log(this.el);
23940        this.el.dom.style.marginBottom = '0';
23941        var _this = this;
23942        var editorcore = this.editorcore;
23943        var editor= this.editor;
23944        
23945        var children = [];
23946        var btn = function(id,cmd , toggle, handler, html){
23947        
23948             var  event = toggle ? 'toggle' : 'click';
23949        
23950             var a = {
23951                 size : 'sm',
23952                 xtype: 'Button',
23953                 xns: Roo.bootstrap,
23954                 glyphicon : id,
23955                 cmd : id || cmd,
23956                 enableToggle:toggle !== false,
23957                 html : html || '',
23958                 pressed : toggle ? false : null,
23959                 listeners : {}
23960             };
23961             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23962                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23963             };
23964             children.push(a);
23965             return a;
23966        }
23967        
23968     //    var cb_box = function...
23969         
23970         var style = {
23971                 xtype: 'Button',
23972                 size : 'sm',
23973                 xns: Roo.bootstrap,
23974                 glyphicon : 'font',
23975                 //html : 'submit'
23976                 menu : {
23977                     xtype: 'Menu',
23978                     xns: Roo.bootstrap,
23979                     items:  []
23980                 }
23981         };
23982         Roo.each(this.formats, function(f) {
23983             style.menu.items.push({
23984                 xtype :'MenuItem',
23985                 xns: Roo.bootstrap,
23986                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23987                 tagname : f,
23988                 listeners : {
23989                     click : function()
23990                     {
23991                         editorcore.insertTag(this.tagname);
23992                         editor.focus();
23993                     }
23994                 }
23995                 
23996             });
23997         });
23998         children.push(style);   
23999         
24000         btn('bold',false,true);
24001         btn('italic',false,true);
24002         btn('align-left', 'justifyleft',true);
24003         btn('align-center', 'justifycenter',true);
24004         btn('align-right' , 'justifyright',true);
24005         btn('link', false, false, function(btn) {
24006             //Roo.log("create link?");
24007             var url = prompt(this.createLinkText, this.defaultLinkValue);
24008             if(url && url != 'http:/'+'/'){
24009                 this.editorcore.relayCmd('createlink', url);
24010             }
24011         }),
24012         btn('list','insertunorderedlist',true);
24013         btn('pencil', false,true, function(btn){
24014                 Roo.log(this);
24015                 this.toggleSourceEdit(btn.pressed);
24016         });
24017         
24018         if (this.editor.btns.length > 0) {
24019             for (var i = 0; i<this.editor.btns.length; i++) {
24020                 children.push(this.editor.btns[i]);
24021             }
24022         }
24023         
24024         /*
24025         var cog = {
24026                 xtype: 'Button',
24027                 size : 'sm',
24028                 xns: Roo.bootstrap,
24029                 glyphicon : 'cog',
24030                 //html : 'submit'
24031                 menu : {
24032                     xtype: 'Menu',
24033                     xns: Roo.bootstrap,
24034                     items:  []
24035                 }
24036         };
24037         
24038         cog.menu.items.push({
24039             xtype :'MenuItem',
24040             xns: Roo.bootstrap,
24041             html : Clean styles,
24042             tagname : f,
24043             listeners : {
24044                 click : function()
24045                 {
24046                     editorcore.insertTag(this.tagname);
24047                     editor.focus();
24048                 }
24049             }
24050             
24051         });
24052        */
24053         
24054          
24055        this.xtype = 'NavSimplebar';
24056         
24057         for(var i=0;i< children.length;i++) {
24058             
24059             this.buttons.add(this.addxtypeChild(children[i]));
24060             
24061         }
24062         
24063         editor.on('editorevent', this.updateToolbar, this);
24064     },
24065     onBtnClick : function(id)
24066     {
24067        this.editorcore.relayCmd(id);
24068        this.editorcore.focus();
24069     },
24070     
24071     /**
24072      * Protected method that will not generally be called directly. It triggers
24073      * a toolbar update by reading the markup state of the current selection in the editor.
24074      */
24075     updateToolbar: function(){
24076
24077         if(!this.editorcore.activated){
24078             this.editor.onFirstFocus(); // is this neeed?
24079             return;
24080         }
24081
24082         var btns = this.buttons; 
24083         var doc = this.editorcore.doc;
24084         btns.get('bold').setActive(doc.queryCommandState('bold'));
24085         btns.get('italic').setActive(doc.queryCommandState('italic'));
24086         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24087         
24088         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24089         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24090         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24091         
24092         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24093         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24094          /*
24095         
24096         var ans = this.editorcore.getAllAncestors();
24097         if (this.formatCombo) {
24098             
24099             
24100             var store = this.formatCombo.store;
24101             this.formatCombo.setValue("");
24102             for (var i =0; i < ans.length;i++) {
24103                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24104                     // select it..
24105                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24106                     break;
24107                 }
24108             }
24109         }
24110         
24111         
24112         
24113         // hides menus... - so this cant be on a menu...
24114         Roo.bootstrap.MenuMgr.hideAll();
24115         */
24116         Roo.bootstrap.MenuMgr.hideAll();
24117         //this.editorsyncValue();
24118     },
24119     onFirstFocus: function() {
24120         this.buttons.each(function(item){
24121            item.enable();
24122         });
24123     },
24124     toggleSourceEdit : function(sourceEditMode){
24125         
24126           
24127         if(sourceEditMode){
24128             Roo.log("disabling buttons");
24129            this.buttons.each( function(item){
24130                 if(item.cmd != 'pencil'){
24131                     item.disable();
24132                 }
24133             });
24134           
24135         }else{
24136             Roo.log("enabling buttons");
24137             if(this.editorcore.initialized){
24138                 this.buttons.each( function(item){
24139                     item.enable();
24140                 });
24141             }
24142             
24143         }
24144         Roo.log("calling toggole on editor");
24145         // tell the editor that it's been pressed..
24146         this.editor.toggleSourceEdit(sourceEditMode);
24147        
24148     }
24149 });
24150
24151
24152
24153
24154
24155 /**
24156  * @class Roo.bootstrap.Table.AbstractSelectionModel
24157  * @extends Roo.util.Observable
24158  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24159  * implemented by descendant classes.  This class should not be directly instantiated.
24160  * @constructor
24161  */
24162 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24163     this.locked = false;
24164     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24165 };
24166
24167
24168 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24169     /** @ignore Called by the grid automatically. Do not call directly. */
24170     init : function(grid){
24171         this.grid = grid;
24172         this.initEvents();
24173     },
24174
24175     /**
24176      * Locks the selections.
24177      */
24178     lock : function(){
24179         this.locked = true;
24180     },
24181
24182     /**
24183      * Unlocks the selections.
24184      */
24185     unlock : function(){
24186         this.locked = false;
24187     },
24188
24189     /**
24190      * Returns true if the selections are locked.
24191      * @return {Boolean}
24192      */
24193     isLocked : function(){
24194         return this.locked;
24195     }
24196 });
24197 /**
24198  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24199  * @class Roo.bootstrap.Table.RowSelectionModel
24200  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24201  * It supports multiple selections and keyboard selection/navigation. 
24202  * @constructor
24203  * @param {Object} config
24204  */
24205
24206 Roo.bootstrap.Table.RowSelectionModel = function(config){
24207     Roo.apply(this, config);
24208     this.selections = new Roo.util.MixedCollection(false, function(o){
24209         return o.id;
24210     });
24211
24212     this.last = false;
24213     this.lastActive = false;
24214
24215     this.addEvents({
24216         /**
24217              * @event selectionchange
24218              * Fires when the selection changes
24219              * @param {SelectionModel} this
24220              */
24221             "selectionchange" : true,
24222         /**
24223              * @event afterselectionchange
24224              * Fires after the selection changes (eg. by key press or clicking)
24225              * @param {SelectionModel} this
24226              */
24227             "afterselectionchange" : true,
24228         /**
24229              * @event beforerowselect
24230              * Fires when a row is selected being selected, return false to cancel.
24231              * @param {SelectionModel} this
24232              * @param {Number} rowIndex The selected index
24233              * @param {Boolean} keepExisting False if other selections will be cleared
24234              */
24235             "beforerowselect" : true,
24236         /**
24237              * @event rowselect
24238              * Fires when a row is selected.
24239              * @param {SelectionModel} this
24240              * @param {Number} rowIndex The selected index
24241              * @param {Roo.data.Record} r The record
24242              */
24243             "rowselect" : true,
24244         /**
24245              * @event rowdeselect
24246              * Fires when a row is deselected.
24247              * @param {SelectionModel} this
24248              * @param {Number} rowIndex The selected index
24249              */
24250         "rowdeselect" : true
24251     });
24252     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24253     this.locked = false;
24254  };
24255
24256 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24257     /**
24258      * @cfg {Boolean} singleSelect
24259      * True to allow selection of only one row at a time (defaults to false)
24260      */
24261     singleSelect : false,
24262
24263     // private
24264     initEvents : function()
24265     {
24266
24267         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24268         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24269         //}else{ // allow click to work like normal
24270          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24271         //}
24272         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24273         this.grid.on("rowclick", this.handleMouseDown, this);
24274         
24275         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24276             "up" : function(e){
24277                 if(!e.shiftKey){
24278                     this.selectPrevious(e.shiftKey);
24279                 }else if(this.last !== false && this.lastActive !== false){
24280                     var last = this.last;
24281                     this.selectRange(this.last,  this.lastActive-1);
24282                     this.grid.getView().focusRow(this.lastActive);
24283                     if(last !== false){
24284                         this.last = last;
24285                     }
24286                 }else{
24287                     this.selectFirstRow();
24288                 }
24289                 this.fireEvent("afterselectionchange", this);
24290             },
24291             "down" : function(e){
24292                 if(!e.shiftKey){
24293                     this.selectNext(e.shiftKey);
24294                 }else if(this.last !== false && this.lastActive !== false){
24295                     var last = this.last;
24296                     this.selectRange(this.last,  this.lastActive+1);
24297                     this.grid.getView().focusRow(this.lastActive);
24298                     if(last !== false){
24299                         this.last = last;
24300                     }
24301                 }else{
24302                     this.selectFirstRow();
24303                 }
24304                 this.fireEvent("afterselectionchange", this);
24305             },
24306             scope: this
24307         });
24308         this.grid.store.on('load', function(){
24309             this.selections.clear();
24310         },this);
24311         /*
24312         var view = this.grid.view;
24313         view.on("refresh", this.onRefresh, this);
24314         view.on("rowupdated", this.onRowUpdated, this);
24315         view.on("rowremoved", this.onRemove, this);
24316         */
24317     },
24318
24319     // private
24320     onRefresh : function()
24321     {
24322         var ds = this.grid.store, i, v = this.grid.view;
24323         var s = this.selections;
24324         s.each(function(r){
24325             if((i = ds.indexOfId(r.id)) != -1){
24326                 v.onRowSelect(i);
24327             }else{
24328                 s.remove(r);
24329             }
24330         });
24331     },
24332
24333     // private
24334     onRemove : function(v, index, r){
24335         this.selections.remove(r);
24336     },
24337
24338     // private
24339     onRowUpdated : function(v, index, r){
24340         if(this.isSelected(r)){
24341             v.onRowSelect(index);
24342         }
24343     },
24344
24345     /**
24346      * Select records.
24347      * @param {Array} records The records to select
24348      * @param {Boolean} keepExisting (optional) True to keep existing selections
24349      */
24350     selectRecords : function(records, keepExisting)
24351     {
24352         if(!keepExisting){
24353             this.clearSelections();
24354         }
24355             var ds = this.grid.store;
24356         for(var i = 0, len = records.length; i < len; i++){
24357             this.selectRow(ds.indexOf(records[i]), true);
24358         }
24359     },
24360
24361     /**
24362      * Gets the number of selected rows.
24363      * @return {Number}
24364      */
24365     getCount : function(){
24366         return this.selections.length;
24367     },
24368
24369     /**
24370      * Selects the first row in the grid.
24371      */
24372     selectFirstRow : function(){
24373         this.selectRow(0);
24374     },
24375
24376     /**
24377      * Select the last row.
24378      * @param {Boolean} keepExisting (optional) True to keep existing selections
24379      */
24380     selectLastRow : function(keepExisting){
24381         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24382         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24383     },
24384
24385     /**
24386      * Selects the row immediately following the last selected row.
24387      * @param {Boolean} keepExisting (optional) True to keep existing selections
24388      */
24389     selectNext : function(keepExisting)
24390     {
24391             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24392             this.selectRow(this.last+1, keepExisting);
24393             this.grid.getView().focusRow(this.last);
24394         }
24395     },
24396
24397     /**
24398      * Selects the row that precedes the last selected row.
24399      * @param {Boolean} keepExisting (optional) True to keep existing selections
24400      */
24401     selectPrevious : function(keepExisting){
24402         if(this.last){
24403             this.selectRow(this.last-1, keepExisting);
24404             this.grid.getView().focusRow(this.last);
24405         }
24406     },
24407
24408     /**
24409      * Returns the selected records
24410      * @return {Array} Array of selected records
24411      */
24412     getSelections : function(){
24413         return [].concat(this.selections.items);
24414     },
24415
24416     /**
24417      * Returns the first selected record.
24418      * @return {Record}
24419      */
24420     getSelected : function(){
24421         return this.selections.itemAt(0);
24422     },
24423
24424
24425     /**
24426      * Clears all selections.
24427      */
24428     clearSelections : function(fast)
24429     {
24430         if(this.locked) {
24431             return;
24432         }
24433         if(fast !== true){
24434                 var ds = this.grid.store;
24435             var s = this.selections;
24436             s.each(function(r){
24437                 this.deselectRow(ds.indexOfId(r.id));
24438             }, this);
24439             s.clear();
24440         }else{
24441             this.selections.clear();
24442         }
24443         this.last = false;
24444     },
24445
24446
24447     /**
24448      * Selects all rows.
24449      */
24450     selectAll : function(){
24451         if(this.locked) {
24452             return;
24453         }
24454         this.selections.clear();
24455         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24456             this.selectRow(i, true);
24457         }
24458     },
24459
24460     /**
24461      * Returns True if there is a selection.
24462      * @return {Boolean}
24463      */
24464     hasSelection : function(){
24465         return this.selections.length > 0;
24466     },
24467
24468     /**
24469      * Returns True if the specified row is selected.
24470      * @param {Number/Record} record The record or index of the record to check
24471      * @return {Boolean}
24472      */
24473     isSelected : function(index){
24474             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24475         return (r && this.selections.key(r.id) ? true : false);
24476     },
24477
24478     /**
24479      * Returns True if the specified record id is selected.
24480      * @param {String} id The id of record to check
24481      * @return {Boolean}
24482      */
24483     isIdSelected : function(id){
24484         return (this.selections.key(id) ? true : false);
24485     },
24486
24487
24488     // private
24489     handleMouseDBClick : function(e, t){
24490         
24491     },
24492     // private
24493     handleMouseDown : function(e, t)
24494     {
24495             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24496         if(this.isLocked() || rowIndex < 0 ){
24497             return;
24498         };
24499         if(e.shiftKey && this.last !== false){
24500             var last = this.last;
24501             this.selectRange(last, rowIndex, e.ctrlKey);
24502             this.last = last; // reset the last
24503             t.focus();
24504     
24505         }else{
24506             var isSelected = this.isSelected(rowIndex);
24507             //Roo.log("select row:" + rowIndex);
24508             if(isSelected){
24509                 this.deselectRow(rowIndex);
24510             } else {
24511                         this.selectRow(rowIndex, true);
24512             }
24513     
24514             /*
24515                 if(e.button !== 0 && isSelected){
24516                 alert('rowIndex 2: ' + rowIndex);
24517                     view.focusRow(rowIndex);
24518                 }else if(e.ctrlKey && isSelected){
24519                     this.deselectRow(rowIndex);
24520                 }else if(!isSelected){
24521                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24522                     view.focusRow(rowIndex);
24523                 }
24524             */
24525         }
24526         this.fireEvent("afterselectionchange", this);
24527     },
24528     // private
24529     handleDragableRowClick :  function(grid, rowIndex, e) 
24530     {
24531         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24532             this.selectRow(rowIndex, false);
24533             grid.view.focusRow(rowIndex);
24534              this.fireEvent("afterselectionchange", this);
24535         }
24536     },
24537     
24538     /**
24539      * Selects multiple rows.
24540      * @param {Array} rows Array of the indexes of the row to select
24541      * @param {Boolean} keepExisting (optional) True to keep existing selections
24542      */
24543     selectRows : function(rows, keepExisting){
24544         if(!keepExisting){
24545             this.clearSelections();
24546         }
24547         for(var i = 0, len = rows.length; i < len; i++){
24548             this.selectRow(rows[i], true);
24549         }
24550     },
24551
24552     /**
24553      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24554      * @param {Number} startRow The index of the first row in the range
24555      * @param {Number} endRow The index of the last row in the range
24556      * @param {Boolean} keepExisting (optional) True to retain existing selections
24557      */
24558     selectRange : function(startRow, endRow, keepExisting){
24559         if(this.locked) {
24560             return;
24561         }
24562         if(!keepExisting){
24563             this.clearSelections();
24564         }
24565         if(startRow <= endRow){
24566             for(var i = startRow; i <= endRow; i++){
24567                 this.selectRow(i, true);
24568             }
24569         }else{
24570             for(var i = startRow; i >= endRow; i--){
24571                 this.selectRow(i, true);
24572             }
24573         }
24574     },
24575
24576     /**
24577      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24578      * @param {Number} startRow The index of the first row in the range
24579      * @param {Number} endRow The index of the last row in the range
24580      */
24581     deselectRange : function(startRow, endRow, preventViewNotify){
24582         if(this.locked) {
24583             return;
24584         }
24585         for(var i = startRow; i <= endRow; i++){
24586             this.deselectRow(i, preventViewNotify);
24587         }
24588     },
24589
24590     /**
24591      * Selects a row.
24592      * @param {Number} row The index of the row to select
24593      * @param {Boolean} keepExisting (optional) True to keep existing selections
24594      */
24595     selectRow : function(index, keepExisting, preventViewNotify)
24596     {
24597             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24598             return;
24599         }
24600         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24601             if(!keepExisting || this.singleSelect){
24602                 this.clearSelections();
24603             }
24604             
24605             var r = this.grid.store.getAt(index);
24606             //console.log('selectRow - record id :' + r.id);
24607             
24608             this.selections.add(r);
24609             this.last = this.lastActive = index;
24610             if(!preventViewNotify){
24611                 var proxy = new Roo.Element(
24612                                 this.grid.getRowDom(index)
24613                 );
24614                 proxy.addClass('bg-info info');
24615             }
24616             this.fireEvent("rowselect", this, index, r);
24617             this.fireEvent("selectionchange", this);
24618         }
24619     },
24620
24621     /**
24622      * Deselects a row.
24623      * @param {Number} row The index of the row to deselect
24624      */
24625     deselectRow : function(index, preventViewNotify)
24626     {
24627         if(this.locked) {
24628             return;
24629         }
24630         if(this.last == index){
24631             this.last = false;
24632         }
24633         if(this.lastActive == index){
24634             this.lastActive = false;
24635         }
24636         
24637         var r = this.grid.store.getAt(index);
24638         if (!r) {
24639             return;
24640         }
24641         
24642         this.selections.remove(r);
24643         //.console.log('deselectRow - record id :' + r.id);
24644         if(!preventViewNotify){
24645         
24646             var proxy = new Roo.Element(
24647                 this.grid.getRowDom(index)
24648             );
24649             proxy.removeClass('bg-info info');
24650         }
24651         this.fireEvent("rowdeselect", this, index);
24652         this.fireEvent("selectionchange", this);
24653     },
24654
24655     // private
24656     restoreLast : function(){
24657         if(this._last){
24658             this.last = this._last;
24659         }
24660     },
24661
24662     // private
24663     acceptsNav : function(row, col, cm){
24664         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24665     },
24666
24667     // private
24668     onEditorKey : function(field, e){
24669         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24670         if(k == e.TAB){
24671             e.stopEvent();
24672             ed.completeEdit();
24673             if(e.shiftKey){
24674                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24675             }else{
24676                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24677             }
24678         }else if(k == e.ENTER && !e.ctrlKey){
24679             e.stopEvent();
24680             ed.completeEdit();
24681             if(e.shiftKey){
24682                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24683             }else{
24684                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24685             }
24686         }else if(k == e.ESC){
24687             ed.cancelEdit();
24688         }
24689         if(newCell){
24690             g.startEditing(newCell[0], newCell[1]);
24691         }
24692     }
24693 });
24694 /*
24695  * Based on:
24696  * Ext JS Library 1.1.1
24697  * Copyright(c) 2006-2007, Ext JS, LLC.
24698  *
24699  * Originally Released Under LGPL - original licence link has changed is not relivant.
24700  *
24701  * Fork - LGPL
24702  * <script type="text/javascript">
24703  */
24704  
24705 /**
24706  * @class Roo.bootstrap.PagingToolbar
24707  * @extends Roo.bootstrap.NavSimplebar
24708  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24709  * @constructor
24710  * Create a new PagingToolbar
24711  * @param {Object} config The config object
24712  * @param {Roo.data.Store} store
24713  */
24714 Roo.bootstrap.PagingToolbar = function(config)
24715 {
24716     // old args format still supported... - xtype is prefered..
24717         // created from xtype...
24718     
24719     this.ds = config.dataSource;
24720     
24721     if (config.store && !this.ds) {
24722         this.store= Roo.factory(config.store, Roo.data);
24723         this.ds = this.store;
24724         this.ds.xmodule = this.xmodule || false;
24725     }
24726     
24727     this.toolbarItems = [];
24728     if (config.items) {
24729         this.toolbarItems = config.items;
24730     }
24731     
24732     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24733     
24734     this.cursor = 0;
24735     
24736     if (this.ds) { 
24737         this.bind(this.ds);
24738     }
24739     
24740     if (Roo.bootstrap.version == 4) {
24741         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24742     } else {
24743         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24744     }
24745     
24746 };
24747
24748 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24749     /**
24750      * @cfg {Roo.data.Store} dataSource
24751      * The underlying data store providing the paged data
24752      */
24753     /**
24754      * @cfg {String/HTMLElement/Element} container
24755      * container The id or element that will contain the toolbar
24756      */
24757     /**
24758      * @cfg {Boolean} displayInfo
24759      * True to display the displayMsg (defaults to false)
24760      */
24761     /**
24762      * @cfg {Number} pageSize
24763      * The number of records to display per page (defaults to 20)
24764      */
24765     pageSize: 20,
24766     /**
24767      * @cfg {String} displayMsg
24768      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24769      */
24770     displayMsg : 'Displaying {0} - {1} of {2}',
24771     /**
24772      * @cfg {String} emptyMsg
24773      * The message to display when no records are found (defaults to "No data to display")
24774      */
24775     emptyMsg : 'No data to display',
24776     /**
24777      * Customizable piece of the default paging text (defaults to "Page")
24778      * @type String
24779      */
24780     beforePageText : "Page",
24781     /**
24782      * Customizable piece of the default paging text (defaults to "of %0")
24783      * @type String
24784      */
24785     afterPageText : "of {0}",
24786     /**
24787      * Customizable piece of the default paging text (defaults to "First Page")
24788      * @type String
24789      */
24790     firstText : "First Page",
24791     /**
24792      * Customizable piece of the default paging text (defaults to "Previous Page")
24793      * @type String
24794      */
24795     prevText : "Previous Page",
24796     /**
24797      * Customizable piece of the default paging text (defaults to "Next Page")
24798      * @type String
24799      */
24800     nextText : "Next Page",
24801     /**
24802      * Customizable piece of the default paging text (defaults to "Last Page")
24803      * @type String
24804      */
24805     lastText : "Last Page",
24806     /**
24807      * Customizable piece of the default paging text (defaults to "Refresh")
24808      * @type String
24809      */
24810     refreshText : "Refresh",
24811
24812     buttons : false,
24813     // private
24814     onRender : function(ct, position) 
24815     {
24816         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24817         this.navgroup.parentId = this.id;
24818         this.navgroup.onRender(this.el, null);
24819         // add the buttons to the navgroup
24820         
24821         if(this.displayInfo){
24822             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24823             this.displayEl = this.el.select('.x-paging-info', true).first();
24824 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24825 //            this.displayEl = navel.el.select('span',true).first();
24826         }
24827         
24828         var _this = this;
24829         
24830         if(this.buttons){
24831             Roo.each(_this.buttons, function(e){ // this might need to use render????
24832                Roo.factory(e).render(_this.el);
24833             });
24834         }
24835             
24836         Roo.each(_this.toolbarItems, function(e) {
24837             _this.navgroup.addItem(e);
24838         });
24839         
24840         
24841         this.first = this.navgroup.addItem({
24842             tooltip: this.firstText,
24843             cls: "prev btn-outline-secondary",
24844             html : ' <i class="fa fa-step-backward"></i>',
24845             disabled: true,
24846             preventDefault: true,
24847             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24848         });
24849         
24850         this.prev =  this.navgroup.addItem({
24851             tooltip: this.prevText,
24852             cls: "prev btn-outline-secondary",
24853             html : ' <i class="fa fa-backward"></i>',
24854             disabled: true,
24855             preventDefault: true,
24856             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24857         });
24858     //this.addSeparator();
24859         
24860         
24861         var field = this.navgroup.addItem( {
24862             tagtype : 'span',
24863             cls : 'x-paging-position  btn-outline-secondary',
24864              disabled: true,
24865             html : this.beforePageText  +
24866                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24867                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24868          } ); //?? escaped?
24869         
24870         this.field = field.el.select('input', true).first();
24871         this.field.on("keydown", this.onPagingKeydown, this);
24872         this.field.on("focus", function(){this.dom.select();});
24873     
24874     
24875         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24876         //this.field.setHeight(18);
24877         //this.addSeparator();
24878         this.next = this.navgroup.addItem({
24879             tooltip: this.nextText,
24880             cls: "next btn-outline-secondary",
24881             html : ' <i class="fa fa-forward"></i>',
24882             disabled: true,
24883             preventDefault: true,
24884             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24885         });
24886         this.last = this.navgroup.addItem({
24887             tooltip: this.lastText,
24888             html : ' <i class="fa fa-step-forward"></i>',
24889             cls: "next btn-outline-secondary",
24890             disabled: true,
24891             preventDefault: true,
24892             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24893         });
24894     //this.addSeparator();
24895         this.loading = this.navgroup.addItem({
24896             tooltip: this.refreshText,
24897             cls: "btn-outline-secondary",
24898             html : ' <i class="fa fa-refresh"></i>',
24899             preventDefault: true,
24900             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24901         });
24902         
24903     },
24904
24905     // private
24906     updateInfo : function(){
24907         if(this.displayEl){
24908             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24909             var msg = count == 0 ?
24910                 this.emptyMsg :
24911                 String.format(
24912                     this.displayMsg,
24913                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24914                 );
24915             this.displayEl.update(msg);
24916         }
24917     },
24918
24919     // private
24920     onLoad : function(ds, r, o)
24921     {
24922         this.cursor = o.params.start ? o.params.start : 0;
24923         
24924         var d = this.getPageData(),
24925             ap = d.activePage,
24926             ps = d.pages;
24927         
24928         
24929         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24930         this.field.dom.value = ap;
24931         this.first.setDisabled(ap == 1);
24932         this.prev.setDisabled(ap == 1);
24933         this.next.setDisabled(ap == ps);
24934         this.last.setDisabled(ap == ps);
24935         this.loading.enable();
24936         this.updateInfo();
24937     },
24938
24939     // private
24940     getPageData : function(){
24941         var total = this.ds.getTotalCount();
24942         return {
24943             total : total,
24944             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24945             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24946         };
24947     },
24948
24949     // private
24950     onLoadError : function(){
24951         this.loading.enable();
24952     },
24953
24954     // private
24955     onPagingKeydown : function(e){
24956         var k = e.getKey();
24957         var d = this.getPageData();
24958         if(k == e.RETURN){
24959             var v = this.field.dom.value, pageNum;
24960             if(!v || isNaN(pageNum = parseInt(v, 10))){
24961                 this.field.dom.value = d.activePage;
24962                 return;
24963             }
24964             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24965             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24966             e.stopEvent();
24967         }
24968         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))
24969         {
24970           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24971           this.field.dom.value = pageNum;
24972           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24973           e.stopEvent();
24974         }
24975         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24976         {
24977           var v = this.field.dom.value, pageNum; 
24978           var increment = (e.shiftKey) ? 10 : 1;
24979           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24980                 increment *= -1;
24981           }
24982           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24983             this.field.dom.value = d.activePage;
24984             return;
24985           }
24986           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24987           {
24988             this.field.dom.value = parseInt(v, 10) + increment;
24989             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24990             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24991           }
24992           e.stopEvent();
24993         }
24994     },
24995
24996     // private
24997     beforeLoad : function(){
24998         if(this.loading){
24999             this.loading.disable();
25000         }
25001     },
25002
25003     // private
25004     onClick : function(which){
25005         
25006         var ds = this.ds;
25007         if (!ds) {
25008             return;
25009         }
25010         
25011         switch(which){
25012             case "first":
25013                 ds.load({params:{start: 0, limit: this.pageSize}});
25014             break;
25015             case "prev":
25016                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25017             break;
25018             case "next":
25019                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25020             break;
25021             case "last":
25022                 var total = ds.getTotalCount();
25023                 var extra = total % this.pageSize;
25024                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25025                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25026             break;
25027             case "refresh":
25028                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25029             break;
25030         }
25031     },
25032
25033     /**
25034      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25035      * @param {Roo.data.Store} store The data store to unbind
25036      */
25037     unbind : function(ds){
25038         ds.un("beforeload", this.beforeLoad, this);
25039         ds.un("load", this.onLoad, this);
25040         ds.un("loadexception", this.onLoadError, this);
25041         ds.un("remove", this.updateInfo, this);
25042         ds.un("add", this.updateInfo, this);
25043         this.ds = undefined;
25044     },
25045
25046     /**
25047      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25048      * @param {Roo.data.Store} store The data store to bind
25049      */
25050     bind : function(ds){
25051         ds.on("beforeload", this.beforeLoad, this);
25052         ds.on("load", this.onLoad, this);
25053         ds.on("loadexception", this.onLoadError, this);
25054         ds.on("remove", this.updateInfo, this);
25055         ds.on("add", this.updateInfo, this);
25056         this.ds = ds;
25057     }
25058 });/*
25059  * - LGPL
25060  *
25061  * element
25062  * 
25063  */
25064
25065 /**
25066  * @class Roo.bootstrap.MessageBar
25067  * @extends Roo.bootstrap.Component
25068  * Bootstrap MessageBar class
25069  * @cfg {String} html contents of the MessageBar
25070  * @cfg {String} weight (info | success | warning | danger) default info
25071  * @cfg {String} beforeClass insert the bar before the given class
25072  * @cfg {Boolean} closable (true | false) default false
25073  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25074  * 
25075  * @constructor
25076  * Create a new Element
25077  * @param {Object} config The config object
25078  */
25079
25080 Roo.bootstrap.MessageBar = function(config){
25081     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25082 };
25083
25084 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25085     
25086     html: '',
25087     weight: 'info',
25088     closable: false,
25089     fixed: false,
25090     beforeClass: 'bootstrap-sticky-wrap',
25091     
25092     getAutoCreate : function(){
25093         
25094         var cfg = {
25095             tag: 'div',
25096             cls: 'alert alert-dismissable alert-' + this.weight,
25097             cn: [
25098                 {
25099                     tag: 'span',
25100                     cls: 'message',
25101                     html: this.html || ''
25102                 }
25103             ]
25104         };
25105         
25106         if(this.fixed){
25107             cfg.cls += ' alert-messages-fixed';
25108         }
25109         
25110         if(this.closable){
25111             cfg.cn.push({
25112                 tag: 'button',
25113                 cls: 'close',
25114                 html: 'x'
25115             });
25116         }
25117         
25118         return cfg;
25119     },
25120     
25121     onRender : function(ct, position)
25122     {
25123         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25124         
25125         if(!this.el){
25126             var cfg = Roo.apply({},  this.getAutoCreate());
25127             cfg.id = Roo.id();
25128             
25129             if (this.cls) {
25130                 cfg.cls += ' ' + this.cls;
25131             }
25132             if (this.style) {
25133                 cfg.style = this.style;
25134             }
25135             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25136             
25137             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25138         }
25139         
25140         this.el.select('>button.close').on('click', this.hide, this);
25141         
25142     },
25143     
25144     show : function()
25145     {
25146         if (!this.rendered) {
25147             this.render();
25148         }
25149         
25150         this.el.show();
25151         
25152         this.fireEvent('show', this);
25153         
25154     },
25155     
25156     hide : function()
25157     {
25158         if (!this.rendered) {
25159             this.render();
25160         }
25161         
25162         this.el.hide();
25163         
25164         this.fireEvent('hide', this);
25165     },
25166     
25167     update : function()
25168     {
25169 //        var e = this.el.dom.firstChild;
25170 //        
25171 //        if(this.closable){
25172 //            e = e.nextSibling;
25173 //        }
25174 //        
25175 //        e.data = this.html || '';
25176
25177         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25178     }
25179    
25180 });
25181
25182  
25183
25184      /*
25185  * - LGPL
25186  *
25187  * Graph
25188  * 
25189  */
25190
25191
25192 /**
25193  * @class Roo.bootstrap.Graph
25194  * @extends Roo.bootstrap.Component
25195  * Bootstrap Graph class
25196 > Prameters
25197  -sm {number} sm 4
25198  -md {number} md 5
25199  @cfg {String} graphtype  bar | vbar | pie
25200  @cfg {number} g_x coodinator | centre x (pie)
25201  @cfg {number} g_y coodinator | centre y (pie)
25202  @cfg {number} g_r radius (pie)
25203  @cfg {number} g_height height of the chart (respected by all elements in the set)
25204  @cfg {number} g_width width of the chart (respected by all elements in the set)
25205  @cfg {Object} title The title of the chart
25206     
25207  -{Array}  values
25208  -opts (object) options for the chart 
25209      o {
25210      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25211      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25212      o vgutter (number)
25213      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.
25214      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25215      o to
25216      o stretch (boolean)
25217      o }
25218  -opts (object) options for the pie
25219      o{
25220      o cut
25221      o startAngle (number)
25222      o endAngle (number)
25223      } 
25224  *
25225  * @constructor
25226  * Create a new Input
25227  * @param {Object} config The config object
25228  */
25229
25230 Roo.bootstrap.Graph = function(config){
25231     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25232     
25233     this.addEvents({
25234         // img events
25235         /**
25236          * @event click
25237          * The img click event for the img.
25238          * @param {Roo.EventObject} e
25239          */
25240         "click" : true
25241     });
25242 };
25243
25244 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25245     
25246     sm: 4,
25247     md: 5,
25248     graphtype: 'bar',
25249     g_height: 250,
25250     g_width: 400,
25251     g_x: 50,
25252     g_y: 50,
25253     g_r: 30,
25254     opts:{
25255         //g_colors: this.colors,
25256         g_type: 'soft',
25257         g_gutter: '20%'
25258
25259     },
25260     title : false,
25261
25262     getAutoCreate : function(){
25263         
25264         var cfg = {
25265             tag: 'div',
25266             html : null
25267         };
25268         
25269         
25270         return  cfg;
25271     },
25272
25273     onRender : function(ct,position){
25274         
25275         
25276         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25277         
25278         if (typeof(Raphael) == 'undefined') {
25279             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25280             return;
25281         }
25282         
25283         this.raphael = Raphael(this.el.dom);
25284         
25285                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25286                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25287                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25288                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25289                 /*
25290                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25291                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25292                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25293                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25294                 
25295                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25296                 r.barchart(330, 10, 300, 220, data1);
25297                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25298                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25299                 */
25300                 
25301                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25302                 // r.barchart(30, 30, 560, 250,  xdata, {
25303                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25304                 //     axis : "0 0 1 1",
25305                 //     axisxlabels :  xdata
25306                 //     //yvalues : cols,
25307                    
25308                 // });
25309 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25310 //        
25311 //        this.load(null,xdata,{
25312 //                axis : "0 0 1 1",
25313 //                axisxlabels :  xdata
25314 //                });
25315
25316     },
25317
25318     load : function(graphtype,xdata,opts)
25319     {
25320         this.raphael.clear();
25321         if(!graphtype) {
25322             graphtype = this.graphtype;
25323         }
25324         if(!opts){
25325             opts = this.opts;
25326         }
25327         var r = this.raphael,
25328             fin = function () {
25329                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25330             },
25331             fout = function () {
25332                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25333             },
25334             pfin = function() {
25335                 this.sector.stop();
25336                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25337
25338                 if (this.label) {
25339                     this.label[0].stop();
25340                     this.label[0].attr({ r: 7.5 });
25341                     this.label[1].attr({ "font-weight": 800 });
25342                 }
25343             },
25344             pfout = function() {
25345                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25346
25347                 if (this.label) {
25348                     this.label[0].animate({ r: 5 }, 500, "bounce");
25349                     this.label[1].attr({ "font-weight": 400 });
25350                 }
25351             };
25352
25353         switch(graphtype){
25354             case 'bar':
25355                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25356                 break;
25357             case 'hbar':
25358                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25359                 break;
25360             case 'pie':
25361 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25362 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25363 //            
25364                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25365                 
25366                 break;
25367
25368         }
25369         
25370         if(this.title){
25371             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25372         }
25373         
25374     },
25375     
25376     setTitle: function(o)
25377     {
25378         this.title = o;
25379     },
25380     
25381     initEvents: function() {
25382         
25383         if(!this.href){
25384             this.el.on('click', this.onClick, this);
25385         }
25386     },
25387     
25388     onClick : function(e)
25389     {
25390         Roo.log('img onclick');
25391         this.fireEvent('click', this, e);
25392     }
25393    
25394 });
25395
25396  
25397 /*
25398  * - LGPL
25399  *
25400  * numberBox
25401  * 
25402  */
25403 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25404
25405 /**
25406  * @class Roo.bootstrap.dash.NumberBox
25407  * @extends Roo.bootstrap.Component
25408  * Bootstrap NumberBox class
25409  * @cfg {String} headline Box headline
25410  * @cfg {String} content Box content
25411  * @cfg {String} icon Box icon
25412  * @cfg {String} footer Footer text
25413  * @cfg {String} fhref Footer href
25414  * 
25415  * @constructor
25416  * Create a new NumberBox
25417  * @param {Object} config The config object
25418  */
25419
25420
25421 Roo.bootstrap.dash.NumberBox = function(config){
25422     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25423     
25424 };
25425
25426 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25427     
25428     headline : '',
25429     content : '',
25430     icon : '',
25431     footer : '',
25432     fhref : '',
25433     ficon : '',
25434     
25435     getAutoCreate : function(){
25436         
25437         var cfg = {
25438             tag : 'div',
25439             cls : 'small-box ',
25440             cn : [
25441                 {
25442                     tag : 'div',
25443                     cls : 'inner',
25444                     cn :[
25445                         {
25446                             tag : 'h3',
25447                             cls : 'roo-headline',
25448                             html : this.headline
25449                         },
25450                         {
25451                             tag : 'p',
25452                             cls : 'roo-content',
25453                             html : this.content
25454                         }
25455                     ]
25456                 }
25457             ]
25458         };
25459         
25460         if(this.icon){
25461             cfg.cn.push({
25462                 tag : 'div',
25463                 cls : 'icon',
25464                 cn :[
25465                     {
25466                         tag : 'i',
25467                         cls : 'ion ' + this.icon
25468                     }
25469                 ]
25470             });
25471         }
25472         
25473         if(this.footer){
25474             var footer = {
25475                 tag : 'a',
25476                 cls : 'small-box-footer',
25477                 href : this.fhref || '#',
25478                 html : this.footer
25479             };
25480             
25481             cfg.cn.push(footer);
25482             
25483         }
25484         
25485         return  cfg;
25486     },
25487
25488     onRender : function(ct,position){
25489         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25490
25491
25492        
25493                 
25494     },
25495
25496     setHeadline: function (value)
25497     {
25498         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25499     },
25500     
25501     setFooter: function (value, href)
25502     {
25503         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25504         
25505         if(href){
25506             this.el.select('a.small-box-footer',true).first().attr('href', href);
25507         }
25508         
25509     },
25510
25511     setContent: function (value)
25512     {
25513         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25514     },
25515
25516     initEvents: function() 
25517     {   
25518         
25519     }
25520     
25521 });
25522
25523  
25524 /*
25525  * - LGPL
25526  *
25527  * TabBox
25528  * 
25529  */
25530 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25531
25532 /**
25533  * @class Roo.bootstrap.dash.TabBox
25534  * @extends Roo.bootstrap.Component
25535  * Bootstrap TabBox class
25536  * @cfg {String} title Title of the TabBox
25537  * @cfg {String} icon Icon of the TabBox
25538  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25539  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25540  * 
25541  * @constructor
25542  * Create a new TabBox
25543  * @param {Object} config The config object
25544  */
25545
25546
25547 Roo.bootstrap.dash.TabBox = function(config){
25548     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25549     this.addEvents({
25550         // raw events
25551         /**
25552          * @event addpane
25553          * When a pane is added
25554          * @param {Roo.bootstrap.dash.TabPane} pane
25555          */
25556         "addpane" : true,
25557         /**
25558          * @event activatepane
25559          * When a pane is activated
25560          * @param {Roo.bootstrap.dash.TabPane} pane
25561          */
25562         "activatepane" : true
25563         
25564          
25565     });
25566     
25567     this.panes = [];
25568 };
25569
25570 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25571
25572     title : '',
25573     icon : false,
25574     showtabs : true,
25575     tabScrollable : false,
25576     
25577     getChildContainer : function()
25578     {
25579         return this.el.select('.tab-content', true).first();
25580     },
25581     
25582     getAutoCreate : function(){
25583         
25584         var header = {
25585             tag: 'li',
25586             cls: 'pull-left header',
25587             html: this.title,
25588             cn : []
25589         };
25590         
25591         if(this.icon){
25592             header.cn.push({
25593                 tag: 'i',
25594                 cls: 'fa ' + this.icon
25595             });
25596         }
25597         
25598         var h = {
25599             tag: 'ul',
25600             cls: 'nav nav-tabs pull-right',
25601             cn: [
25602                 header
25603             ]
25604         };
25605         
25606         if(this.tabScrollable){
25607             h = {
25608                 tag: 'div',
25609                 cls: 'tab-header',
25610                 cn: [
25611                     {
25612                         tag: 'ul',
25613                         cls: 'nav nav-tabs pull-right',
25614                         cn: [
25615                             header
25616                         ]
25617                     }
25618                 ]
25619             };
25620         }
25621         
25622         var cfg = {
25623             tag: 'div',
25624             cls: 'nav-tabs-custom',
25625             cn: [
25626                 h,
25627                 {
25628                     tag: 'div',
25629                     cls: 'tab-content no-padding',
25630                     cn: []
25631                 }
25632             ]
25633         };
25634
25635         return  cfg;
25636     },
25637     initEvents : function()
25638     {
25639         //Roo.log('add add pane handler');
25640         this.on('addpane', this.onAddPane, this);
25641     },
25642      /**
25643      * Updates the box title
25644      * @param {String} html to set the title to.
25645      */
25646     setTitle : function(value)
25647     {
25648         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25649     },
25650     onAddPane : function(pane)
25651     {
25652         this.panes.push(pane);
25653         //Roo.log('addpane');
25654         //Roo.log(pane);
25655         // tabs are rendere left to right..
25656         if(!this.showtabs){
25657             return;
25658         }
25659         
25660         var ctr = this.el.select('.nav-tabs', true).first();
25661          
25662          
25663         var existing = ctr.select('.nav-tab',true);
25664         var qty = existing.getCount();;
25665         
25666         
25667         var tab = ctr.createChild({
25668             tag : 'li',
25669             cls : 'nav-tab' + (qty ? '' : ' active'),
25670             cn : [
25671                 {
25672                     tag : 'a',
25673                     href:'#',
25674                     html : pane.title
25675                 }
25676             ]
25677         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25678         pane.tab = tab;
25679         
25680         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25681         if (!qty) {
25682             pane.el.addClass('active');
25683         }
25684         
25685                 
25686     },
25687     onTabClick : function(ev,un,ob,pane)
25688     {
25689         //Roo.log('tab - prev default');
25690         ev.preventDefault();
25691         
25692         
25693         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25694         pane.tab.addClass('active');
25695         //Roo.log(pane.title);
25696         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25697         // technically we should have a deactivate event.. but maybe add later.
25698         // and it should not de-activate the selected tab...
25699         this.fireEvent('activatepane', pane);
25700         pane.el.addClass('active');
25701         pane.fireEvent('activate');
25702         
25703         
25704     },
25705     
25706     getActivePane : function()
25707     {
25708         var r = false;
25709         Roo.each(this.panes, function(p) {
25710             if(p.el.hasClass('active')){
25711                 r = p;
25712                 return false;
25713             }
25714             
25715             return;
25716         });
25717         
25718         return r;
25719     }
25720     
25721     
25722 });
25723
25724  
25725 /*
25726  * - LGPL
25727  *
25728  * Tab pane
25729  * 
25730  */
25731 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25732 /**
25733  * @class Roo.bootstrap.TabPane
25734  * @extends Roo.bootstrap.Component
25735  * Bootstrap TabPane class
25736  * @cfg {Boolean} active (false | true) Default false
25737  * @cfg {String} title title of panel
25738
25739  * 
25740  * @constructor
25741  * Create a new TabPane
25742  * @param {Object} config The config object
25743  */
25744
25745 Roo.bootstrap.dash.TabPane = function(config){
25746     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25747     
25748     this.addEvents({
25749         // raw events
25750         /**
25751          * @event activate
25752          * When a pane is activated
25753          * @param {Roo.bootstrap.dash.TabPane} pane
25754          */
25755         "activate" : true
25756          
25757     });
25758 };
25759
25760 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25761     
25762     active : false,
25763     title : '',
25764     
25765     // the tabBox that this is attached to.
25766     tab : false,
25767      
25768     getAutoCreate : function() 
25769     {
25770         var cfg = {
25771             tag: 'div',
25772             cls: 'tab-pane'
25773         };
25774         
25775         if(this.active){
25776             cfg.cls += ' active';
25777         }
25778         
25779         return cfg;
25780     },
25781     initEvents  : function()
25782     {
25783         //Roo.log('trigger add pane handler');
25784         this.parent().fireEvent('addpane', this)
25785     },
25786     
25787      /**
25788      * Updates the tab title 
25789      * @param {String} html to set the title to.
25790      */
25791     setTitle: function(str)
25792     {
25793         if (!this.tab) {
25794             return;
25795         }
25796         this.title = str;
25797         this.tab.select('a', true).first().dom.innerHTML = str;
25798         
25799     }
25800     
25801     
25802     
25803 });
25804
25805  
25806
25807
25808  /*
25809  * - LGPL
25810  *
25811  * menu
25812  * 
25813  */
25814 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25815
25816 /**
25817  * @class Roo.bootstrap.menu.Menu
25818  * @extends Roo.bootstrap.Component
25819  * Bootstrap Menu class - container for Menu
25820  * @cfg {String} html Text of the menu
25821  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25822  * @cfg {String} icon Font awesome icon
25823  * @cfg {String} pos Menu align to (top | bottom) default bottom
25824  * 
25825  * 
25826  * @constructor
25827  * Create a new Menu
25828  * @param {Object} config The config object
25829  */
25830
25831
25832 Roo.bootstrap.menu.Menu = function(config){
25833     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25834     
25835     this.addEvents({
25836         /**
25837          * @event beforeshow
25838          * Fires before this menu is displayed
25839          * @param {Roo.bootstrap.menu.Menu} this
25840          */
25841         beforeshow : true,
25842         /**
25843          * @event beforehide
25844          * Fires before this menu is hidden
25845          * @param {Roo.bootstrap.menu.Menu} this
25846          */
25847         beforehide : true,
25848         /**
25849          * @event show
25850          * Fires after this menu is displayed
25851          * @param {Roo.bootstrap.menu.Menu} this
25852          */
25853         show : true,
25854         /**
25855          * @event hide
25856          * Fires after this menu is hidden
25857          * @param {Roo.bootstrap.menu.Menu} this
25858          */
25859         hide : true,
25860         /**
25861          * @event click
25862          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25863          * @param {Roo.bootstrap.menu.Menu} this
25864          * @param {Roo.EventObject} e
25865          */
25866         click : true
25867     });
25868     
25869 };
25870
25871 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25872     
25873     submenu : false,
25874     html : '',
25875     weight : 'default',
25876     icon : false,
25877     pos : 'bottom',
25878     
25879     
25880     getChildContainer : function() {
25881         if(this.isSubMenu){
25882             return this.el;
25883         }
25884         
25885         return this.el.select('ul.dropdown-menu', true).first();  
25886     },
25887     
25888     getAutoCreate : function()
25889     {
25890         var text = [
25891             {
25892                 tag : 'span',
25893                 cls : 'roo-menu-text',
25894                 html : this.html
25895             }
25896         ];
25897         
25898         if(this.icon){
25899             text.unshift({
25900                 tag : 'i',
25901                 cls : 'fa ' + this.icon
25902             })
25903         }
25904         
25905         
25906         var cfg = {
25907             tag : 'div',
25908             cls : 'btn-group',
25909             cn : [
25910                 {
25911                     tag : 'button',
25912                     cls : 'dropdown-button btn btn-' + this.weight,
25913                     cn : text
25914                 },
25915                 {
25916                     tag : 'button',
25917                     cls : 'dropdown-toggle btn btn-' + this.weight,
25918                     cn : [
25919                         {
25920                             tag : 'span',
25921                             cls : 'caret'
25922                         }
25923                     ]
25924                 },
25925                 {
25926                     tag : 'ul',
25927                     cls : 'dropdown-menu'
25928                 }
25929             ]
25930             
25931         };
25932         
25933         if(this.pos == 'top'){
25934             cfg.cls += ' dropup';
25935         }
25936         
25937         if(this.isSubMenu){
25938             cfg = {
25939                 tag : 'ul',
25940                 cls : 'dropdown-menu'
25941             }
25942         }
25943         
25944         return cfg;
25945     },
25946     
25947     onRender : function(ct, position)
25948     {
25949         this.isSubMenu = ct.hasClass('dropdown-submenu');
25950         
25951         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25952     },
25953     
25954     initEvents : function() 
25955     {
25956         if(this.isSubMenu){
25957             return;
25958         }
25959         
25960         this.hidden = true;
25961         
25962         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25963         this.triggerEl.on('click', this.onTriggerPress, this);
25964         
25965         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25966         this.buttonEl.on('click', this.onClick, this);
25967         
25968     },
25969     
25970     list : function()
25971     {
25972         if(this.isSubMenu){
25973             return this.el;
25974         }
25975         
25976         return this.el.select('ul.dropdown-menu', true).first();
25977     },
25978     
25979     onClick : function(e)
25980     {
25981         this.fireEvent("click", this, e);
25982     },
25983     
25984     onTriggerPress  : function(e)
25985     {   
25986         if (this.isVisible()) {
25987             this.hide();
25988         } else {
25989             this.show();
25990         }
25991     },
25992     
25993     isVisible : function(){
25994         return !this.hidden;
25995     },
25996     
25997     show : function()
25998     {
25999         this.fireEvent("beforeshow", this);
26000         
26001         this.hidden = false;
26002         this.el.addClass('open');
26003         
26004         Roo.get(document).on("mouseup", this.onMouseUp, this);
26005         
26006         this.fireEvent("show", this);
26007         
26008         
26009     },
26010     
26011     hide : function()
26012     {
26013         this.fireEvent("beforehide", this);
26014         
26015         this.hidden = true;
26016         this.el.removeClass('open');
26017         
26018         Roo.get(document).un("mouseup", this.onMouseUp);
26019         
26020         this.fireEvent("hide", this);
26021     },
26022     
26023     onMouseUp : function()
26024     {
26025         this.hide();
26026     }
26027     
26028 });
26029
26030  
26031  /*
26032  * - LGPL
26033  *
26034  * menu item
26035  * 
26036  */
26037 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26038
26039 /**
26040  * @class Roo.bootstrap.menu.Item
26041  * @extends Roo.bootstrap.Component
26042  * Bootstrap MenuItem class
26043  * @cfg {Boolean} submenu (true | false) default false
26044  * @cfg {String} html text of the item
26045  * @cfg {String} href the link
26046  * @cfg {Boolean} disable (true | false) default false
26047  * @cfg {Boolean} preventDefault (true | false) default true
26048  * @cfg {String} icon Font awesome icon
26049  * @cfg {String} pos Submenu align to (left | right) default right 
26050  * 
26051  * 
26052  * @constructor
26053  * Create a new Item
26054  * @param {Object} config The config object
26055  */
26056
26057
26058 Roo.bootstrap.menu.Item = function(config){
26059     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26060     this.addEvents({
26061         /**
26062          * @event mouseover
26063          * Fires when the mouse is hovering over this menu
26064          * @param {Roo.bootstrap.menu.Item} this
26065          * @param {Roo.EventObject} e
26066          */
26067         mouseover : true,
26068         /**
26069          * @event mouseout
26070          * Fires when the mouse exits this menu
26071          * @param {Roo.bootstrap.menu.Item} this
26072          * @param {Roo.EventObject} e
26073          */
26074         mouseout : true,
26075         // raw events
26076         /**
26077          * @event click
26078          * The raw click event for the entire grid.
26079          * @param {Roo.EventObject} e
26080          */
26081         click : true
26082     });
26083 };
26084
26085 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26086     
26087     submenu : false,
26088     href : '',
26089     html : '',
26090     preventDefault: true,
26091     disable : false,
26092     icon : false,
26093     pos : 'right',
26094     
26095     getAutoCreate : function()
26096     {
26097         var text = [
26098             {
26099                 tag : 'span',
26100                 cls : 'roo-menu-item-text',
26101                 html : this.html
26102             }
26103         ];
26104         
26105         if(this.icon){
26106             text.unshift({
26107                 tag : 'i',
26108                 cls : 'fa ' + this.icon
26109             })
26110         }
26111         
26112         var cfg = {
26113             tag : 'li',
26114             cn : [
26115                 {
26116                     tag : 'a',
26117                     href : this.href || '#',
26118                     cn : text
26119                 }
26120             ]
26121         };
26122         
26123         if(this.disable){
26124             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26125         }
26126         
26127         if(this.submenu){
26128             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26129             
26130             if(this.pos == 'left'){
26131                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26132             }
26133         }
26134         
26135         return cfg;
26136     },
26137     
26138     initEvents : function() 
26139     {
26140         this.el.on('mouseover', this.onMouseOver, this);
26141         this.el.on('mouseout', this.onMouseOut, this);
26142         
26143         this.el.select('a', true).first().on('click', this.onClick, this);
26144         
26145     },
26146     
26147     onClick : function(e)
26148     {
26149         if(this.preventDefault){
26150             e.preventDefault();
26151         }
26152         
26153         this.fireEvent("click", this, e);
26154     },
26155     
26156     onMouseOver : function(e)
26157     {
26158         if(this.submenu && this.pos == 'left'){
26159             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26160         }
26161         
26162         this.fireEvent("mouseover", this, e);
26163     },
26164     
26165     onMouseOut : function(e)
26166     {
26167         this.fireEvent("mouseout", this, e);
26168     }
26169 });
26170
26171  
26172
26173  /*
26174  * - LGPL
26175  *
26176  * menu separator
26177  * 
26178  */
26179 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26180
26181 /**
26182  * @class Roo.bootstrap.menu.Separator
26183  * @extends Roo.bootstrap.Component
26184  * Bootstrap Separator class
26185  * 
26186  * @constructor
26187  * Create a new Separator
26188  * @param {Object} config The config object
26189  */
26190
26191
26192 Roo.bootstrap.menu.Separator = function(config){
26193     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26194 };
26195
26196 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26197     
26198     getAutoCreate : function(){
26199         var cfg = {
26200             tag : 'li',
26201             cls: 'divider'
26202         };
26203         
26204         return cfg;
26205     }
26206    
26207 });
26208
26209  
26210
26211  /*
26212  * - LGPL
26213  *
26214  * Tooltip
26215  * 
26216  */
26217
26218 /**
26219  * @class Roo.bootstrap.Tooltip
26220  * Bootstrap Tooltip class
26221  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26222  * to determine which dom element triggers the tooltip.
26223  * 
26224  * It needs to add support for additional attributes like tooltip-position
26225  * 
26226  * @constructor
26227  * Create a new Toolti
26228  * @param {Object} config The config object
26229  */
26230
26231 Roo.bootstrap.Tooltip = function(config){
26232     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26233     
26234     this.alignment = Roo.bootstrap.Tooltip.alignment;
26235     
26236     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26237         this.alignment = config.alignment;
26238     }
26239     
26240 };
26241
26242 Roo.apply(Roo.bootstrap.Tooltip, {
26243     /**
26244      * @function init initialize tooltip monitoring.
26245      * @static
26246      */
26247     currentEl : false,
26248     currentTip : false,
26249     currentRegion : false,
26250     
26251     //  init : delay?
26252     
26253     init : function()
26254     {
26255         Roo.get(document).on('mouseover', this.enter ,this);
26256         Roo.get(document).on('mouseout', this.leave, this);
26257          
26258         
26259         this.currentTip = new Roo.bootstrap.Tooltip();
26260     },
26261     
26262     enter : function(ev)
26263     {
26264         var dom = ev.getTarget();
26265         
26266         //Roo.log(['enter',dom]);
26267         var el = Roo.fly(dom);
26268         if (this.currentEl) {
26269             //Roo.log(dom);
26270             //Roo.log(this.currentEl);
26271             //Roo.log(this.currentEl.contains(dom));
26272             if (this.currentEl == el) {
26273                 return;
26274             }
26275             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26276                 return;
26277             }
26278
26279         }
26280         
26281         if (this.currentTip.el) {
26282             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26283         }    
26284         //Roo.log(ev);
26285         
26286         if(!el || el.dom == document){
26287             return;
26288         }
26289         
26290         var bindEl = el;
26291         
26292         // you can not look for children, as if el is the body.. then everythign is the child..
26293         if (!el.attr('tooltip')) { //
26294             if (!el.select("[tooltip]").elements.length) {
26295                 return;
26296             }
26297             // is the mouse over this child...?
26298             bindEl = el.select("[tooltip]").first();
26299             var xy = ev.getXY();
26300             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26301                 //Roo.log("not in region.");
26302                 return;
26303             }
26304             //Roo.log("child element over..");
26305             
26306         }
26307         this.currentEl = bindEl;
26308         this.currentTip.bind(bindEl);
26309         this.currentRegion = Roo.lib.Region.getRegion(dom);
26310         this.currentTip.enter();
26311         
26312     },
26313     leave : function(ev)
26314     {
26315         var dom = ev.getTarget();
26316         //Roo.log(['leave',dom]);
26317         if (!this.currentEl) {
26318             return;
26319         }
26320         
26321         
26322         if (dom != this.currentEl.dom) {
26323             return;
26324         }
26325         var xy = ev.getXY();
26326         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26327             return;
26328         }
26329         // only activate leave if mouse cursor is outside... bounding box..
26330         
26331         
26332         
26333         
26334         if (this.currentTip) {
26335             this.currentTip.leave();
26336         }
26337         //Roo.log('clear currentEl');
26338         this.currentEl = false;
26339         
26340         
26341     },
26342     alignment : {
26343         'left' : ['r-l', [-2,0], 'right'],
26344         'right' : ['l-r', [2,0], 'left'],
26345         'bottom' : ['t-b', [0,2], 'top'],
26346         'top' : [ 'b-t', [0,-2], 'bottom']
26347     }
26348     
26349 });
26350
26351
26352 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26353     
26354     
26355     bindEl : false,
26356     
26357     delay : null, // can be { show : 300 , hide: 500}
26358     
26359     timeout : null,
26360     
26361     hoverState : null, //???
26362     
26363     placement : 'bottom', 
26364     
26365     alignment : false,
26366     
26367     getAutoCreate : function(){
26368     
26369         var cfg = {
26370            cls : 'tooltip',
26371            role : 'tooltip',
26372            cn : [
26373                 {
26374                     cls : 'tooltip-arrow'
26375                 },
26376                 {
26377                     cls : 'tooltip-inner'
26378                 }
26379            ]
26380         };
26381         
26382         return cfg;
26383     },
26384     bind : function(el)
26385     {
26386         this.bindEl = el;
26387     },
26388       
26389     
26390     enter : function () {
26391        
26392         if (this.timeout != null) {
26393             clearTimeout(this.timeout);
26394         }
26395         
26396         this.hoverState = 'in';
26397          //Roo.log("enter - show");
26398         if (!this.delay || !this.delay.show) {
26399             this.show();
26400             return;
26401         }
26402         var _t = this;
26403         this.timeout = setTimeout(function () {
26404             if (_t.hoverState == 'in') {
26405                 _t.show();
26406             }
26407         }, this.delay.show);
26408     },
26409     leave : function()
26410     {
26411         clearTimeout(this.timeout);
26412     
26413         this.hoverState = 'out';
26414          if (!this.delay || !this.delay.hide) {
26415             this.hide();
26416             return;
26417         }
26418        
26419         var _t = this;
26420         this.timeout = setTimeout(function () {
26421             //Roo.log("leave - timeout");
26422             
26423             if (_t.hoverState == 'out') {
26424                 _t.hide();
26425                 Roo.bootstrap.Tooltip.currentEl = false;
26426             }
26427         }, delay);
26428     },
26429     
26430     show : function (msg)
26431     {
26432         if (!this.el) {
26433             this.render(document.body);
26434         }
26435         // set content.
26436         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26437         
26438         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26439         
26440         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26441         
26442         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26443         
26444         var placement = typeof this.placement == 'function' ?
26445             this.placement.call(this, this.el, on_el) :
26446             this.placement;
26447             
26448         var autoToken = /\s?auto?\s?/i;
26449         var autoPlace = autoToken.test(placement);
26450         if (autoPlace) {
26451             placement = placement.replace(autoToken, '') || 'top';
26452         }
26453         
26454         //this.el.detach()
26455         //this.el.setXY([0,0]);
26456         this.el.show();
26457         //this.el.dom.style.display='block';
26458         
26459         //this.el.appendTo(on_el);
26460         
26461         var p = this.getPosition();
26462         var box = this.el.getBox();
26463         
26464         if (autoPlace) {
26465             // fixme..
26466         }
26467         
26468         var align = this.alignment[placement];
26469         
26470         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26471         
26472         if(placement == 'top' || placement == 'bottom'){
26473             if(xy[0] < 0){
26474                 placement = 'right';
26475             }
26476             
26477             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26478                 placement = 'left';
26479             }
26480             
26481             var scroll = Roo.select('body', true).first().getScroll();
26482             
26483             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26484                 placement = 'top';
26485             }
26486             
26487             align = this.alignment[placement];
26488         }
26489         
26490         this.el.alignTo(this.bindEl, align[0],align[1]);
26491         //var arrow = this.el.select('.arrow',true).first();
26492         //arrow.set(align[2], 
26493         
26494         this.el.addClass(placement);
26495         
26496         this.el.addClass('in fade');
26497         
26498         this.hoverState = null;
26499         
26500         if (this.el.hasClass('fade')) {
26501             // fade it?
26502         }
26503         
26504     },
26505     hide : function()
26506     {
26507          
26508         if (!this.el) {
26509             return;
26510         }
26511         //this.el.setXY([0,0]);
26512         this.el.removeClass('in');
26513         //this.el.hide();
26514         
26515     }
26516     
26517 });
26518  
26519
26520  /*
26521  * - LGPL
26522  *
26523  * Location Picker
26524  * 
26525  */
26526
26527 /**
26528  * @class Roo.bootstrap.LocationPicker
26529  * @extends Roo.bootstrap.Component
26530  * Bootstrap LocationPicker class
26531  * @cfg {Number} latitude Position when init default 0
26532  * @cfg {Number} longitude Position when init default 0
26533  * @cfg {Number} zoom default 15
26534  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26535  * @cfg {Boolean} mapTypeControl default false
26536  * @cfg {Boolean} disableDoubleClickZoom default false
26537  * @cfg {Boolean} scrollwheel default true
26538  * @cfg {Boolean} streetViewControl default false
26539  * @cfg {Number} radius default 0
26540  * @cfg {String} locationName
26541  * @cfg {Boolean} draggable default true
26542  * @cfg {Boolean} enableAutocomplete default false
26543  * @cfg {Boolean} enableReverseGeocode default true
26544  * @cfg {String} markerTitle
26545  * 
26546  * @constructor
26547  * Create a new LocationPicker
26548  * @param {Object} config The config object
26549  */
26550
26551
26552 Roo.bootstrap.LocationPicker = function(config){
26553     
26554     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26555     
26556     this.addEvents({
26557         /**
26558          * @event initial
26559          * Fires when the picker initialized.
26560          * @param {Roo.bootstrap.LocationPicker} this
26561          * @param {Google Location} location
26562          */
26563         initial : true,
26564         /**
26565          * @event positionchanged
26566          * Fires when the picker position changed.
26567          * @param {Roo.bootstrap.LocationPicker} this
26568          * @param {Google Location} location
26569          */
26570         positionchanged : true,
26571         /**
26572          * @event resize
26573          * Fires when the map resize.
26574          * @param {Roo.bootstrap.LocationPicker} this
26575          */
26576         resize : true,
26577         /**
26578          * @event show
26579          * Fires when the map show.
26580          * @param {Roo.bootstrap.LocationPicker} this
26581          */
26582         show : true,
26583         /**
26584          * @event hide
26585          * Fires when the map hide.
26586          * @param {Roo.bootstrap.LocationPicker} this
26587          */
26588         hide : true,
26589         /**
26590          * @event mapClick
26591          * Fires when click the map.
26592          * @param {Roo.bootstrap.LocationPicker} this
26593          * @param {Map event} e
26594          */
26595         mapClick : true,
26596         /**
26597          * @event mapRightClick
26598          * Fires when right click the map.
26599          * @param {Roo.bootstrap.LocationPicker} this
26600          * @param {Map event} e
26601          */
26602         mapRightClick : true,
26603         /**
26604          * @event markerClick
26605          * Fires when click the marker.
26606          * @param {Roo.bootstrap.LocationPicker} this
26607          * @param {Map event} e
26608          */
26609         markerClick : true,
26610         /**
26611          * @event markerRightClick
26612          * Fires when right click the marker.
26613          * @param {Roo.bootstrap.LocationPicker} this
26614          * @param {Map event} e
26615          */
26616         markerRightClick : true,
26617         /**
26618          * @event OverlayViewDraw
26619          * Fires when OverlayView Draw
26620          * @param {Roo.bootstrap.LocationPicker} this
26621          */
26622         OverlayViewDraw : true,
26623         /**
26624          * @event OverlayViewOnAdd
26625          * Fires when OverlayView Draw
26626          * @param {Roo.bootstrap.LocationPicker} this
26627          */
26628         OverlayViewOnAdd : true,
26629         /**
26630          * @event OverlayViewOnRemove
26631          * Fires when OverlayView Draw
26632          * @param {Roo.bootstrap.LocationPicker} this
26633          */
26634         OverlayViewOnRemove : true,
26635         /**
26636          * @event OverlayViewShow
26637          * Fires when OverlayView Draw
26638          * @param {Roo.bootstrap.LocationPicker} this
26639          * @param {Pixel} cpx
26640          */
26641         OverlayViewShow : true,
26642         /**
26643          * @event OverlayViewHide
26644          * Fires when OverlayView Draw
26645          * @param {Roo.bootstrap.LocationPicker} this
26646          */
26647         OverlayViewHide : true,
26648         /**
26649          * @event loadexception
26650          * Fires when load google lib failed.
26651          * @param {Roo.bootstrap.LocationPicker} this
26652          */
26653         loadexception : true
26654     });
26655         
26656 };
26657
26658 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26659     
26660     gMapContext: false,
26661     
26662     latitude: 0,
26663     longitude: 0,
26664     zoom: 15,
26665     mapTypeId: false,
26666     mapTypeControl: false,
26667     disableDoubleClickZoom: false,
26668     scrollwheel: true,
26669     streetViewControl: false,
26670     radius: 0,
26671     locationName: '',
26672     draggable: true,
26673     enableAutocomplete: false,
26674     enableReverseGeocode: true,
26675     markerTitle: '',
26676     
26677     getAutoCreate: function()
26678     {
26679
26680         var cfg = {
26681             tag: 'div',
26682             cls: 'roo-location-picker'
26683         };
26684         
26685         return cfg
26686     },
26687     
26688     initEvents: function(ct, position)
26689     {       
26690         if(!this.el.getWidth() || this.isApplied()){
26691             return;
26692         }
26693         
26694         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26695         
26696         this.initial();
26697     },
26698     
26699     initial: function()
26700     {
26701         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26702             this.fireEvent('loadexception', this);
26703             return;
26704         }
26705         
26706         if(!this.mapTypeId){
26707             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26708         }
26709         
26710         this.gMapContext = this.GMapContext();
26711         
26712         this.initOverlayView();
26713         
26714         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26715         
26716         var _this = this;
26717                 
26718         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26719             _this.setPosition(_this.gMapContext.marker.position);
26720         });
26721         
26722         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26723             _this.fireEvent('mapClick', this, event);
26724             
26725         });
26726
26727         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26728             _this.fireEvent('mapRightClick', this, event);
26729             
26730         });
26731         
26732         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26733             _this.fireEvent('markerClick', this, event);
26734             
26735         });
26736
26737         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26738             _this.fireEvent('markerRightClick', this, event);
26739             
26740         });
26741         
26742         this.setPosition(this.gMapContext.location);
26743         
26744         this.fireEvent('initial', this, this.gMapContext.location);
26745     },
26746     
26747     initOverlayView: function()
26748     {
26749         var _this = this;
26750         
26751         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26752             
26753             draw: function()
26754             {
26755                 _this.fireEvent('OverlayViewDraw', _this);
26756             },
26757             
26758             onAdd: function()
26759             {
26760                 _this.fireEvent('OverlayViewOnAdd', _this);
26761             },
26762             
26763             onRemove: function()
26764             {
26765                 _this.fireEvent('OverlayViewOnRemove', _this);
26766             },
26767             
26768             show: function(cpx)
26769             {
26770                 _this.fireEvent('OverlayViewShow', _this, cpx);
26771             },
26772             
26773             hide: function()
26774             {
26775                 _this.fireEvent('OverlayViewHide', _this);
26776             }
26777             
26778         });
26779     },
26780     
26781     fromLatLngToContainerPixel: function(event)
26782     {
26783         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26784     },
26785     
26786     isApplied: function() 
26787     {
26788         return this.getGmapContext() == false ? false : true;
26789     },
26790     
26791     getGmapContext: function() 
26792     {
26793         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26794     },
26795     
26796     GMapContext: function() 
26797     {
26798         var position = new google.maps.LatLng(this.latitude, this.longitude);
26799         
26800         var _map = new google.maps.Map(this.el.dom, {
26801             center: position,
26802             zoom: this.zoom,
26803             mapTypeId: this.mapTypeId,
26804             mapTypeControl: this.mapTypeControl,
26805             disableDoubleClickZoom: this.disableDoubleClickZoom,
26806             scrollwheel: this.scrollwheel,
26807             streetViewControl: this.streetViewControl,
26808             locationName: this.locationName,
26809             draggable: this.draggable,
26810             enableAutocomplete: this.enableAutocomplete,
26811             enableReverseGeocode: this.enableReverseGeocode
26812         });
26813         
26814         var _marker = new google.maps.Marker({
26815             position: position,
26816             map: _map,
26817             title: this.markerTitle,
26818             draggable: this.draggable
26819         });
26820         
26821         return {
26822             map: _map,
26823             marker: _marker,
26824             circle: null,
26825             location: position,
26826             radius: this.radius,
26827             locationName: this.locationName,
26828             addressComponents: {
26829                 formatted_address: null,
26830                 addressLine1: null,
26831                 addressLine2: null,
26832                 streetName: null,
26833                 streetNumber: null,
26834                 city: null,
26835                 district: null,
26836                 state: null,
26837                 stateOrProvince: null
26838             },
26839             settings: this,
26840             domContainer: this.el.dom,
26841             geodecoder: new google.maps.Geocoder()
26842         };
26843     },
26844     
26845     drawCircle: function(center, radius, options) 
26846     {
26847         if (this.gMapContext.circle != null) {
26848             this.gMapContext.circle.setMap(null);
26849         }
26850         if (radius > 0) {
26851             radius *= 1;
26852             options = Roo.apply({}, options, {
26853                 strokeColor: "#0000FF",
26854                 strokeOpacity: .35,
26855                 strokeWeight: 2,
26856                 fillColor: "#0000FF",
26857                 fillOpacity: .2
26858             });
26859             
26860             options.map = this.gMapContext.map;
26861             options.radius = radius;
26862             options.center = center;
26863             this.gMapContext.circle = new google.maps.Circle(options);
26864             return this.gMapContext.circle;
26865         }
26866         
26867         return null;
26868     },
26869     
26870     setPosition: function(location) 
26871     {
26872         this.gMapContext.location = location;
26873         this.gMapContext.marker.setPosition(location);
26874         this.gMapContext.map.panTo(location);
26875         this.drawCircle(location, this.gMapContext.radius, {});
26876         
26877         var _this = this;
26878         
26879         if (this.gMapContext.settings.enableReverseGeocode) {
26880             this.gMapContext.geodecoder.geocode({
26881                 latLng: this.gMapContext.location
26882             }, function(results, status) {
26883                 
26884                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26885                     _this.gMapContext.locationName = results[0].formatted_address;
26886                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26887                     
26888                     _this.fireEvent('positionchanged', this, location);
26889                 }
26890             });
26891             
26892             return;
26893         }
26894         
26895         this.fireEvent('positionchanged', this, location);
26896     },
26897     
26898     resize: function()
26899     {
26900         google.maps.event.trigger(this.gMapContext.map, "resize");
26901         
26902         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26903         
26904         this.fireEvent('resize', this);
26905     },
26906     
26907     setPositionByLatLng: function(latitude, longitude)
26908     {
26909         this.setPosition(new google.maps.LatLng(latitude, longitude));
26910     },
26911     
26912     getCurrentPosition: function() 
26913     {
26914         return {
26915             latitude: this.gMapContext.location.lat(),
26916             longitude: this.gMapContext.location.lng()
26917         };
26918     },
26919     
26920     getAddressName: function() 
26921     {
26922         return this.gMapContext.locationName;
26923     },
26924     
26925     getAddressComponents: function() 
26926     {
26927         return this.gMapContext.addressComponents;
26928     },
26929     
26930     address_component_from_google_geocode: function(address_components) 
26931     {
26932         var result = {};
26933         
26934         for (var i = 0; i < address_components.length; i++) {
26935             var component = address_components[i];
26936             if (component.types.indexOf("postal_code") >= 0) {
26937                 result.postalCode = component.short_name;
26938             } else if (component.types.indexOf("street_number") >= 0) {
26939                 result.streetNumber = component.short_name;
26940             } else if (component.types.indexOf("route") >= 0) {
26941                 result.streetName = component.short_name;
26942             } else if (component.types.indexOf("neighborhood") >= 0) {
26943                 result.city = component.short_name;
26944             } else if (component.types.indexOf("locality") >= 0) {
26945                 result.city = component.short_name;
26946             } else if (component.types.indexOf("sublocality") >= 0) {
26947                 result.district = component.short_name;
26948             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26949                 result.stateOrProvince = component.short_name;
26950             } else if (component.types.indexOf("country") >= 0) {
26951                 result.country = component.short_name;
26952             }
26953         }
26954         
26955         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26956         result.addressLine2 = "";
26957         return result;
26958     },
26959     
26960     setZoomLevel: function(zoom)
26961     {
26962         this.gMapContext.map.setZoom(zoom);
26963     },
26964     
26965     show: function()
26966     {
26967         if(!this.el){
26968             return;
26969         }
26970         
26971         this.el.show();
26972         
26973         this.resize();
26974         
26975         this.fireEvent('show', this);
26976     },
26977     
26978     hide: function()
26979     {
26980         if(!this.el){
26981             return;
26982         }
26983         
26984         this.el.hide();
26985         
26986         this.fireEvent('hide', this);
26987     }
26988     
26989 });
26990
26991 Roo.apply(Roo.bootstrap.LocationPicker, {
26992     
26993     OverlayView : function(map, options)
26994     {
26995         options = options || {};
26996         
26997         this.setMap(map);
26998     }
26999     
27000     
27001 });/*
27002  * - LGPL
27003  *
27004  * Alert
27005  * 
27006  */
27007
27008 /**
27009  * @class Roo.bootstrap.Alert
27010  * @extends Roo.bootstrap.Component
27011  * Bootstrap Alert class
27012  * @cfg {String} title The title of alert
27013  * @cfg {String} html The content of alert
27014  * @cfg {String} weight (  success | info | warning | danger )
27015  * @cfg {String} faicon font-awesomeicon
27016  * 
27017  * @constructor
27018  * Create a new alert
27019  * @param {Object} config The config object
27020  */
27021
27022
27023 Roo.bootstrap.Alert = function(config){
27024     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27025     
27026 };
27027
27028 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27029     
27030     title: '',
27031     html: '',
27032     weight: false,
27033     faicon: false,
27034     
27035     getAutoCreate : function()
27036     {
27037         
27038         var cfg = {
27039             tag : 'div',
27040             cls : 'alert',
27041             cn : [
27042                 {
27043                     tag : 'i',
27044                     cls : 'roo-alert-icon'
27045                     
27046                 },
27047                 {
27048                     tag : 'b',
27049                     cls : 'roo-alert-title',
27050                     html : this.title
27051                 },
27052                 {
27053                     tag : 'span',
27054                     cls : 'roo-alert-text',
27055                     html : this.html
27056                 }
27057             ]
27058         };
27059         
27060         if(this.faicon){
27061             cfg.cn[0].cls += ' fa ' + this.faicon;
27062         }
27063         
27064         if(this.weight){
27065             cfg.cls += ' alert-' + this.weight;
27066         }
27067         
27068         return cfg;
27069     },
27070     
27071     initEvents: function() 
27072     {
27073         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27074     },
27075     
27076     setTitle : function(str)
27077     {
27078         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27079     },
27080     
27081     setText : function(str)
27082     {
27083         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27084     },
27085     
27086     setWeight : function(weight)
27087     {
27088         if(this.weight){
27089             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27090         }
27091         
27092         this.weight = weight;
27093         
27094         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27095     },
27096     
27097     setIcon : function(icon)
27098     {
27099         if(this.faicon){
27100             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27101         }
27102         
27103         this.faicon = icon;
27104         
27105         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27106     },
27107     
27108     hide: function() 
27109     {
27110         this.el.hide();   
27111     },
27112     
27113     show: function() 
27114     {  
27115         this.el.show();   
27116     }
27117     
27118 });
27119
27120  
27121 /*
27122 * Licence: LGPL
27123 */
27124
27125 /**
27126  * @class Roo.bootstrap.UploadCropbox
27127  * @extends Roo.bootstrap.Component
27128  * Bootstrap UploadCropbox class
27129  * @cfg {String} emptyText show when image has been loaded
27130  * @cfg {String} rotateNotify show when image too small to rotate
27131  * @cfg {Number} errorTimeout default 3000
27132  * @cfg {Number} minWidth default 300
27133  * @cfg {Number} minHeight default 300
27134  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27135  * @cfg {Boolean} isDocument (true|false) default false
27136  * @cfg {String} url action url
27137  * @cfg {String} paramName default 'imageUpload'
27138  * @cfg {String} method default POST
27139  * @cfg {Boolean} loadMask (true|false) default true
27140  * @cfg {Boolean} loadingText default 'Loading...'
27141  * 
27142  * @constructor
27143  * Create a new UploadCropbox
27144  * @param {Object} config The config object
27145  */
27146
27147 Roo.bootstrap.UploadCropbox = function(config){
27148     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27149     
27150     this.addEvents({
27151         /**
27152          * @event beforeselectfile
27153          * Fire before select file
27154          * @param {Roo.bootstrap.UploadCropbox} this
27155          */
27156         "beforeselectfile" : true,
27157         /**
27158          * @event initial
27159          * Fire after initEvent
27160          * @param {Roo.bootstrap.UploadCropbox} this
27161          */
27162         "initial" : true,
27163         /**
27164          * @event crop
27165          * Fire after initEvent
27166          * @param {Roo.bootstrap.UploadCropbox} this
27167          * @param {String} data
27168          */
27169         "crop" : true,
27170         /**
27171          * @event prepare
27172          * Fire when preparing the file data
27173          * @param {Roo.bootstrap.UploadCropbox} this
27174          * @param {Object} file
27175          */
27176         "prepare" : true,
27177         /**
27178          * @event exception
27179          * Fire when get exception
27180          * @param {Roo.bootstrap.UploadCropbox} this
27181          * @param {XMLHttpRequest} xhr
27182          */
27183         "exception" : true,
27184         /**
27185          * @event beforeloadcanvas
27186          * Fire before load the canvas
27187          * @param {Roo.bootstrap.UploadCropbox} this
27188          * @param {String} src
27189          */
27190         "beforeloadcanvas" : true,
27191         /**
27192          * @event trash
27193          * Fire when trash image
27194          * @param {Roo.bootstrap.UploadCropbox} this
27195          */
27196         "trash" : true,
27197         /**
27198          * @event download
27199          * Fire when download the image
27200          * @param {Roo.bootstrap.UploadCropbox} this
27201          */
27202         "download" : true,
27203         /**
27204          * @event footerbuttonclick
27205          * Fire when footerbuttonclick
27206          * @param {Roo.bootstrap.UploadCropbox} this
27207          * @param {String} type
27208          */
27209         "footerbuttonclick" : true,
27210         /**
27211          * @event resize
27212          * Fire when resize
27213          * @param {Roo.bootstrap.UploadCropbox} this
27214          */
27215         "resize" : true,
27216         /**
27217          * @event rotate
27218          * Fire when rotate the image
27219          * @param {Roo.bootstrap.UploadCropbox} this
27220          * @param {String} pos
27221          */
27222         "rotate" : true,
27223         /**
27224          * @event inspect
27225          * Fire when inspect the file
27226          * @param {Roo.bootstrap.UploadCropbox} this
27227          * @param {Object} file
27228          */
27229         "inspect" : true,
27230         /**
27231          * @event upload
27232          * Fire when xhr upload the file
27233          * @param {Roo.bootstrap.UploadCropbox} this
27234          * @param {Object} data
27235          */
27236         "upload" : true,
27237         /**
27238          * @event arrange
27239          * Fire when arrange the file data
27240          * @param {Roo.bootstrap.UploadCropbox} this
27241          * @param {Object} formData
27242          */
27243         "arrange" : true
27244     });
27245     
27246     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27247 };
27248
27249 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27250     
27251     emptyText : 'Click to upload image',
27252     rotateNotify : 'Image is too small to rotate',
27253     errorTimeout : 3000,
27254     scale : 0,
27255     baseScale : 1,
27256     rotate : 0,
27257     dragable : false,
27258     pinching : false,
27259     mouseX : 0,
27260     mouseY : 0,
27261     cropData : false,
27262     minWidth : 300,
27263     minHeight : 300,
27264     file : false,
27265     exif : {},
27266     baseRotate : 1,
27267     cropType : 'image/jpeg',
27268     buttons : false,
27269     canvasLoaded : false,
27270     isDocument : false,
27271     method : 'POST',
27272     paramName : 'imageUpload',
27273     loadMask : true,
27274     loadingText : 'Loading...',
27275     maskEl : false,
27276     
27277     getAutoCreate : function()
27278     {
27279         var cfg = {
27280             tag : 'div',
27281             cls : 'roo-upload-cropbox',
27282             cn : [
27283                 {
27284                     tag : 'input',
27285                     cls : 'roo-upload-cropbox-selector',
27286                     type : 'file'
27287                 },
27288                 {
27289                     tag : 'div',
27290                     cls : 'roo-upload-cropbox-body',
27291                     style : 'cursor:pointer',
27292                     cn : [
27293                         {
27294                             tag : 'div',
27295                             cls : 'roo-upload-cropbox-preview'
27296                         },
27297                         {
27298                             tag : 'div',
27299                             cls : 'roo-upload-cropbox-thumb'
27300                         },
27301                         {
27302                             tag : 'div',
27303                             cls : 'roo-upload-cropbox-empty-notify',
27304                             html : this.emptyText
27305                         },
27306                         {
27307                             tag : 'div',
27308                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27309                             html : this.rotateNotify
27310                         }
27311                     ]
27312                 },
27313                 {
27314                     tag : 'div',
27315                     cls : 'roo-upload-cropbox-footer',
27316                     cn : {
27317                         tag : 'div',
27318                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27319                         cn : []
27320                     }
27321                 }
27322             ]
27323         };
27324         
27325         return cfg;
27326     },
27327     
27328     onRender : function(ct, position)
27329     {
27330         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27331         
27332         if (this.buttons.length) {
27333             
27334             Roo.each(this.buttons, function(bb) {
27335                 
27336                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27337                 
27338                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27339                 
27340             }, this);
27341         }
27342         
27343         if(this.loadMask){
27344             this.maskEl = this.el;
27345         }
27346     },
27347     
27348     initEvents : function()
27349     {
27350         this.urlAPI = (window.createObjectURL && window) || 
27351                                 (window.URL && URL.revokeObjectURL && URL) || 
27352                                 (window.webkitURL && webkitURL);
27353                         
27354         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27355         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27356         
27357         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27358         this.selectorEl.hide();
27359         
27360         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27361         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27362         
27363         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27364         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27365         this.thumbEl.hide();
27366         
27367         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27368         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27369         
27370         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27371         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27372         this.errorEl.hide();
27373         
27374         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27375         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27376         this.footerEl.hide();
27377         
27378         this.setThumbBoxSize();
27379         
27380         this.bind();
27381         
27382         this.resize();
27383         
27384         this.fireEvent('initial', this);
27385     },
27386
27387     bind : function()
27388     {
27389         var _this = this;
27390         
27391         window.addEventListener("resize", function() { _this.resize(); } );
27392         
27393         this.bodyEl.on('click', this.beforeSelectFile, this);
27394         
27395         if(Roo.isTouch){
27396             this.bodyEl.on('touchstart', this.onTouchStart, this);
27397             this.bodyEl.on('touchmove', this.onTouchMove, this);
27398             this.bodyEl.on('touchend', this.onTouchEnd, this);
27399         }
27400         
27401         if(!Roo.isTouch){
27402             this.bodyEl.on('mousedown', this.onMouseDown, this);
27403             this.bodyEl.on('mousemove', this.onMouseMove, this);
27404             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27405             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27406             Roo.get(document).on('mouseup', this.onMouseUp, this);
27407         }
27408         
27409         this.selectorEl.on('change', this.onFileSelected, this);
27410     },
27411     
27412     reset : function()
27413     {    
27414         this.scale = 0;
27415         this.baseScale = 1;
27416         this.rotate = 0;
27417         this.baseRotate = 1;
27418         this.dragable = false;
27419         this.pinching = false;
27420         this.mouseX = 0;
27421         this.mouseY = 0;
27422         this.cropData = false;
27423         this.notifyEl.dom.innerHTML = this.emptyText;
27424         
27425         this.selectorEl.dom.value = '';
27426         
27427     },
27428     
27429     resize : function()
27430     {
27431         if(this.fireEvent('resize', this) != false){
27432             this.setThumbBoxPosition();
27433             this.setCanvasPosition();
27434         }
27435     },
27436     
27437     onFooterButtonClick : function(e, el, o, type)
27438     {
27439         switch (type) {
27440             case 'rotate-left' :
27441                 this.onRotateLeft(e);
27442                 break;
27443             case 'rotate-right' :
27444                 this.onRotateRight(e);
27445                 break;
27446             case 'picture' :
27447                 this.beforeSelectFile(e);
27448                 break;
27449             case 'trash' :
27450                 this.trash(e);
27451                 break;
27452             case 'crop' :
27453                 this.crop(e);
27454                 break;
27455             case 'download' :
27456                 this.download(e);
27457                 break;
27458             default :
27459                 break;
27460         }
27461         
27462         this.fireEvent('footerbuttonclick', this, type);
27463     },
27464     
27465     beforeSelectFile : function(e)
27466     {
27467         e.preventDefault();
27468         
27469         if(this.fireEvent('beforeselectfile', this) != false){
27470             this.selectorEl.dom.click();
27471         }
27472     },
27473     
27474     onFileSelected : function(e)
27475     {
27476         e.preventDefault();
27477         
27478         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27479             return;
27480         }
27481         
27482         var file = this.selectorEl.dom.files[0];
27483         
27484         if(this.fireEvent('inspect', this, file) != false){
27485             this.prepare(file);
27486         }
27487         
27488     },
27489     
27490     trash : function(e)
27491     {
27492         this.fireEvent('trash', this);
27493     },
27494     
27495     download : function(e)
27496     {
27497         this.fireEvent('download', this);
27498     },
27499     
27500     loadCanvas : function(src)
27501     {   
27502         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27503             
27504             this.reset();
27505             
27506             this.imageEl = document.createElement('img');
27507             
27508             var _this = this;
27509             
27510             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27511             
27512             this.imageEl.src = src;
27513         }
27514     },
27515     
27516     onLoadCanvas : function()
27517     {   
27518         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27519         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27520         
27521         this.bodyEl.un('click', this.beforeSelectFile, this);
27522         
27523         this.notifyEl.hide();
27524         this.thumbEl.show();
27525         this.footerEl.show();
27526         
27527         this.baseRotateLevel();
27528         
27529         if(this.isDocument){
27530             this.setThumbBoxSize();
27531         }
27532         
27533         this.setThumbBoxPosition();
27534         
27535         this.baseScaleLevel();
27536         
27537         this.draw();
27538         
27539         this.resize();
27540         
27541         this.canvasLoaded = true;
27542         
27543         if(this.loadMask){
27544             this.maskEl.unmask();
27545         }
27546         
27547     },
27548     
27549     setCanvasPosition : function()
27550     {   
27551         if(!this.canvasEl){
27552             return;
27553         }
27554         
27555         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27556         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27557         
27558         this.previewEl.setLeft(pw);
27559         this.previewEl.setTop(ph);
27560         
27561     },
27562     
27563     onMouseDown : function(e)
27564     {   
27565         e.stopEvent();
27566         
27567         this.dragable = true;
27568         this.pinching = false;
27569         
27570         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27571             this.dragable = false;
27572             return;
27573         }
27574         
27575         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27576         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27577         
27578     },
27579     
27580     onMouseMove : function(e)
27581     {   
27582         e.stopEvent();
27583         
27584         if(!this.canvasLoaded){
27585             return;
27586         }
27587         
27588         if (!this.dragable){
27589             return;
27590         }
27591         
27592         var minX = Math.ceil(this.thumbEl.getLeft(true));
27593         var minY = Math.ceil(this.thumbEl.getTop(true));
27594         
27595         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27596         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27597         
27598         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27599         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27600         
27601         x = x - this.mouseX;
27602         y = y - this.mouseY;
27603         
27604         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27605         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27606         
27607         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27608         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27609         
27610         this.previewEl.setLeft(bgX);
27611         this.previewEl.setTop(bgY);
27612         
27613         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27614         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27615     },
27616     
27617     onMouseUp : function(e)
27618     {   
27619         e.stopEvent();
27620         
27621         this.dragable = false;
27622     },
27623     
27624     onMouseWheel : function(e)
27625     {   
27626         e.stopEvent();
27627         
27628         this.startScale = this.scale;
27629         
27630         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27631         
27632         if(!this.zoomable()){
27633             this.scale = this.startScale;
27634             return;
27635         }
27636         
27637         this.draw();
27638         
27639         return;
27640     },
27641     
27642     zoomable : function()
27643     {
27644         var minScale = this.thumbEl.getWidth() / this.minWidth;
27645         
27646         if(this.minWidth < this.minHeight){
27647             minScale = this.thumbEl.getHeight() / this.minHeight;
27648         }
27649         
27650         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27651         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27652         
27653         if(
27654                 this.isDocument &&
27655                 (this.rotate == 0 || this.rotate == 180) && 
27656                 (
27657                     width > this.imageEl.OriginWidth || 
27658                     height > this.imageEl.OriginHeight ||
27659                     (width < this.minWidth && height < this.minHeight)
27660                 )
27661         ){
27662             return false;
27663         }
27664         
27665         if(
27666                 this.isDocument &&
27667                 (this.rotate == 90 || this.rotate == 270) && 
27668                 (
27669                     width > this.imageEl.OriginWidth || 
27670                     height > this.imageEl.OriginHeight ||
27671                     (width < this.minHeight && height < this.minWidth)
27672                 )
27673         ){
27674             return false;
27675         }
27676         
27677         if(
27678                 !this.isDocument &&
27679                 (this.rotate == 0 || this.rotate == 180) && 
27680                 (
27681                     width < this.minWidth || 
27682                     width > this.imageEl.OriginWidth || 
27683                     height < this.minHeight || 
27684                     height > this.imageEl.OriginHeight
27685                 )
27686         ){
27687             return false;
27688         }
27689         
27690         if(
27691                 !this.isDocument &&
27692                 (this.rotate == 90 || this.rotate == 270) && 
27693                 (
27694                     width < this.minHeight || 
27695                     width > this.imageEl.OriginWidth || 
27696                     height < this.minWidth || 
27697                     height > this.imageEl.OriginHeight
27698                 )
27699         ){
27700             return false;
27701         }
27702         
27703         return true;
27704         
27705     },
27706     
27707     onRotateLeft : function(e)
27708     {   
27709         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27710             
27711             var minScale = this.thumbEl.getWidth() / this.minWidth;
27712             
27713             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27714             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27715             
27716             this.startScale = this.scale;
27717             
27718             while (this.getScaleLevel() < minScale){
27719             
27720                 this.scale = this.scale + 1;
27721                 
27722                 if(!this.zoomable()){
27723                     break;
27724                 }
27725                 
27726                 if(
27727                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27728                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27729                 ){
27730                     continue;
27731                 }
27732                 
27733                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27734
27735                 this.draw();
27736                 
27737                 return;
27738             }
27739             
27740             this.scale = this.startScale;
27741             
27742             this.onRotateFail();
27743             
27744             return false;
27745         }
27746         
27747         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27748
27749         if(this.isDocument){
27750             this.setThumbBoxSize();
27751             this.setThumbBoxPosition();
27752             this.setCanvasPosition();
27753         }
27754         
27755         this.draw();
27756         
27757         this.fireEvent('rotate', this, 'left');
27758         
27759     },
27760     
27761     onRotateRight : function(e)
27762     {
27763         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27764             
27765             var minScale = this.thumbEl.getWidth() / this.minWidth;
27766         
27767             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27768             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27769             
27770             this.startScale = this.scale;
27771             
27772             while (this.getScaleLevel() < minScale){
27773             
27774                 this.scale = this.scale + 1;
27775                 
27776                 if(!this.zoomable()){
27777                     break;
27778                 }
27779                 
27780                 if(
27781                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27782                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27783                 ){
27784                     continue;
27785                 }
27786                 
27787                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27788
27789                 this.draw();
27790                 
27791                 return;
27792             }
27793             
27794             this.scale = this.startScale;
27795             
27796             this.onRotateFail();
27797             
27798             return false;
27799         }
27800         
27801         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27802
27803         if(this.isDocument){
27804             this.setThumbBoxSize();
27805             this.setThumbBoxPosition();
27806             this.setCanvasPosition();
27807         }
27808         
27809         this.draw();
27810         
27811         this.fireEvent('rotate', this, 'right');
27812     },
27813     
27814     onRotateFail : function()
27815     {
27816         this.errorEl.show(true);
27817         
27818         var _this = this;
27819         
27820         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27821     },
27822     
27823     draw : function()
27824     {
27825         this.previewEl.dom.innerHTML = '';
27826         
27827         var canvasEl = document.createElement("canvas");
27828         
27829         var contextEl = canvasEl.getContext("2d");
27830         
27831         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27832         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27833         var center = this.imageEl.OriginWidth / 2;
27834         
27835         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27836             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27837             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27838             center = this.imageEl.OriginHeight / 2;
27839         }
27840         
27841         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27842         
27843         contextEl.translate(center, center);
27844         contextEl.rotate(this.rotate * Math.PI / 180);
27845
27846         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27847         
27848         this.canvasEl = document.createElement("canvas");
27849         
27850         this.contextEl = this.canvasEl.getContext("2d");
27851         
27852         switch (this.rotate) {
27853             case 0 :
27854                 
27855                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27856                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27857                 
27858                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27859                 
27860                 break;
27861             case 90 : 
27862                 
27863                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27864                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27865                 
27866                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27867                     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);
27868                     break;
27869                 }
27870                 
27871                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27872                 
27873                 break;
27874             case 180 :
27875                 
27876                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27877                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27878                 
27879                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27880                     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);
27881                     break;
27882                 }
27883                 
27884                 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);
27885                 
27886                 break;
27887             case 270 :
27888                 
27889                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27890                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27891         
27892                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27893                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27894                     break;
27895                 }
27896                 
27897                 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);
27898                 
27899                 break;
27900             default : 
27901                 break;
27902         }
27903         
27904         this.previewEl.appendChild(this.canvasEl);
27905         
27906         this.setCanvasPosition();
27907     },
27908     
27909     crop : function()
27910     {
27911         if(!this.canvasLoaded){
27912             return;
27913         }
27914         
27915         var imageCanvas = document.createElement("canvas");
27916         
27917         var imageContext = imageCanvas.getContext("2d");
27918         
27919         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27920         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27921         
27922         var center = imageCanvas.width / 2;
27923         
27924         imageContext.translate(center, center);
27925         
27926         imageContext.rotate(this.rotate * Math.PI / 180);
27927         
27928         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27929         
27930         var canvas = document.createElement("canvas");
27931         
27932         var context = canvas.getContext("2d");
27933                 
27934         canvas.width = this.minWidth;
27935         canvas.height = this.minHeight;
27936
27937         switch (this.rotate) {
27938             case 0 :
27939                 
27940                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27941                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27942                 
27943                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27944                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27945                 
27946                 var targetWidth = this.minWidth - 2 * x;
27947                 var targetHeight = this.minHeight - 2 * y;
27948                 
27949                 var scale = 1;
27950                 
27951                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27952                     scale = targetWidth / width;
27953                 }
27954                 
27955                 if(x > 0 && y == 0){
27956                     scale = targetHeight / height;
27957                 }
27958                 
27959                 if(x > 0 && y > 0){
27960                     scale = targetWidth / width;
27961                     
27962                     if(width < height){
27963                         scale = targetHeight / height;
27964                     }
27965                 }
27966                 
27967                 context.scale(scale, scale);
27968                 
27969                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27970                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27971
27972                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27973                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27974
27975                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27976                 
27977                 break;
27978             case 90 : 
27979                 
27980                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27981                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27982                 
27983                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27984                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27985                 
27986                 var targetWidth = this.minWidth - 2 * x;
27987                 var targetHeight = this.minHeight - 2 * y;
27988                 
27989                 var scale = 1;
27990                 
27991                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27992                     scale = targetWidth / width;
27993                 }
27994                 
27995                 if(x > 0 && y == 0){
27996                     scale = targetHeight / height;
27997                 }
27998                 
27999                 if(x > 0 && y > 0){
28000                     scale = targetWidth / width;
28001                     
28002                     if(width < height){
28003                         scale = targetHeight / height;
28004                     }
28005                 }
28006                 
28007                 context.scale(scale, scale);
28008                 
28009                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28010                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28011
28012                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28013                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28014                 
28015                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28016                 
28017                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28018                 
28019                 break;
28020             case 180 :
28021                 
28022                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28023                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28024                 
28025                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28026                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28027                 
28028                 var targetWidth = this.minWidth - 2 * x;
28029                 var targetHeight = this.minHeight - 2 * y;
28030                 
28031                 var scale = 1;
28032                 
28033                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28034                     scale = targetWidth / width;
28035                 }
28036                 
28037                 if(x > 0 && y == 0){
28038                     scale = targetHeight / height;
28039                 }
28040                 
28041                 if(x > 0 && y > 0){
28042                     scale = targetWidth / width;
28043                     
28044                     if(width < height){
28045                         scale = targetHeight / height;
28046                     }
28047                 }
28048                 
28049                 context.scale(scale, scale);
28050                 
28051                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28052                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28053
28054                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28055                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28056
28057                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28058                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28059                 
28060                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28061                 
28062                 break;
28063             case 270 :
28064                 
28065                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28066                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28067                 
28068                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28069                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28070                 
28071                 var targetWidth = this.minWidth - 2 * x;
28072                 var targetHeight = this.minHeight - 2 * y;
28073                 
28074                 var scale = 1;
28075                 
28076                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28077                     scale = targetWidth / width;
28078                 }
28079                 
28080                 if(x > 0 && y == 0){
28081                     scale = targetHeight / height;
28082                 }
28083                 
28084                 if(x > 0 && y > 0){
28085                     scale = targetWidth / width;
28086                     
28087                     if(width < height){
28088                         scale = targetHeight / height;
28089                     }
28090                 }
28091                 
28092                 context.scale(scale, scale);
28093                 
28094                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28095                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28096
28097                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28098                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28099                 
28100                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28101                 
28102                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28103                 
28104                 break;
28105             default : 
28106                 break;
28107         }
28108         
28109         this.cropData = canvas.toDataURL(this.cropType);
28110         
28111         if(this.fireEvent('crop', this, this.cropData) !== false){
28112             this.process(this.file, this.cropData);
28113         }
28114         
28115         return;
28116         
28117     },
28118     
28119     setThumbBoxSize : function()
28120     {
28121         var width, height;
28122         
28123         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28124             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28125             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28126             
28127             this.minWidth = width;
28128             this.minHeight = height;
28129             
28130             if(this.rotate == 90 || this.rotate == 270){
28131                 this.minWidth = height;
28132                 this.minHeight = width;
28133             }
28134         }
28135         
28136         height = 300;
28137         width = Math.ceil(this.minWidth * height / this.minHeight);
28138         
28139         if(this.minWidth > this.minHeight){
28140             width = 300;
28141             height = Math.ceil(this.minHeight * width / this.minWidth);
28142         }
28143         
28144         this.thumbEl.setStyle({
28145             width : width + 'px',
28146             height : height + 'px'
28147         });
28148
28149         return;
28150             
28151     },
28152     
28153     setThumbBoxPosition : function()
28154     {
28155         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28156         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28157         
28158         this.thumbEl.setLeft(x);
28159         this.thumbEl.setTop(y);
28160         
28161     },
28162     
28163     baseRotateLevel : function()
28164     {
28165         this.baseRotate = 1;
28166         
28167         if(
28168                 typeof(this.exif) != 'undefined' &&
28169                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28170                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28171         ){
28172             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28173         }
28174         
28175         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28176         
28177     },
28178     
28179     baseScaleLevel : function()
28180     {
28181         var width, height;
28182         
28183         if(this.isDocument){
28184             
28185             if(this.baseRotate == 6 || this.baseRotate == 8){
28186             
28187                 height = this.thumbEl.getHeight();
28188                 this.baseScale = height / this.imageEl.OriginWidth;
28189
28190                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28191                     width = this.thumbEl.getWidth();
28192                     this.baseScale = width / this.imageEl.OriginHeight;
28193                 }
28194
28195                 return;
28196             }
28197
28198             height = this.thumbEl.getHeight();
28199             this.baseScale = height / this.imageEl.OriginHeight;
28200
28201             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28202                 width = this.thumbEl.getWidth();
28203                 this.baseScale = width / this.imageEl.OriginWidth;
28204             }
28205
28206             return;
28207         }
28208         
28209         if(this.baseRotate == 6 || this.baseRotate == 8){
28210             
28211             width = this.thumbEl.getHeight();
28212             this.baseScale = width / this.imageEl.OriginHeight;
28213             
28214             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28215                 height = this.thumbEl.getWidth();
28216                 this.baseScale = height / this.imageEl.OriginHeight;
28217             }
28218             
28219             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28220                 height = this.thumbEl.getWidth();
28221                 this.baseScale = height / this.imageEl.OriginHeight;
28222                 
28223                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28224                     width = this.thumbEl.getHeight();
28225                     this.baseScale = width / this.imageEl.OriginWidth;
28226                 }
28227             }
28228             
28229             return;
28230         }
28231         
28232         width = this.thumbEl.getWidth();
28233         this.baseScale = width / this.imageEl.OriginWidth;
28234         
28235         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28236             height = this.thumbEl.getHeight();
28237             this.baseScale = height / this.imageEl.OriginHeight;
28238         }
28239         
28240         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28241             
28242             height = this.thumbEl.getHeight();
28243             this.baseScale = height / this.imageEl.OriginHeight;
28244             
28245             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28246                 width = this.thumbEl.getWidth();
28247                 this.baseScale = width / this.imageEl.OriginWidth;
28248             }
28249             
28250         }
28251         
28252         return;
28253     },
28254     
28255     getScaleLevel : function()
28256     {
28257         return this.baseScale * Math.pow(1.1, this.scale);
28258     },
28259     
28260     onTouchStart : function(e)
28261     {
28262         if(!this.canvasLoaded){
28263             this.beforeSelectFile(e);
28264             return;
28265         }
28266         
28267         var touches = e.browserEvent.touches;
28268         
28269         if(!touches){
28270             return;
28271         }
28272         
28273         if(touches.length == 1){
28274             this.onMouseDown(e);
28275             return;
28276         }
28277         
28278         if(touches.length != 2){
28279             return;
28280         }
28281         
28282         var coords = [];
28283         
28284         for(var i = 0, finger; finger = touches[i]; i++){
28285             coords.push(finger.pageX, finger.pageY);
28286         }
28287         
28288         var x = Math.pow(coords[0] - coords[2], 2);
28289         var y = Math.pow(coords[1] - coords[3], 2);
28290         
28291         this.startDistance = Math.sqrt(x + y);
28292         
28293         this.startScale = this.scale;
28294         
28295         this.pinching = true;
28296         this.dragable = false;
28297         
28298     },
28299     
28300     onTouchMove : function(e)
28301     {
28302         if(!this.pinching && !this.dragable){
28303             return;
28304         }
28305         
28306         var touches = e.browserEvent.touches;
28307         
28308         if(!touches){
28309             return;
28310         }
28311         
28312         if(this.dragable){
28313             this.onMouseMove(e);
28314             return;
28315         }
28316         
28317         var coords = [];
28318         
28319         for(var i = 0, finger; finger = touches[i]; i++){
28320             coords.push(finger.pageX, finger.pageY);
28321         }
28322         
28323         var x = Math.pow(coords[0] - coords[2], 2);
28324         var y = Math.pow(coords[1] - coords[3], 2);
28325         
28326         this.endDistance = Math.sqrt(x + y);
28327         
28328         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28329         
28330         if(!this.zoomable()){
28331             this.scale = this.startScale;
28332             return;
28333         }
28334         
28335         this.draw();
28336         
28337     },
28338     
28339     onTouchEnd : function(e)
28340     {
28341         this.pinching = false;
28342         this.dragable = false;
28343         
28344     },
28345     
28346     process : function(file, crop)
28347     {
28348         if(this.loadMask){
28349             this.maskEl.mask(this.loadingText);
28350         }
28351         
28352         this.xhr = new XMLHttpRequest();
28353         
28354         file.xhr = this.xhr;
28355
28356         this.xhr.open(this.method, this.url, true);
28357         
28358         var headers = {
28359             "Accept": "application/json",
28360             "Cache-Control": "no-cache",
28361             "X-Requested-With": "XMLHttpRequest"
28362         };
28363         
28364         for (var headerName in headers) {
28365             var headerValue = headers[headerName];
28366             if (headerValue) {
28367                 this.xhr.setRequestHeader(headerName, headerValue);
28368             }
28369         }
28370         
28371         var _this = this;
28372         
28373         this.xhr.onload = function()
28374         {
28375             _this.xhrOnLoad(_this.xhr);
28376         }
28377         
28378         this.xhr.onerror = function()
28379         {
28380             _this.xhrOnError(_this.xhr);
28381         }
28382         
28383         var formData = new FormData();
28384
28385         formData.append('returnHTML', 'NO');
28386         
28387         if(crop){
28388             formData.append('crop', crop);
28389         }
28390         
28391         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28392             formData.append(this.paramName, file, file.name);
28393         }
28394         
28395         if(typeof(file.filename) != 'undefined'){
28396             formData.append('filename', file.filename);
28397         }
28398         
28399         if(typeof(file.mimetype) != 'undefined'){
28400             formData.append('mimetype', file.mimetype);
28401         }
28402         
28403         if(this.fireEvent('arrange', this, formData) != false){
28404             this.xhr.send(formData);
28405         };
28406     },
28407     
28408     xhrOnLoad : function(xhr)
28409     {
28410         if(this.loadMask){
28411             this.maskEl.unmask();
28412         }
28413         
28414         if (xhr.readyState !== 4) {
28415             this.fireEvent('exception', this, xhr);
28416             return;
28417         }
28418
28419         var response = Roo.decode(xhr.responseText);
28420         
28421         if(!response.success){
28422             this.fireEvent('exception', this, xhr);
28423             return;
28424         }
28425         
28426         var response = Roo.decode(xhr.responseText);
28427         
28428         this.fireEvent('upload', this, response);
28429         
28430     },
28431     
28432     xhrOnError : function()
28433     {
28434         if(this.loadMask){
28435             this.maskEl.unmask();
28436         }
28437         
28438         Roo.log('xhr on error');
28439         
28440         var response = Roo.decode(xhr.responseText);
28441           
28442         Roo.log(response);
28443         
28444     },
28445     
28446     prepare : function(file)
28447     {   
28448         if(this.loadMask){
28449             this.maskEl.mask(this.loadingText);
28450         }
28451         
28452         this.file = false;
28453         this.exif = {};
28454         
28455         if(typeof(file) === 'string'){
28456             this.loadCanvas(file);
28457             return;
28458         }
28459         
28460         if(!file || !this.urlAPI){
28461             return;
28462         }
28463         
28464         this.file = file;
28465         this.cropType = file.type;
28466         
28467         var _this = this;
28468         
28469         if(this.fireEvent('prepare', this, this.file) != false){
28470             
28471             var reader = new FileReader();
28472             
28473             reader.onload = function (e) {
28474                 if (e.target.error) {
28475                     Roo.log(e.target.error);
28476                     return;
28477                 }
28478                 
28479                 var buffer = e.target.result,
28480                     dataView = new DataView(buffer),
28481                     offset = 2,
28482                     maxOffset = dataView.byteLength - 4,
28483                     markerBytes,
28484                     markerLength;
28485                 
28486                 if (dataView.getUint16(0) === 0xffd8) {
28487                     while (offset < maxOffset) {
28488                         markerBytes = dataView.getUint16(offset);
28489                         
28490                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28491                             markerLength = dataView.getUint16(offset + 2) + 2;
28492                             if (offset + markerLength > dataView.byteLength) {
28493                                 Roo.log('Invalid meta data: Invalid segment size.');
28494                                 break;
28495                             }
28496                             
28497                             if(markerBytes == 0xffe1){
28498                                 _this.parseExifData(
28499                                     dataView,
28500                                     offset,
28501                                     markerLength
28502                                 );
28503                             }
28504                             
28505                             offset += markerLength;
28506                             
28507                             continue;
28508                         }
28509                         
28510                         break;
28511                     }
28512                     
28513                 }
28514                 
28515                 var url = _this.urlAPI.createObjectURL(_this.file);
28516                 
28517                 _this.loadCanvas(url);
28518                 
28519                 return;
28520             }
28521             
28522             reader.readAsArrayBuffer(this.file);
28523             
28524         }
28525         
28526     },
28527     
28528     parseExifData : function(dataView, offset, length)
28529     {
28530         var tiffOffset = offset + 10,
28531             littleEndian,
28532             dirOffset;
28533     
28534         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28535             // No Exif data, might be XMP data instead
28536             return;
28537         }
28538         
28539         // Check for the ASCII code for "Exif" (0x45786966):
28540         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28541             // No Exif data, might be XMP data instead
28542             return;
28543         }
28544         if (tiffOffset + 8 > dataView.byteLength) {
28545             Roo.log('Invalid Exif data: Invalid segment size.');
28546             return;
28547         }
28548         // Check for the two null bytes:
28549         if (dataView.getUint16(offset + 8) !== 0x0000) {
28550             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28551             return;
28552         }
28553         // Check the byte alignment:
28554         switch (dataView.getUint16(tiffOffset)) {
28555         case 0x4949:
28556             littleEndian = true;
28557             break;
28558         case 0x4D4D:
28559             littleEndian = false;
28560             break;
28561         default:
28562             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28563             return;
28564         }
28565         // Check for the TIFF tag marker (0x002A):
28566         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28567             Roo.log('Invalid Exif data: Missing TIFF marker.');
28568             return;
28569         }
28570         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28571         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28572         
28573         this.parseExifTags(
28574             dataView,
28575             tiffOffset,
28576             tiffOffset + dirOffset,
28577             littleEndian
28578         );
28579     },
28580     
28581     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28582     {
28583         var tagsNumber,
28584             dirEndOffset,
28585             i;
28586         if (dirOffset + 6 > dataView.byteLength) {
28587             Roo.log('Invalid Exif data: Invalid directory offset.');
28588             return;
28589         }
28590         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28591         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28592         if (dirEndOffset + 4 > dataView.byteLength) {
28593             Roo.log('Invalid Exif data: Invalid directory size.');
28594             return;
28595         }
28596         for (i = 0; i < tagsNumber; i += 1) {
28597             this.parseExifTag(
28598                 dataView,
28599                 tiffOffset,
28600                 dirOffset + 2 + 12 * i, // tag offset
28601                 littleEndian
28602             );
28603         }
28604         // Return the offset to the next directory:
28605         return dataView.getUint32(dirEndOffset, littleEndian);
28606     },
28607     
28608     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28609     {
28610         var tag = dataView.getUint16(offset, littleEndian);
28611         
28612         this.exif[tag] = this.getExifValue(
28613             dataView,
28614             tiffOffset,
28615             offset,
28616             dataView.getUint16(offset + 2, littleEndian), // tag type
28617             dataView.getUint32(offset + 4, littleEndian), // tag length
28618             littleEndian
28619         );
28620     },
28621     
28622     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28623     {
28624         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28625             tagSize,
28626             dataOffset,
28627             values,
28628             i,
28629             str,
28630             c;
28631     
28632         if (!tagType) {
28633             Roo.log('Invalid Exif data: Invalid tag type.');
28634             return;
28635         }
28636         
28637         tagSize = tagType.size * length;
28638         // Determine if the value is contained in the dataOffset bytes,
28639         // or if the value at the dataOffset is a pointer to the actual data:
28640         dataOffset = tagSize > 4 ?
28641                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28642         if (dataOffset + tagSize > dataView.byteLength) {
28643             Roo.log('Invalid Exif data: Invalid data offset.');
28644             return;
28645         }
28646         if (length === 1) {
28647             return tagType.getValue(dataView, dataOffset, littleEndian);
28648         }
28649         values = [];
28650         for (i = 0; i < length; i += 1) {
28651             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28652         }
28653         
28654         if (tagType.ascii) {
28655             str = '';
28656             // Concatenate the chars:
28657             for (i = 0; i < values.length; i += 1) {
28658                 c = values[i];
28659                 // Ignore the terminating NULL byte(s):
28660                 if (c === '\u0000') {
28661                     break;
28662                 }
28663                 str += c;
28664             }
28665             return str;
28666         }
28667         return values;
28668     }
28669     
28670 });
28671
28672 Roo.apply(Roo.bootstrap.UploadCropbox, {
28673     tags : {
28674         'Orientation': 0x0112
28675     },
28676     
28677     Orientation: {
28678             1: 0, //'top-left',
28679 //            2: 'top-right',
28680             3: 180, //'bottom-right',
28681 //            4: 'bottom-left',
28682 //            5: 'left-top',
28683             6: 90, //'right-top',
28684 //            7: 'right-bottom',
28685             8: 270 //'left-bottom'
28686     },
28687     
28688     exifTagTypes : {
28689         // byte, 8-bit unsigned int:
28690         1: {
28691             getValue: function (dataView, dataOffset) {
28692                 return dataView.getUint8(dataOffset);
28693             },
28694             size: 1
28695         },
28696         // ascii, 8-bit byte:
28697         2: {
28698             getValue: function (dataView, dataOffset) {
28699                 return String.fromCharCode(dataView.getUint8(dataOffset));
28700             },
28701             size: 1,
28702             ascii: true
28703         },
28704         // short, 16 bit int:
28705         3: {
28706             getValue: function (dataView, dataOffset, littleEndian) {
28707                 return dataView.getUint16(dataOffset, littleEndian);
28708             },
28709             size: 2
28710         },
28711         // long, 32 bit int:
28712         4: {
28713             getValue: function (dataView, dataOffset, littleEndian) {
28714                 return dataView.getUint32(dataOffset, littleEndian);
28715             },
28716             size: 4
28717         },
28718         // rational = two long values, first is numerator, second is denominator:
28719         5: {
28720             getValue: function (dataView, dataOffset, littleEndian) {
28721                 return dataView.getUint32(dataOffset, littleEndian) /
28722                     dataView.getUint32(dataOffset + 4, littleEndian);
28723             },
28724             size: 8
28725         },
28726         // slong, 32 bit signed int:
28727         9: {
28728             getValue: function (dataView, dataOffset, littleEndian) {
28729                 return dataView.getInt32(dataOffset, littleEndian);
28730             },
28731             size: 4
28732         },
28733         // srational, two slongs, first is numerator, second is denominator:
28734         10: {
28735             getValue: function (dataView, dataOffset, littleEndian) {
28736                 return dataView.getInt32(dataOffset, littleEndian) /
28737                     dataView.getInt32(dataOffset + 4, littleEndian);
28738             },
28739             size: 8
28740         }
28741     },
28742     
28743     footer : {
28744         STANDARD : [
28745             {
28746                 tag : 'div',
28747                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28748                 action : 'rotate-left',
28749                 cn : [
28750                     {
28751                         tag : 'button',
28752                         cls : 'btn btn-default',
28753                         html : '<i class="fa fa-undo"></i>'
28754                     }
28755                 ]
28756             },
28757             {
28758                 tag : 'div',
28759                 cls : 'btn-group roo-upload-cropbox-picture',
28760                 action : 'picture',
28761                 cn : [
28762                     {
28763                         tag : 'button',
28764                         cls : 'btn btn-default',
28765                         html : '<i class="fa fa-picture-o"></i>'
28766                     }
28767                 ]
28768             },
28769             {
28770                 tag : 'div',
28771                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28772                 action : 'rotate-right',
28773                 cn : [
28774                     {
28775                         tag : 'button',
28776                         cls : 'btn btn-default',
28777                         html : '<i class="fa fa-repeat"></i>'
28778                     }
28779                 ]
28780             }
28781         ],
28782         DOCUMENT : [
28783             {
28784                 tag : 'div',
28785                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28786                 action : 'rotate-left',
28787                 cn : [
28788                     {
28789                         tag : 'button',
28790                         cls : 'btn btn-default',
28791                         html : '<i class="fa fa-undo"></i>'
28792                     }
28793                 ]
28794             },
28795             {
28796                 tag : 'div',
28797                 cls : 'btn-group roo-upload-cropbox-download',
28798                 action : 'download',
28799                 cn : [
28800                     {
28801                         tag : 'button',
28802                         cls : 'btn btn-default',
28803                         html : '<i class="fa fa-download"></i>'
28804                     }
28805                 ]
28806             },
28807             {
28808                 tag : 'div',
28809                 cls : 'btn-group roo-upload-cropbox-crop',
28810                 action : 'crop',
28811                 cn : [
28812                     {
28813                         tag : 'button',
28814                         cls : 'btn btn-default',
28815                         html : '<i class="fa fa-crop"></i>'
28816                     }
28817                 ]
28818             },
28819             {
28820                 tag : 'div',
28821                 cls : 'btn-group roo-upload-cropbox-trash',
28822                 action : 'trash',
28823                 cn : [
28824                     {
28825                         tag : 'button',
28826                         cls : 'btn btn-default',
28827                         html : '<i class="fa fa-trash"></i>'
28828                     }
28829                 ]
28830             },
28831             {
28832                 tag : 'div',
28833                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28834                 action : 'rotate-right',
28835                 cn : [
28836                     {
28837                         tag : 'button',
28838                         cls : 'btn btn-default',
28839                         html : '<i class="fa fa-repeat"></i>'
28840                     }
28841                 ]
28842             }
28843         ],
28844         ROTATOR : [
28845             {
28846                 tag : 'div',
28847                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28848                 action : 'rotate-left',
28849                 cn : [
28850                     {
28851                         tag : 'button',
28852                         cls : 'btn btn-default',
28853                         html : '<i class="fa fa-undo"></i>'
28854                     }
28855                 ]
28856             },
28857             {
28858                 tag : 'div',
28859                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28860                 action : 'rotate-right',
28861                 cn : [
28862                     {
28863                         tag : 'button',
28864                         cls : 'btn btn-default',
28865                         html : '<i class="fa fa-repeat"></i>'
28866                     }
28867                 ]
28868             }
28869         ]
28870     }
28871 });
28872
28873 /*
28874 * Licence: LGPL
28875 */
28876
28877 /**
28878  * @class Roo.bootstrap.DocumentManager
28879  * @extends Roo.bootstrap.Component
28880  * Bootstrap DocumentManager class
28881  * @cfg {String} paramName default 'imageUpload'
28882  * @cfg {String} toolTipName default 'filename'
28883  * @cfg {String} method default POST
28884  * @cfg {String} url action url
28885  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28886  * @cfg {Boolean} multiple multiple upload default true
28887  * @cfg {Number} thumbSize default 300
28888  * @cfg {String} fieldLabel
28889  * @cfg {Number} labelWidth default 4
28890  * @cfg {String} labelAlign (left|top) default left
28891  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28892 * @cfg {Number} labellg set the width of label (1-12)
28893  * @cfg {Number} labelmd set the width of label (1-12)
28894  * @cfg {Number} labelsm set the width of label (1-12)
28895  * @cfg {Number} labelxs set the width of label (1-12)
28896  * 
28897  * @constructor
28898  * Create a new DocumentManager
28899  * @param {Object} config The config object
28900  */
28901
28902 Roo.bootstrap.DocumentManager = function(config){
28903     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28904     
28905     this.files = [];
28906     this.delegates = [];
28907     
28908     this.addEvents({
28909         /**
28910          * @event initial
28911          * Fire when initial the DocumentManager
28912          * @param {Roo.bootstrap.DocumentManager} this
28913          */
28914         "initial" : true,
28915         /**
28916          * @event inspect
28917          * inspect selected file
28918          * @param {Roo.bootstrap.DocumentManager} this
28919          * @param {File} file
28920          */
28921         "inspect" : true,
28922         /**
28923          * @event exception
28924          * Fire when xhr load exception
28925          * @param {Roo.bootstrap.DocumentManager} this
28926          * @param {XMLHttpRequest} xhr
28927          */
28928         "exception" : true,
28929         /**
28930          * @event afterupload
28931          * Fire when xhr load exception
28932          * @param {Roo.bootstrap.DocumentManager} this
28933          * @param {XMLHttpRequest} xhr
28934          */
28935         "afterupload" : true,
28936         /**
28937          * @event prepare
28938          * prepare the form data
28939          * @param {Roo.bootstrap.DocumentManager} this
28940          * @param {Object} formData
28941          */
28942         "prepare" : true,
28943         /**
28944          * @event remove
28945          * Fire when remove the file
28946          * @param {Roo.bootstrap.DocumentManager} this
28947          * @param {Object} file
28948          */
28949         "remove" : true,
28950         /**
28951          * @event refresh
28952          * Fire after refresh the file
28953          * @param {Roo.bootstrap.DocumentManager} this
28954          */
28955         "refresh" : true,
28956         /**
28957          * @event click
28958          * Fire after click the image
28959          * @param {Roo.bootstrap.DocumentManager} this
28960          * @param {Object} file
28961          */
28962         "click" : true,
28963         /**
28964          * @event edit
28965          * Fire when upload a image and editable set to true
28966          * @param {Roo.bootstrap.DocumentManager} this
28967          * @param {Object} file
28968          */
28969         "edit" : true,
28970         /**
28971          * @event beforeselectfile
28972          * Fire before select file
28973          * @param {Roo.bootstrap.DocumentManager} this
28974          */
28975         "beforeselectfile" : true,
28976         /**
28977          * @event process
28978          * Fire before process file
28979          * @param {Roo.bootstrap.DocumentManager} this
28980          * @param {Object} file
28981          */
28982         "process" : true,
28983         /**
28984          * @event previewrendered
28985          * Fire when preview rendered
28986          * @param {Roo.bootstrap.DocumentManager} this
28987          * @param {Object} file
28988          */
28989         "previewrendered" : true,
28990         /**
28991          */
28992         "previewResize" : true
28993         
28994     });
28995 };
28996
28997 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28998     
28999     boxes : 0,
29000     inputName : '',
29001     thumbSize : 300,
29002     multiple : true,
29003     files : false,
29004     method : 'POST',
29005     url : '',
29006     paramName : 'imageUpload',
29007     toolTipName : 'filename',
29008     fieldLabel : '',
29009     labelWidth : 4,
29010     labelAlign : 'left',
29011     editable : true,
29012     delegates : false,
29013     xhr : false, 
29014     
29015     labellg : 0,
29016     labelmd : 0,
29017     labelsm : 0,
29018     labelxs : 0,
29019     
29020     getAutoCreate : function()
29021     {   
29022         var managerWidget = {
29023             tag : 'div',
29024             cls : 'roo-document-manager',
29025             cn : [
29026                 {
29027                     tag : 'input',
29028                     cls : 'roo-document-manager-selector',
29029                     type : 'file'
29030                 },
29031                 {
29032                     tag : 'div',
29033                     cls : 'roo-document-manager-uploader',
29034                     cn : [
29035                         {
29036                             tag : 'div',
29037                             cls : 'roo-document-manager-upload-btn',
29038                             html : '<i class="fa fa-plus"></i>'
29039                         }
29040                     ]
29041                     
29042                 }
29043             ]
29044         };
29045         
29046         var content = [
29047             {
29048                 tag : 'div',
29049                 cls : 'column col-md-12',
29050                 cn : managerWidget
29051             }
29052         ];
29053         
29054         if(this.fieldLabel.length){
29055             
29056             content = [
29057                 {
29058                     tag : 'div',
29059                     cls : 'column col-md-12',
29060                     html : this.fieldLabel
29061                 },
29062                 {
29063                     tag : 'div',
29064                     cls : 'column col-md-12',
29065                     cn : managerWidget
29066                 }
29067             ];
29068
29069             if(this.labelAlign == 'left'){
29070                 content = [
29071                     {
29072                         tag : 'div',
29073                         cls : 'column',
29074                         html : this.fieldLabel
29075                     },
29076                     {
29077                         tag : 'div',
29078                         cls : 'column',
29079                         cn : managerWidget
29080                     }
29081                 ];
29082                 
29083                 if(this.labelWidth > 12){
29084                     content[0].style = "width: " + this.labelWidth + 'px';
29085                 }
29086
29087                 if(this.labelWidth < 13 && this.labelmd == 0){
29088                     this.labelmd = this.labelWidth;
29089                 }
29090
29091                 if(this.labellg > 0){
29092                     content[0].cls += ' col-lg-' + this.labellg;
29093                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29094                 }
29095
29096                 if(this.labelmd > 0){
29097                     content[0].cls += ' col-md-' + this.labelmd;
29098                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29099                 }
29100
29101                 if(this.labelsm > 0){
29102                     content[0].cls += ' col-sm-' + this.labelsm;
29103                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29104                 }
29105
29106                 if(this.labelxs > 0){
29107                     content[0].cls += ' col-xs-' + this.labelxs;
29108                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29109                 }
29110                 
29111             }
29112         }
29113         
29114         var cfg = {
29115             tag : 'div',
29116             cls : 'row clearfix',
29117             cn : content
29118         };
29119         
29120         return cfg;
29121         
29122     },
29123     
29124     initEvents : function()
29125     {
29126         this.managerEl = this.el.select('.roo-document-manager', true).first();
29127         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29128         
29129         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29130         this.selectorEl.hide();
29131         
29132         if(this.multiple){
29133             this.selectorEl.attr('multiple', 'multiple');
29134         }
29135         
29136         this.selectorEl.on('change', this.onFileSelected, this);
29137         
29138         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29139         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29140         
29141         this.uploader.on('click', this.onUploaderClick, this);
29142         
29143         this.renderProgressDialog();
29144         
29145         var _this = this;
29146         
29147         window.addEventListener("resize", function() { _this.refresh(); } );
29148         
29149         this.fireEvent('initial', this);
29150     },
29151     
29152     renderProgressDialog : function()
29153     {
29154         var _this = this;
29155         
29156         this.progressDialog = new Roo.bootstrap.Modal({
29157             cls : 'roo-document-manager-progress-dialog',
29158             allow_close : false,
29159             title : '',
29160             buttons : [
29161                 {
29162                     name  :'cancel',
29163                     weight : 'danger',
29164                     html : 'Cancel'
29165                 }
29166             ], 
29167             listeners : { 
29168                 btnclick : function() {
29169                     _this.uploadCancel();
29170                     this.hide();
29171                 }
29172             }
29173         });
29174          
29175         this.progressDialog.render(Roo.get(document.body));
29176          
29177         this.progress = new Roo.bootstrap.Progress({
29178             cls : 'roo-document-manager-progress',
29179             active : true,
29180             striped : true
29181         });
29182         
29183         this.progress.render(this.progressDialog.getChildContainer());
29184         
29185         this.progressBar = new Roo.bootstrap.ProgressBar({
29186             cls : 'roo-document-manager-progress-bar',
29187             aria_valuenow : 0,
29188             aria_valuemin : 0,
29189             aria_valuemax : 12,
29190             panel : 'success'
29191         });
29192         
29193         this.progressBar.render(this.progress.getChildContainer());
29194     },
29195     
29196     onUploaderClick : function(e)
29197     {
29198         e.preventDefault();
29199      
29200         if(this.fireEvent('beforeselectfile', this) != false){
29201             this.selectorEl.dom.click();
29202         }
29203         
29204     },
29205     
29206     onFileSelected : function(e)
29207     {
29208         e.preventDefault();
29209         
29210         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29211             return;
29212         }
29213         
29214         Roo.each(this.selectorEl.dom.files, function(file){
29215             if(this.fireEvent('inspect', this, file) != false){
29216                 this.files.push(file);
29217             }
29218         }, this);
29219         
29220         this.queue();
29221         
29222     },
29223     
29224     queue : function()
29225     {
29226         this.selectorEl.dom.value = '';
29227         
29228         if(!this.files || !this.files.length){
29229             return;
29230         }
29231         
29232         if(this.boxes > 0 && this.files.length > this.boxes){
29233             this.files = this.files.slice(0, this.boxes);
29234         }
29235         
29236         this.uploader.show();
29237         
29238         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29239             this.uploader.hide();
29240         }
29241         
29242         var _this = this;
29243         
29244         var files = [];
29245         
29246         var docs = [];
29247         
29248         Roo.each(this.files, function(file){
29249             
29250             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29251                 var f = this.renderPreview(file);
29252                 files.push(f);
29253                 return;
29254             }
29255             
29256             if(file.type.indexOf('image') != -1){
29257                 this.delegates.push(
29258                     (function(){
29259                         _this.process(file);
29260                     }).createDelegate(this)
29261                 );
29262         
29263                 return;
29264             }
29265             
29266             docs.push(
29267                 (function(){
29268                     _this.process(file);
29269                 }).createDelegate(this)
29270             );
29271             
29272         }, this);
29273         
29274         this.files = files;
29275         
29276         this.delegates = this.delegates.concat(docs);
29277         
29278         if(!this.delegates.length){
29279             this.refresh();
29280             return;
29281         }
29282         
29283         this.progressBar.aria_valuemax = this.delegates.length;
29284         
29285         this.arrange();
29286         
29287         return;
29288     },
29289     
29290     arrange : function()
29291     {
29292         if(!this.delegates.length){
29293             this.progressDialog.hide();
29294             this.refresh();
29295             return;
29296         }
29297         
29298         var delegate = this.delegates.shift();
29299         
29300         this.progressDialog.show();
29301         
29302         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29303         
29304         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29305         
29306         delegate();
29307     },
29308     
29309     refresh : function()
29310     {
29311         this.uploader.show();
29312         
29313         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29314             this.uploader.hide();
29315         }
29316         
29317         Roo.isTouch ? this.closable(false) : this.closable(true);
29318         
29319         this.fireEvent('refresh', this);
29320     },
29321     
29322     onRemove : function(e, el, o)
29323     {
29324         e.preventDefault();
29325         
29326         this.fireEvent('remove', this, o);
29327         
29328     },
29329     
29330     remove : function(o)
29331     {
29332         var files = [];
29333         
29334         Roo.each(this.files, function(file){
29335             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29336                 files.push(file);
29337                 return;
29338             }
29339
29340             o.target.remove();
29341
29342         }, this);
29343         
29344         this.files = files;
29345         
29346         this.refresh();
29347     },
29348     
29349     clear : function()
29350     {
29351         Roo.each(this.files, function(file){
29352             if(!file.target){
29353                 return;
29354             }
29355             
29356             file.target.remove();
29357
29358         }, this);
29359         
29360         this.files = [];
29361         
29362         this.refresh();
29363     },
29364     
29365     onClick : function(e, el, o)
29366     {
29367         e.preventDefault();
29368         
29369         this.fireEvent('click', this, o);
29370         
29371     },
29372     
29373     closable : function(closable)
29374     {
29375         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29376             
29377             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29378             
29379             if(closable){
29380                 el.show();
29381                 return;
29382             }
29383             
29384             el.hide();
29385             
29386         }, this);
29387     },
29388     
29389     xhrOnLoad : function(xhr)
29390     {
29391         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29392             el.remove();
29393         }, this);
29394         
29395         if (xhr.readyState !== 4) {
29396             this.arrange();
29397             this.fireEvent('exception', this, xhr);
29398             return;
29399         }
29400
29401         var response = Roo.decode(xhr.responseText);
29402         
29403         if(!response.success){
29404             this.arrange();
29405             this.fireEvent('exception', this, xhr);
29406             return;
29407         }
29408         
29409         var file = this.renderPreview(response.data);
29410         
29411         this.files.push(file);
29412         
29413         this.arrange();
29414         
29415         this.fireEvent('afterupload', this, xhr);
29416         
29417     },
29418     
29419     xhrOnError : function(xhr)
29420     {
29421         Roo.log('xhr on error');
29422         
29423         var response = Roo.decode(xhr.responseText);
29424           
29425         Roo.log(response);
29426         
29427         this.arrange();
29428     },
29429     
29430     process : function(file)
29431     {
29432         if(this.fireEvent('process', this, file) !== false){
29433             if(this.editable && file.type.indexOf('image') != -1){
29434                 this.fireEvent('edit', this, file);
29435                 return;
29436             }
29437
29438             this.uploadStart(file, false);
29439
29440             return;
29441         }
29442         
29443     },
29444     
29445     uploadStart : function(file, crop)
29446     {
29447         this.xhr = new XMLHttpRequest();
29448         
29449         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29450             this.arrange();
29451             return;
29452         }
29453         
29454         file.xhr = this.xhr;
29455             
29456         this.managerEl.createChild({
29457             tag : 'div',
29458             cls : 'roo-document-manager-loading',
29459             cn : [
29460                 {
29461                     tag : 'div',
29462                     tooltip : file.name,
29463                     cls : 'roo-document-manager-thumb',
29464                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29465                 }
29466             ]
29467
29468         });
29469
29470         this.xhr.open(this.method, this.url, true);
29471         
29472         var headers = {
29473             "Accept": "application/json",
29474             "Cache-Control": "no-cache",
29475             "X-Requested-With": "XMLHttpRequest"
29476         };
29477         
29478         for (var headerName in headers) {
29479             var headerValue = headers[headerName];
29480             if (headerValue) {
29481                 this.xhr.setRequestHeader(headerName, headerValue);
29482             }
29483         }
29484         
29485         var _this = this;
29486         
29487         this.xhr.onload = function()
29488         {
29489             _this.xhrOnLoad(_this.xhr);
29490         }
29491         
29492         this.xhr.onerror = function()
29493         {
29494             _this.xhrOnError(_this.xhr);
29495         }
29496         
29497         var formData = new FormData();
29498
29499         formData.append('returnHTML', 'NO');
29500         
29501         if(crop){
29502             formData.append('crop', crop);
29503         }
29504         
29505         formData.append(this.paramName, file, file.name);
29506         
29507         var options = {
29508             file : file, 
29509             manually : false
29510         };
29511         
29512         if(this.fireEvent('prepare', this, formData, options) != false){
29513             
29514             if(options.manually){
29515                 return;
29516             }
29517             
29518             this.xhr.send(formData);
29519             return;
29520         };
29521         
29522         this.uploadCancel();
29523     },
29524     
29525     uploadCancel : function()
29526     {
29527         if (this.xhr) {
29528             this.xhr.abort();
29529         }
29530         
29531         this.delegates = [];
29532         
29533         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29534             el.remove();
29535         }, this);
29536         
29537         this.arrange();
29538     },
29539     
29540     renderPreview : function(file)
29541     {
29542         if(typeof(file.target) != 'undefined' && file.target){
29543             return file;
29544         }
29545         
29546         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29547         
29548         var previewEl = this.managerEl.createChild({
29549             tag : 'div',
29550             cls : 'roo-document-manager-preview',
29551             cn : [
29552                 {
29553                     tag : 'div',
29554                     tooltip : file[this.toolTipName],
29555                     cls : 'roo-document-manager-thumb',
29556                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29557                 },
29558                 {
29559                     tag : 'button',
29560                     cls : 'close',
29561                     html : '<i class="fa fa-times-circle"></i>'
29562                 }
29563             ]
29564         });
29565
29566         var close = previewEl.select('button.close', true).first();
29567
29568         close.on('click', this.onRemove, this, file);
29569
29570         file.target = previewEl;
29571
29572         var image = previewEl.select('img', true).first();
29573         
29574         var _this = this;
29575         
29576         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29577         
29578         image.on('click', this.onClick, this, file);
29579         
29580         this.fireEvent('previewrendered', this, file);
29581         
29582         return file;
29583         
29584     },
29585     
29586     onPreviewLoad : function(file, image)
29587     {
29588         if(typeof(file.target) == 'undefined' || !file.target){
29589             return;
29590         }
29591         
29592         var width = image.dom.naturalWidth || image.dom.width;
29593         var height = image.dom.naturalHeight || image.dom.height;
29594         
29595         if(!this.previewResize) {
29596             return;
29597         }
29598         
29599         if(width > height){
29600             file.target.addClass('wide');
29601             return;
29602         }
29603         
29604         file.target.addClass('tall');
29605         return;
29606         
29607     },
29608     
29609     uploadFromSource : function(file, crop)
29610     {
29611         this.xhr = new XMLHttpRequest();
29612         
29613         this.managerEl.createChild({
29614             tag : 'div',
29615             cls : 'roo-document-manager-loading',
29616             cn : [
29617                 {
29618                     tag : 'div',
29619                     tooltip : file.name,
29620                     cls : 'roo-document-manager-thumb',
29621                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29622                 }
29623             ]
29624
29625         });
29626
29627         this.xhr.open(this.method, this.url, true);
29628         
29629         var headers = {
29630             "Accept": "application/json",
29631             "Cache-Control": "no-cache",
29632             "X-Requested-With": "XMLHttpRequest"
29633         };
29634         
29635         for (var headerName in headers) {
29636             var headerValue = headers[headerName];
29637             if (headerValue) {
29638                 this.xhr.setRequestHeader(headerName, headerValue);
29639             }
29640         }
29641         
29642         var _this = this;
29643         
29644         this.xhr.onload = function()
29645         {
29646             _this.xhrOnLoad(_this.xhr);
29647         }
29648         
29649         this.xhr.onerror = function()
29650         {
29651             _this.xhrOnError(_this.xhr);
29652         }
29653         
29654         var formData = new FormData();
29655
29656         formData.append('returnHTML', 'NO');
29657         
29658         formData.append('crop', crop);
29659         
29660         if(typeof(file.filename) != 'undefined'){
29661             formData.append('filename', file.filename);
29662         }
29663         
29664         if(typeof(file.mimetype) != 'undefined'){
29665             formData.append('mimetype', file.mimetype);
29666         }
29667         
29668         Roo.log(formData);
29669         
29670         if(this.fireEvent('prepare', this, formData) != false){
29671             this.xhr.send(formData);
29672         };
29673     }
29674 });
29675
29676 /*
29677 * Licence: LGPL
29678 */
29679
29680 /**
29681  * @class Roo.bootstrap.DocumentViewer
29682  * @extends Roo.bootstrap.Component
29683  * Bootstrap DocumentViewer class
29684  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29685  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29686  * 
29687  * @constructor
29688  * Create a new DocumentViewer
29689  * @param {Object} config The config object
29690  */
29691
29692 Roo.bootstrap.DocumentViewer = function(config){
29693     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29694     
29695     this.addEvents({
29696         /**
29697          * @event initial
29698          * Fire after initEvent
29699          * @param {Roo.bootstrap.DocumentViewer} this
29700          */
29701         "initial" : true,
29702         /**
29703          * @event click
29704          * Fire after click
29705          * @param {Roo.bootstrap.DocumentViewer} this
29706          */
29707         "click" : true,
29708         /**
29709          * @event download
29710          * Fire after download button
29711          * @param {Roo.bootstrap.DocumentViewer} this
29712          */
29713         "download" : true,
29714         /**
29715          * @event trash
29716          * Fire after trash button
29717          * @param {Roo.bootstrap.DocumentViewer} this
29718          */
29719         "trash" : true
29720         
29721     });
29722 };
29723
29724 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29725     
29726     showDownload : true,
29727     
29728     showTrash : true,
29729     
29730     getAutoCreate : function()
29731     {
29732         var cfg = {
29733             tag : 'div',
29734             cls : 'roo-document-viewer',
29735             cn : [
29736                 {
29737                     tag : 'div',
29738                     cls : 'roo-document-viewer-body',
29739                     cn : [
29740                         {
29741                             tag : 'div',
29742                             cls : 'roo-document-viewer-thumb',
29743                             cn : [
29744                                 {
29745                                     tag : 'img',
29746                                     cls : 'roo-document-viewer-image'
29747                                 }
29748                             ]
29749                         }
29750                     ]
29751                 },
29752                 {
29753                     tag : 'div',
29754                     cls : 'roo-document-viewer-footer',
29755                     cn : {
29756                         tag : 'div',
29757                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29758                         cn : [
29759                             {
29760                                 tag : 'div',
29761                                 cls : 'btn-group roo-document-viewer-download',
29762                                 cn : [
29763                                     {
29764                                         tag : 'button',
29765                                         cls : 'btn btn-default',
29766                                         html : '<i class="fa fa-download"></i>'
29767                                     }
29768                                 ]
29769                             },
29770                             {
29771                                 tag : 'div',
29772                                 cls : 'btn-group roo-document-viewer-trash',
29773                                 cn : [
29774                                     {
29775                                         tag : 'button',
29776                                         cls : 'btn btn-default',
29777                                         html : '<i class="fa fa-trash"></i>'
29778                                     }
29779                                 ]
29780                             }
29781                         ]
29782                     }
29783                 }
29784             ]
29785         };
29786         
29787         return cfg;
29788     },
29789     
29790     initEvents : function()
29791     {
29792         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29793         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29794         
29795         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29796         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29797         
29798         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29799         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29800         
29801         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29802         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29803         
29804         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29805         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29806         
29807         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29808         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29809         
29810         this.bodyEl.on('click', this.onClick, this);
29811         this.downloadBtn.on('click', this.onDownload, this);
29812         this.trashBtn.on('click', this.onTrash, this);
29813         
29814         this.downloadBtn.hide();
29815         this.trashBtn.hide();
29816         
29817         if(this.showDownload){
29818             this.downloadBtn.show();
29819         }
29820         
29821         if(this.showTrash){
29822             this.trashBtn.show();
29823         }
29824         
29825         if(!this.showDownload && !this.showTrash) {
29826             this.footerEl.hide();
29827         }
29828         
29829     },
29830     
29831     initial : function()
29832     {
29833         this.fireEvent('initial', this);
29834         
29835     },
29836     
29837     onClick : function(e)
29838     {
29839         e.preventDefault();
29840         
29841         this.fireEvent('click', this);
29842     },
29843     
29844     onDownload : function(e)
29845     {
29846         e.preventDefault();
29847         
29848         this.fireEvent('download', this);
29849     },
29850     
29851     onTrash : function(e)
29852     {
29853         e.preventDefault();
29854         
29855         this.fireEvent('trash', this);
29856     }
29857     
29858 });
29859 /*
29860  * - LGPL
29861  *
29862  * nav progress bar
29863  * 
29864  */
29865
29866 /**
29867  * @class Roo.bootstrap.NavProgressBar
29868  * @extends Roo.bootstrap.Component
29869  * Bootstrap NavProgressBar class
29870  * 
29871  * @constructor
29872  * Create a new nav progress bar
29873  * @param {Object} config The config object
29874  */
29875
29876 Roo.bootstrap.NavProgressBar = function(config){
29877     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29878
29879     this.bullets = this.bullets || [];
29880    
29881 //    Roo.bootstrap.NavProgressBar.register(this);
29882      this.addEvents({
29883         /**
29884              * @event changed
29885              * Fires when the active item changes
29886              * @param {Roo.bootstrap.NavProgressBar} this
29887              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29888              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29889          */
29890         'changed': true
29891      });
29892     
29893 };
29894
29895 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29896     
29897     bullets : [],
29898     barItems : [],
29899     
29900     getAutoCreate : function()
29901     {
29902         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29903         
29904         cfg = {
29905             tag : 'div',
29906             cls : 'roo-navigation-bar-group',
29907             cn : [
29908                 {
29909                     tag : 'div',
29910                     cls : 'roo-navigation-top-bar'
29911                 },
29912                 {
29913                     tag : 'div',
29914                     cls : 'roo-navigation-bullets-bar',
29915                     cn : [
29916                         {
29917                             tag : 'ul',
29918                             cls : 'roo-navigation-bar'
29919                         }
29920                     ]
29921                 },
29922                 
29923                 {
29924                     tag : 'div',
29925                     cls : 'roo-navigation-bottom-bar'
29926                 }
29927             ]
29928             
29929         };
29930         
29931         return cfg;
29932         
29933     },
29934     
29935     initEvents: function() 
29936     {
29937         
29938     },
29939     
29940     onRender : function(ct, position) 
29941     {
29942         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29943         
29944         if(this.bullets.length){
29945             Roo.each(this.bullets, function(b){
29946                this.addItem(b);
29947             }, this);
29948         }
29949         
29950         this.format();
29951         
29952     },
29953     
29954     addItem : function(cfg)
29955     {
29956         var item = new Roo.bootstrap.NavProgressItem(cfg);
29957         
29958         item.parentId = this.id;
29959         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29960         
29961         if(cfg.html){
29962             var top = new Roo.bootstrap.Element({
29963                 tag : 'div',
29964                 cls : 'roo-navigation-bar-text'
29965             });
29966             
29967             var bottom = new Roo.bootstrap.Element({
29968                 tag : 'div',
29969                 cls : 'roo-navigation-bar-text'
29970             });
29971             
29972             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29973             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29974             
29975             var topText = new Roo.bootstrap.Element({
29976                 tag : 'span',
29977                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29978             });
29979             
29980             var bottomText = new Roo.bootstrap.Element({
29981                 tag : 'span',
29982                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29983             });
29984             
29985             topText.onRender(top.el, null);
29986             bottomText.onRender(bottom.el, null);
29987             
29988             item.topEl = top;
29989             item.bottomEl = bottom;
29990         }
29991         
29992         this.barItems.push(item);
29993         
29994         return item;
29995     },
29996     
29997     getActive : function()
29998     {
29999         var active = false;
30000         
30001         Roo.each(this.barItems, function(v){
30002             
30003             if (!v.isActive()) {
30004                 return;
30005             }
30006             
30007             active = v;
30008             return false;
30009             
30010         });
30011         
30012         return active;
30013     },
30014     
30015     setActiveItem : function(item)
30016     {
30017         var prev = false;
30018         
30019         Roo.each(this.barItems, function(v){
30020             if (v.rid == item.rid) {
30021                 return ;
30022             }
30023             
30024             if (v.isActive()) {
30025                 v.setActive(false);
30026                 prev = v;
30027             }
30028         });
30029
30030         item.setActive(true);
30031         
30032         this.fireEvent('changed', this, item, prev);
30033     },
30034     
30035     getBarItem: function(rid)
30036     {
30037         var ret = false;
30038         
30039         Roo.each(this.barItems, function(e) {
30040             if (e.rid != rid) {
30041                 return;
30042             }
30043             
30044             ret =  e;
30045             return false;
30046         });
30047         
30048         return ret;
30049     },
30050     
30051     indexOfItem : function(item)
30052     {
30053         var index = false;
30054         
30055         Roo.each(this.barItems, function(v, i){
30056             
30057             if (v.rid != item.rid) {
30058                 return;
30059             }
30060             
30061             index = i;
30062             return false
30063         });
30064         
30065         return index;
30066     },
30067     
30068     setActiveNext : function()
30069     {
30070         var i = this.indexOfItem(this.getActive());
30071         
30072         if (i > this.barItems.length) {
30073             return;
30074         }
30075         
30076         this.setActiveItem(this.barItems[i+1]);
30077     },
30078     
30079     setActivePrev : function()
30080     {
30081         var i = this.indexOfItem(this.getActive());
30082         
30083         if (i  < 1) {
30084             return;
30085         }
30086         
30087         this.setActiveItem(this.barItems[i-1]);
30088     },
30089     
30090     format : function()
30091     {
30092         if(!this.barItems.length){
30093             return;
30094         }
30095      
30096         var width = 100 / this.barItems.length;
30097         
30098         Roo.each(this.barItems, function(i){
30099             i.el.setStyle('width', width + '%');
30100             i.topEl.el.setStyle('width', width + '%');
30101             i.bottomEl.el.setStyle('width', width + '%');
30102         }, this);
30103         
30104     }
30105     
30106 });
30107 /*
30108  * - LGPL
30109  *
30110  * Nav Progress Item
30111  * 
30112  */
30113
30114 /**
30115  * @class Roo.bootstrap.NavProgressItem
30116  * @extends Roo.bootstrap.Component
30117  * Bootstrap NavProgressItem class
30118  * @cfg {String} rid the reference id
30119  * @cfg {Boolean} active (true|false) Is item active default false
30120  * @cfg {Boolean} disabled (true|false) Is item active default false
30121  * @cfg {String} html
30122  * @cfg {String} position (top|bottom) text position default bottom
30123  * @cfg {String} icon show icon instead of number
30124  * 
30125  * @constructor
30126  * Create a new NavProgressItem
30127  * @param {Object} config The config object
30128  */
30129 Roo.bootstrap.NavProgressItem = function(config){
30130     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30131     this.addEvents({
30132         // raw events
30133         /**
30134          * @event click
30135          * The raw click event for the entire grid.
30136          * @param {Roo.bootstrap.NavProgressItem} this
30137          * @param {Roo.EventObject} e
30138          */
30139         "click" : true
30140     });
30141    
30142 };
30143
30144 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30145     
30146     rid : '',
30147     active : false,
30148     disabled : false,
30149     html : '',
30150     position : 'bottom',
30151     icon : false,
30152     
30153     getAutoCreate : function()
30154     {
30155         var iconCls = 'roo-navigation-bar-item-icon';
30156         
30157         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30158         
30159         var cfg = {
30160             tag: 'li',
30161             cls: 'roo-navigation-bar-item',
30162             cn : [
30163                 {
30164                     tag : 'i',
30165                     cls : iconCls
30166                 }
30167             ]
30168         };
30169         
30170         if(this.active){
30171             cfg.cls += ' active';
30172         }
30173         if(this.disabled){
30174             cfg.cls += ' disabled';
30175         }
30176         
30177         return cfg;
30178     },
30179     
30180     disable : function()
30181     {
30182         this.setDisabled(true);
30183     },
30184     
30185     enable : function()
30186     {
30187         this.setDisabled(false);
30188     },
30189     
30190     initEvents: function() 
30191     {
30192         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30193         
30194         this.iconEl.on('click', this.onClick, this);
30195     },
30196     
30197     onClick : function(e)
30198     {
30199         e.preventDefault();
30200         
30201         if(this.disabled){
30202             return;
30203         }
30204         
30205         if(this.fireEvent('click', this, e) === false){
30206             return;
30207         };
30208         
30209         this.parent().setActiveItem(this);
30210     },
30211     
30212     isActive: function () 
30213     {
30214         return this.active;
30215     },
30216     
30217     setActive : function(state)
30218     {
30219         if(this.active == state){
30220             return;
30221         }
30222         
30223         this.active = state;
30224         
30225         if (state) {
30226             this.el.addClass('active');
30227             return;
30228         }
30229         
30230         this.el.removeClass('active');
30231         
30232         return;
30233     },
30234     
30235     setDisabled : function(state)
30236     {
30237         if(this.disabled == state){
30238             return;
30239         }
30240         
30241         this.disabled = state;
30242         
30243         if (state) {
30244             this.el.addClass('disabled');
30245             return;
30246         }
30247         
30248         this.el.removeClass('disabled');
30249     },
30250     
30251     tooltipEl : function()
30252     {
30253         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30254     }
30255 });
30256  
30257
30258  /*
30259  * - LGPL
30260  *
30261  * FieldLabel
30262  * 
30263  */
30264
30265 /**
30266  * @class Roo.bootstrap.FieldLabel
30267  * @extends Roo.bootstrap.Component
30268  * Bootstrap FieldLabel class
30269  * @cfg {String} html contents of the element
30270  * @cfg {String} tag tag of the element default label
30271  * @cfg {String} cls class of the element
30272  * @cfg {String} target label target 
30273  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30274  * @cfg {String} invalidClass default "text-warning"
30275  * @cfg {String} validClass default "text-success"
30276  * @cfg {String} iconTooltip default "This field is required"
30277  * @cfg {String} indicatorpos (left|right) default left
30278  * 
30279  * @constructor
30280  * Create a new FieldLabel
30281  * @param {Object} config The config object
30282  */
30283
30284 Roo.bootstrap.FieldLabel = function(config){
30285     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30286     
30287     this.addEvents({
30288             /**
30289              * @event invalid
30290              * Fires after the field has been marked as invalid.
30291              * @param {Roo.form.FieldLabel} this
30292              * @param {String} msg The validation message
30293              */
30294             invalid : true,
30295             /**
30296              * @event valid
30297              * Fires after the field has been validated with no errors.
30298              * @param {Roo.form.FieldLabel} this
30299              */
30300             valid : true
30301         });
30302 };
30303
30304 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30305     
30306     tag: 'label',
30307     cls: '',
30308     html: '',
30309     target: '',
30310     allowBlank : true,
30311     invalidClass : 'has-warning',
30312     validClass : 'has-success',
30313     iconTooltip : 'This field is required',
30314     indicatorpos : 'left',
30315     
30316     getAutoCreate : function(){
30317         
30318         var cls = "";
30319         if (!this.allowBlank) {
30320             cls  = "visible";
30321         }
30322         
30323         var cfg = {
30324             tag : this.tag,
30325             cls : 'roo-bootstrap-field-label ' + this.cls,
30326             for : this.target,
30327             cn : [
30328                 {
30329                     tag : 'i',
30330                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30331                     tooltip : this.iconTooltip
30332                 },
30333                 {
30334                     tag : 'span',
30335                     html : this.html
30336                 }
30337             ] 
30338         };
30339         
30340         if(this.indicatorpos == 'right'){
30341             var cfg = {
30342                 tag : this.tag,
30343                 cls : 'roo-bootstrap-field-label ' + this.cls,
30344                 for : this.target,
30345                 cn : [
30346                     {
30347                         tag : 'span',
30348                         html : this.html
30349                     },
30350                     {
30351                         tag : 'i',
30352                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30353                         tooltip : this.iconTooltip
30354                     }
30355                 ] 
30356             };
30357         }
30358         
30359         return cfg;
30360     },
30361     
30362     initEvents: function() 
30363     {
30364         Roo.bootstrap.Element.superclass.initEvents.call(this);
30365         
30366         this.indicator = this.indicatorEl();
30367         
30368         if(this.indicator){
30369             this.indicator.removeClass('visible');
30370             this.indicator.addClass('invisible');
30371         }
30372         
30373         Roo.bootstrap.FieldLabel.register(this);
30374     },
30375     
30376     indicatorEl : function()
30377     {
30378         var indicator = this.el.select('i.roo-required-indicator',true).first();
30379         
30380         if(!indicator){
30381             return false;
30382         }
30383         
30384         return indicator;
30385         
30386     },
30387     
30388     /**
30389      * Mark this field as valid
30390      */
30391     markValid : function()
30392     {
30393         if(this.indicator){
30394             this.indicator.removeClass('visible');
30395             this.indicator.addClass('invisible');
30396         }
30397         
30398         this.el.removeClass(this.invalidClass);
30399         
30400         this.el.addClass(this.validClass);
30401         
30402         this.fireEvent('valid', this);
30403     },
30404     
30405     /**
30406      * Mark this field as invalid
30407      * @param {String} msg The validation message
30408      */
30409     markInvalid : function(msg)
30410     {
30411         if(this.indicator){
30412             this.indicator.removeClass('invisible');
30413             this.indicator.addClass('visible');
30414         }
30415         
30416         this.el.removeClass(this.validClass);
30417         
30418         this.el.addClass(this.invalidClass);
30419         
30420         this.fireEvent('invalid', this, msg);
30421     }
30422     
30423    
30424 });
30425
30426 Roo.apply(Roo.bootstrap.FieldLabel, {
30427     
30428     groups: {},
30429     
30430      /**
30431     * register a FieldLabel Group
30432     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30433     */
30434     register : function(label)
30435     {
30436         if(this.groups.hasOwnProperty(label.target)){
30437             return;
30438         }
30439      
30440         this.groups[label.target] = label;
30441         
30442     },
30443     /**
30444     * fetch a FieldLabel Group based on the target
30445     * @param {string} target
30446     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30447     */
30448     get: function(target) {
30449         if (typeof(this.groups[target]) == 'undefined') {
30450             return false;
30451         }
30452         
30453         return this.groups[target] ;
30454     }
30455 });
30456
30457  
30458
30459  /*
30460  * - LGPL
30461  *
30462  * page DateSplitField.
30463  * 
30464  */
30465
30466
30467 /**
30468  * @class Roo.bootstrap.DateSplitField
30469  * @extends Roo.bootstrap.Component
30470  * Bootstrap DateSplitField class
30471  * @cfg {string} fieldLabel - the label associated
30472  * @cfg {Number} labelWidth set the width of label (0-12)
30473  * @cfg {String} labelAlign (top|left)
30474  * @cfg {Boolean} dayAllowBlank (true|false) default false
30475  * @cfg {Boolean} monthAllowBlank (true|false) default false
30476  * @cfg {Boolean} yearAllowBlank (true|false) default false
30477  * @cfg {string} dayPlaceholder 
30478  * @cfg {string} monthPlaceholder
30479  * @cfg {string} yearPlaceholder
30480  * @cfg {string} dayFormat default 'd'
30481  * @cfg {string} monthFormat default 'm'
30482  * @cfg {string} yearFormat default 'Y'
30483  * @cfg {Number} labellg set the width of label (1-12)
30484  * @cfg {Number} labelmd set the width of label (1-12)
30485  * @cfg {Number} labelsm set the width of label (1-12)
30486  * @cfg {Number} labelxs set the width of label (1-12)
30487
30488  *     
30489  * @constructor
30490  * Create a new DateSplitField
30491  * @param {Object} config The config object
30492  */
30493
30494 Roo.bootstrap.DateSplitField = function(config){
30495     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30496     
30497     this.addEvents({
30498         // raw events
30499          /**
30500          * @event years
30501          * getting the data of years
30502          * @param {Roo.bootstrap.DateSplitField} this
30503          * @param {Object} years
30504          */
30505         "years" : true,
30506         /**
30507          * @event days
30508          * getting the data of days
30509          * @param {Roo.bootstrap.DateSplitField} this
30510          * @param {Object} days
30511          */
30512         "days" : true,
30513         /**
30514          * @event invalid
30515          * Fires after the field has been marked as invalid.
30516          * @param {Roo.form.Field} this
30517          * @param {String} msg The validation message
30518          */
30519         invalid : true,
30520        /**
30521          * @event valid
30522          * Fires after the field has been validated with no errors.
30523          * @param {Roo.form.Field} this
30524          */
30525         valid : true
30526     });
30527 };
30528
30529 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30530     
30531     fieldLabel : '',
30532     labelAlign : 'top',
30533     labelWidth : 3,
30534     dayAllowBlank : false,
30535     monthAllowBlank : false,
30536     yearAllowBlank : false,
30537     dayPlaceholder : '',
30538     monthPlaceholder : '',
30539     yearPlaceholder : '',
30540     dayFormat : 'd',
30541     monthFormat : 'm',
30542     yearFormat : 'Y',
30543     isFormField : true,
30544     labellg : 0,
30545     labelmd : 0,
30546     labelsm : 0,
30547     labelxs : 0,
30548     
30549     getAutoCreate : function()
30550     {
30551         var cfg = {
30552             tag : 'div',
30553             cls : 'row roo-date-split-field-group',
30554             cn : [
30555                 {
30556                     tag : 'input',
30557                     type : 'hidden',
30558                     cls : 'form-hidden-field roo-date-split-field-group-value',
30559                     name : this.name
30560                 }
30561             ]
30562         };
30563         
30564         var labelCls = 'col-md-12';
30565         var contentCls = 'col-md-4';
30566         
30567         if(this.fieldLabel){
30568             
30569             var label = {
30570                 tag : 'div',
30571                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30572                 cn : [
30573                     {
30574                         tag : 'label',
30575                         html : this.fieldLabel
30576                     }
30577                 ]
30578             };
30579             
30580             if(this.labelAlign == 'left'){
30581             
30582                 if(this.labelWidth > 12){
30583                     label.style = "width: " + this.labelWidth + 'px';
30584                 }
30585
30586                 if(this.labelWidth < 13 && this.labelmd == 0){
30587                     this.labelmd = this.labelWidth;
30588                 }
30589
30590                 if(this.labellg > 0){
30591                     labelCls = ' col-lg-' + this.labellg;
30592                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30593                 }
30594
30595                 if(this.labelmd > 0){
30596                     labelCls = ' col-md-' + this.labelmd;
30597                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30598                 }
30599
30600                 if(this.labelsm > 0){
30601                     labelCls = ' col-sm-' + this.labelsm;
30602                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30603                 }
30604
30605                 if(this.labelxs > 0){
30606                     labelCls = ' col-xs-' + this.labelxs;
30607                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30608                 }
30609             }
30610             
30611             label.cls += ' ' + labelCls;
30612             
30613             cfg.cn.push(label);
30614         }
30615         
30616         Roo.each(['day', 'month', 'year'], function(t){
30617             cfg.cn.push({
30618                 tag : 'div',
30619                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30620             });
30621         }, this);
30622         
30623         return cfg;
30624     },
30625     
30626     inputEl: function ()
30627     {
30628         return this.el.select('.roo-date-split-field-group-value', true).first();
30629     },
30630     
30631     onRender : function(ct, position) 
30632     {
30633         var _this = this;
30634         
30635         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30636         
30637         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30638         
30639         this.dayField = new Roo.bootstrap.ComboBox({
30640             allowBlank : this.dayAllowBlank,
30641             alwaysQuery : true,
30642             displayField : 'value',
30643             editable : false,
30644             fieldLabel : '',
30645             forceSelection : true,
30646             mode : 'local',
30647             placeholder : this.dayPlaceholder,
30648             selectOnFocus : true,
30649             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30650             triggerAction : 'all',
30651             typeAhead : true,
30652             valueField : 'value',
30653             store : new Roo.data.SimpleStore({
30654                 data : (function() {    
30655                     var days = [];
30656                     _this.fireEvent('days', _this, days);
30657                     return days;
30658                 })(),
30659                 fields : [ 'value' ]
30660             }),
30661             listeners : {
30662                 select : function (_self, record, index)
30663                 {
30664                     _this.setValue(_this.getValue());
30665                 }
30666             }
30667         });
30668
30669         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30670         
30671         this.monthField = new Roo.bootstrap.MonthField({
30672             after : '<i class=\"fa fa-calendar\"></i>',
30673             allowBlank : this.monthAllowBlank,
30674             placeholder : this.monthPlaceholder,
30675             readOnly : true,
30676             listeners : {
30677                 render : function (_self)
30678                 {
30679                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30680                         e.preventDefault();
30681                         _self.focus();
30682                     });
30683                 },
30684                 select : function (_self, oldvalue, newvalue)
30685                 {
30686                     _this.setValue(_this.getValue());
30687                 }
30688             }
30689         });
30690         
30691         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30692         
30693         this.yearField = new Roo.bootstrap.ComboBox({
30694             allowBlank : this.yearAllowBlank,
30695             alwaysQuery : true,
30696             displayField : 'value',
30697             editable : false,
30698             fieldLabel : '',
30699             forceSelection : true,
30700             mode : 'local',
30701             placeholder : this.yearPlaceholder,
30702             selectOnFocus : true,
30703             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30704             triggerAction : 'all',
30705             typeAhead : true,
30706             valueField : 'value',
30707             store : new Roo.data.SimpleStore({
30708                 data : (function() {
30709                     var years = [];
30710                     _this.fireEvent('years', _this, years);
30711                     return years;
30712                 })(),
30713                 fields : [ 'value' ]
30714             }),
30715             listeners : {
30716                 select : function (_self, record, index)
30717                 {
30718                     _this.setValue(_this.getValue());
30719                 }
30720             }
30721         });
30722
30723         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30724     },
30725     
30726     setValue : function(v, format)
30727     {
30728         this.inputEl.dom.value = v;
30729         
30730         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30731         
30732         var d = Date.parseDate(v, f);
30733         
30734         if(!d){
30735             this.validate();
30736             return;
30737         }
30738         
30739         this.setDay(d.format(this.dayFormat));
30740         this.setMonth(d.format(this.monthFormat));
30741         this.setYear(d.format(this.yearFormat));
30742         
30743         this.validate();
30744         
30745         return;
30746     },
30747     
30748     setDay : function(v)
30749     {
30750         this.dayField.setValue(v);
30751         this.inputEl.dom.value = this.getValue();
30752         this.validate();
30753         return;
30754     },
30755     
30756     setMonth : function(v)
30757     {
30758         this.monthField.setValue(v, true);
30759         this.inputEl.dom.value = this.getValue();
30760         this.validate();
30761         return;
30762     },
30763     
30764     setYear : function(v)
30765     {
30766         this.yearField.setValue(v);
30767         this.inputEl.dom.value = this.getValue();
30768         this.validate();
30769         return;
30770     },
30771     
30772     getDay : function()
30773     {
30774         return this.dayField.getValue();
30775     },
30776     
30777     getMonth : function()
30778     {
30779         return this.monthField.getValue();
30780     },
30781     
30782     getYear : function()
30783     {
30784         return this.yearField.getValue();
30785     },
30786     
30787     getValue : function()
30788     {
30789         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30790         
30791         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30792         
30793         return date;
30794     },
30795     
30796     reset : function()
30797     {
30798         this.setDay('');
30799         this.setMonth('');
30800         this.setYear('');
30801         this.inputEl.dom.value = '';
30802         this.validate();
30803         return;
30804     },
30805     
30806     validate : function()
30807     {
30808         var d = this.dayField.validate();
30809         var m = this.monthField.validate();
30810         var y = this.yearField.validate();
30811         
30812         var valid = true;
30813         
30814         if(
30815                 (!this.dayAllowBlank && !d) ||
30816                 (!this.monthAllowBlank && !m) ||
30817                 (!this.yearAllowBlank && !y)
30818         ){
30819             valid = false;
30820         }
30821         
30822         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30823             return valid;
30824         }
30825         
30826         if(valid){
30827             this.markValid();
30828             return valid;
30829         }
30830         
30831         this.markInvalid();
30832         
30833         return valid;
30834     },
30835     
30836     markValid : function()
30837     {
30838         
30839         var label = this.el.select('label', true).first();
30840         var icon = this.el.select('i.fa-star', true).first();
30841
30842         if(label && icon){
30843             icon.remove();
30844         }
30845         
30846         this.fireEvent('valid', this);
30847     },
30848     
30849      /**
30850      * Mark this field as invalid
30851      * @param {String} msg The validation message
30852      */
30853     markInvalid : function(msg)
30854     {
30855         
30856         var label = this.el.select('label', true).first();
30857         var icon = this.el.select('i.fa-star', true).first();
30858
30859         if(label && !icon){
30860             this.el.select('.roo-date-split-field-label', true).createChild({
30861                 tag : 'i',
30862                 cls : 'text-danger fa fa-lg fa-star',
30863                 tooltip : 'This field is required',
30864                 style : 'margin-right:5px;'
30865             }, label, true);
30866         }
30867         
30868         this.fireEvent('invalid', this, msg);
30869     },
30870     
30871     clearInvalid : function()
30872     {
30873         var label = this.el.select('label', true).first();
30874         var icon = this.el.select('i.fa-star', true).first();
30875
30876         if(label && icon){
30877             icon.remove();
30878         }
30879         
30880         this.fireEvent('valid', this);
30881     },
30882     
30883     getName: function()
30884     {
30885         return this.name;
30886     }
30887     
30888 });
30889
30890  /**
30891  *
30892  * This is based on 
30893  * http://masonry.desandro.com
30894  *
30895  * The idea is to render all the bricks based on vertical width...
30896  *
30897  * The original code extends 'outlayer' - we might need to use that....
30898  * 
30899  */
30900
30901
30902 /**
30903  * @class Roo.bootstrap.LayoutMasonry
30904  * @extends Roo.bootstrap.Component
30905  * Bootstrap Layout Masonry class
30906  * 
30907  * @constructor
30908  * Create a new Element
30909  * @param {Object} config The config object
30910  */
30911
30912 Roo.bootstrap.LayoutMasonry = function(config){
30913     
30914     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30915     
30916     this.bricks = [];
30917     
30918     Roo.bootstrap.LayoutMasonry.register(this);
30919     
30920     this.addEvents({
30921         // raw events
30922         /**
30923          * @event layout
30924          * Fire after layout the items
30925          * @param {Roo.bootstrap.LayoutMasonry} this
30926          * @param {Roo.EventObject} e
30927          */
30928         "layout" : true
30929     });
30930     
30931 };
30932
30933 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30934     
30935     /**
30936      * @cfg {Boolean} isLayoutInstant = no animation?
30937      */   
30938     isLayoutInstant : false, // needed?
30939    
30940     /**
30941      * @cfg {Number} boxWidth  width of the columns
30942      */   
30943     boxWidth : 450,
30944     
30945       /**
30946      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30947      */   
30948     boxHeight : 0,
30949     
30950     /**
30951      * @cfg {Number} padWidth padding below box..
30952      */   
30953     padWidth : 10, 
30954     
30955     /**
30956      * @cfg {Number} gutter gutter width..
30957      */   
30958     gutter : 10,
30959     
30960      /**
30961      * @cfg {Number} maxCols maximum number of columns
30962      */   
30963     
30964     maxCols: 0,
30965     
30966     /**
30967      * @cfg {Boolean} isAutoInitial defalut true
30968      */   
30969     isAutoInitial : true, 
30970     
30971     containerWidth: 0,
30972     
30973     /**
30974      * @cfg {Boolean} isHorizontal defalut false
30975      */   
30976     isHorizontal : false, 
30977
30978     currentSize : null,
30979     
30980     tag: 'div',
30981     
30982     cls: '',
30983     
30984     bricks: null, //CompositeElement
30985     
30986     cols : 1,
30987     
30988     _isLayoutInited : false,
30989     
30990 //    isAlternative : false, // only use for vertical layout...
30991     
30992     /**
30993      * @cfg {Number} alternativePadWidth padding below box..
30994      */   
30995     alternativePadWidth : 50,
30996     
30997     selectedBrick : [],
30998     
30999     getAutoCreate : function(){
31000         
31001         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31002         
31003         var cfg = {
31004             tag: this.tag,
31005             cls: 'blog-masonary-wrapper ' + this.cls,
31006             cn : {
31007                 cls : 'mas-boxes masonary'
31008             }
31009         };
31010         
31011         return cfg;
31012     },
31013     
31014     getChildContainer: function( )
31015     {
31016         if (this.boxesEl) {
31017             return this.boxesEl;
31018         }
31019         
31020         this.boxesEl = this.el.select('.mas-boxes').first();
31021         
31022         return this.boxesEl;
31023     },
31024     
31025     
31026     initEvents : function()
31027     {
31028         var _this = this;
31029         
31030         if(this.isAutoInitial){
31031             Roo.log('hook children rendered');
31032             this.on('childrenrendered', function() {
31033                 Roo.log('children rendered');
31034                 _this.initial();
31035             } ,this);
31036         }
31037     },
31038     
31039     initial : function()
31040     {
31041         this.selectedBrick = [];
31042         
31043         this.currentSize = this.el.getBox(true);
31044         
31045         Roo.EventManager.onWindowResize(this.resize, this); 
31046
31047         if(!this.isAutoInitial){
31048             this.layout();
31049             return;
31050         }
31051         
31052         this.layout();
31053         
31054         return;
31055         //this.layout.defer(500,this);
31056         
31057     },
31058     
31059     resize : function()
31060     {
31061         var cs = this.el.getBox(true);
31062         
31063         if (
31064                 this.currentSize.width == cs.width && 
31065                 this.currentSize.x == cs.x && 
31066                 this.currentSize.height == cs.height && 
31067                 this.currentSize.y == cs.y 
31068         ) {
31069             Roo.log("no change in with or X or Y");
31070             return;
31071         }
31072         
31073         this.currentSize = cs;
31074         
31075         this.layout();
31076         
31077     },
31078     
31079     layout : function()
31080     {   
31081         this._resetLayout();
31082         
31083         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31084         
31085         this.layoutItems( isInstant );
31086       
31087         this._isLayoutInited = true;
31088         
31089         this.fireEvent('layout', this);
31090         
31091     },
31092     
31093     _resetLayout : function()
31094     {
31095         if(this.isHorizontal){
31096             this.horizontalMeasureColumns();
31097             return;
31098         }
31099         
31100         this.verticalMeasureColumns();
31101         
31102     },
31103     
31104     verticalMeasureColumns : function()
31105     {
31106         this.getContainerWidth();
31107         
31108 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31109 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31110 //            return;
31111 //        }
31112         
31113         var boxWidth = this.boxWidth + this.padWidth;
31114         
31115         if(this.containerWidth < this.boxWidth){
31116             boxWidth = this.containerWidth
31117         }
31118         
31119         var containerWidth = this.containerWidth;
31120         
31121         var cols = Math.floor(containerWidth / boxWidth);
31122         
31123         this.cols = Math.max( cols, 1 );
31124         
31125         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31126         
31127         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31128         
31129         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31130         
31131         this.colWidth = boxWidth + avail - this.padWidth;
31132         
31133         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31134         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31135     },
31136     
31137     horizontalMeasureColumns : function()
31138     {
31139         this.getContainerWidth();
31140         
31141         var boxWidth = this.boxWidth;
31142         
31143         if(this.containerWidth < boxWidth){
31144             boxWidth = this.containerWidth;
31145         }
31146         
31147         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31148         
31149         this.el.setHeight(boxWidth);
31150         
31151     },
31152     
31153     getContainerWidth : function()
31154     {
31155         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31156     },
31157     
31158     layoutItems : function( isInstant )
31159     {
31160         Roo.log(this.bricks);
31161         
31162         var items = Roo.apply([], this.bricks);
31163         
31164         if(this.isHorizontal){
31165             this._horizontalLayoutItems( items , isInstant );
31166             return;
31167         }
31168         
31169 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31170 //            this._verticalAlternativeLayoutItems( items , isInstant );
31171 //            return;
31172 //        }
31173         
31174         this._verticalLayoutItems( items , isInstant );
31175         
31176     },
31177     
31178     _verticalLayoutItems : function ( items , isInstant)
31179     {
31180         if ( !items || !items.length ) {
31181             return;
31182         }
31183         
31184         var standard = [
31185             ['xs', 'xs', 'xs', 'tall'],
31186             ['xs', 'xs', 'tall'],
31187             ['xs', 'xs', 'sm'],
31188             ['xs', 'xs', 'xs'],
31189             ['xs', 'tall'],
31190             ['xs', 'sm'],
31191             ['xs', 'xs'],
31192             ['xs'],
31193             
31194             ['sm', 'xs', 'xs'],
31195             ['sm', 'xs'],
31196             ['sm'],
31197             
31198             ['tall', 'xs', 'xs', 'xs'],
31199             ['tall', 'xs', 'xs'],
31200             ['tall', 'xs'],
31201             ['tall']
31202             
31203         ];
31204         
31205         var queue = [];
31206         
31207         var boxes = [];
31208         
31209         var box = [];
31210         
31211         Roo.each(items, function(item, k){
31212             
31213             switch (item.size) {
31214                 // these layouts take up a full box,
31215                 case 'md' :
31216                 case 'md-left' :
31217                 case 'md-right' :
31218                 case 'wide' :
31219                     
31220                     if(box.length){
31221                         boxes.push(box);
31222                         box = [];
31223                     }
31224                     
31225                     boxes.push([item]);
31226                     
31227                     break;
31228                     
31229                 case 'xs' :
31230                 case 'sm' :
31231                 case 'tall' :
31232                     
31233                     box.push(item);
31234                     
31235                     break;
31236                 default :
31237                     break;
31238                     
31239             }
31240             
31241         }, this);
31242         
31243         if(box.length){
31244             boxes.push(box);
31245             box = [];
31246         }
31247         
31248         var filterPattern = function(box, length)
31249         {
31250             if(!box.length){
31251                 return;
31252             }
31253             
31254             var match = false;
31255             
31256             var pattern = box.slice(0, length);
31257             
31258             var format = [];
31259             
31260             Roo.each(pattern, function(i){
31261                 format.push(i.size);
31262             }, this);
31263             
31264             Roo.each(standard, function(s){
31265                 
31266                 if(String(s) != String(format)){
31267                     return;
31268                 }
31269                 
31270                 match = true;
31271                 return false;
31272                 
31273             }, this);
31274             
31275             if(!match && length == 1){
31276                 return;
31277             }
31278             
31279             if(!match){
31280                 filterPattern(box, length - 1);
31281                 return;
31282             }
31283                 
31284             queue.push(pattern);
31285
31286             box = box.slice(length, box.length);
31287
31288             filterPattern(box, 4);
31289
31290             return;
31291             
31292         }
31293         
31294         Roo.each(boxes, function(box, k){
31295             
31296             if(!box.length){
31297                 return;
31298             }
31299             
31300             if(box.length == 1){
31301                 queue.push(box);
31302                 return;
31303             }
31304             
31305             filterPattern(box, 4);
31306             
31307         }, this);
31308         
31309         this._processVerticalLayoutQueue( queue, isInstant );
31310         
31311     },
31312     
31313 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31314 //    {
31315 //        if ( !items || !items.length ) {
31316 //            return;
31317 //        }
31318 //
31319 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31320 //        
31321 //    },
31322     
31323     _horizontalLayoutItems : function ( items , isInstant)
31324     {
31325         if ( !items || !items.length || items.length < 3) {
31326             return;
31327         }
31328         
31329         items.reverse();
31330         
31331         var eItems = items.slice(0, 3);
31332         
31333         items = items.slice(3, items.length);
31334         
31335         var standard = [
31336             ['xs', 'xs', 'xs', 'wide'],
31337             ['xs', 'xs', 'wide'],
31338             ['xs', 'xs', 'sm'],
31339             ['xs', 'xs', 'xs'],
31340             ['xs', 'wide'],
31341             ['xs', 'sm'],
31342             ['xs', 'xs'],
31343             ['xs'],
31344             
31345             ['sm', 'xs', 'xs'],
31346             ['sm', 'xs'],
31347             ['sm'],
31348             
31349             ['wide', 'xs', 'xs', 'xs'],
31350             ['wide', 'xs', 'xs'],
31351             ['wide', 'xs'],
31352             ['wide'],
31353             
31354             ['wide-thin']
31355         ];
31356         
31357         var queue = [];
31358         
31359         var boxes = [];
31360         
31361         var box = [];
31362         
31363         Roo.each(items, function(item, k){
31364             
31365             switch (item.size) {
31366                 case 'md' :
31367                 case 'md-left' :
31368                 case 'md-right' :
31369                 case 'tall' :
31370                     
31371                     if(box.length){
31372                         boxes.push(box);
31373                         box = [];
31374                     }
31375                     
31376                     boxes.push([item]);
31377                     
31378                     break;
31379                     
31380                 case 'xs' :
31381                 case 'sm' :
31382                 case 'wide' :
31383                 case 'wide-thin' :
31384                     
31385                     box.push(item);
31386                     
31387                     break;
31388                 default :
31389                     break;
31390                     
31391             }
31392             
31393         }, this);
31394         
31395         if(box.length){
31396             boxes.push(box);
31397             box = [];
31398         }
31399         
31400         var filterPattern = function(box, length)
31401         {
31402             if(!box.length){
31403                 return;
31404             }
31405             
31406             var match = false;
31407             
31408             var pattern = box.slice(0, length);
31409             
31410             var format = [];
31411             
31412             Roo.each(pattern, function(i){
31413                 format.push(i.size);
31414             }, this);
31415             
31416             Roo.each(standard, function(s){
31417                 
31418                 if(String(s) != String(format)){
31419                     return;
31420                 }
31421                 
31422                 match = true;
31423                 return false;
31424                 
31425             }, this);
31426             
31427             if(!match && length == 1){
31428                 return;
31429             }
31430             
31431             if(!match){
31432                 filterPattern(box, length - 1);
31433                 return;
31434             }
31435                 
31436             queue.push(pattern);
31437
31438             box = box.slice(length, box.length);
31439
31440             filterPattern(box, 4);
31441
31442             return;
31443             
31444         }
31445         
31446         Roo.each(boxes, function(box, k){
31447             
31448             if(!box.length){
31449                 return;
31450             }
31451             
31452             if(box.length == 1){
31453                 queue.push(box);
31454                 return;
31455             }
31456             
31457             filterPattern(box, 4);
31458             
31459         }, this);
31460         
31461         
31462         var prune = [];
31463         
31464         var pos = this.el.getBox(true);
31465         
31466         var minX = pos.x;
31467         
31468         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31469         
31470         var hit_end = false;
31471         
31472         Roo.each(queue, function(box){
31473             
31474             if(hit_end){
31475                 
31476                 Roo.each(box, function(b){
31477                 
31478                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31479                     b.el.hide();
31480
31481                 }, this);
31482
31483                 return;
31484             }
31485             
31486             var mx = 0;
31487             
31488             Roo.each(box, function(b){
31489                 
31490                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31491                 b.el.show();
31492
31493                 mx = Math.max(mx, b.x);
31494                 
31495             }, this);
31496             
31497             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31498             
31499             if(maxX < minX){
31500                 
31501                 Roo.each(box, function(b){
31502                 
31503                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31504                     b.el.hide();
31505                     
31506                 }, this);
31507                 
31508                 hit_end = true;
31509                 
31510                 return;
31511             }
31512             
31513             prune.push(box);
31514             
31515         }, this);
31516         
31517         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31518     },
31519     
31520     /** Sets position of item in DOM
31521     * @param {Element} item
31522     * @param {Number} x - horizontal position
31523     * @param {Number} y - vertical position
31524     * @param {Boolean} isInstant - disables transitions
31525     */
31526     _processVerticalLayoutQueue : function( queue, isInstant )
31527     {
31528         var pos = this.el.getBox(true);
31529         var x = pos.x;
31530         var y = pos.y;
31531         var maxY = [];
31532         
31533         for (var i = 0; i < this.cols; i++){
31534             maxY[i] = pos.y;
31535         }
31536         
31537         Roo.each(queue, function(box, k){
31538             
31539             var col = k % this.cols;
31540             
31541             Roo.each(box, function(b,kk){
31542                 
31543                 b.el.position('absolute');
31544                 
31545                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31546                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31547                 
31548                 if(b.size == 'md-left' || b.size == 'md-right'){
31549                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31550                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31551                 }
31552                 
31553                 b.el.setWidth(width);
31554                 b.el.setHeight(height);
31555                 // iframe?
31556                 b.el.select('iframe',true).setSize(width,height);
31557                 
31558             }, this);
31559             
31560             for (var i = 0; i < this.cols; i++){
31561                 
31562                 if(maxY[i] < maxY[col]){
31563                     col = i;
31564                     continue;
31565                 }
31566                 
31567                 col = Math.min(col, i);
31568                 
31569             }
31570             
31571             x = pos.x + col * (this.colWidth + this.padWidth);
31572             
31573             y = maxY[col];
31574             
31575             var positions = [];
31576             
31577             switch (box.length){
31578                 case 1 :
31579                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31580                     break;
31581                 case 2 :
31582                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31583                     break;
31584                 case 3 :
31585                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31586                     break;
31587                 case 4 :
31588                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31589                     break;
31590                 default :
31591                     break;
31592             }
31593             
31594             Roo.each(box, function(b,kk){
31595                 
31596                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31597                 
31598                 var sz = b.el.getSize();
31599                 
31600                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31601                 
31602             }, this);
31603             
31604         }, this);
31605         
31606         var mY = 0;
31607         
31608         for (var i = 0; i < this.cols; i++){
31609             mY = Math.max(mY, maxY[i]);
31610         }
31611         
31612         this.el.setHeight(mY - pos.y);
31613         
31614     },
31615     
31616 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31617 //    {
31618 //        var pos = this.el.getBox(true);
31619 //        var x = pos.x;
31620 //        var y = pos.y;
31621 //        var maxX = pos.right;
31622 //        
31623 //        var maxHeight = 0;
31624 //        
31625 //        Roo.each(items, function(item, k){
31626 //            
31627 //            var c = k % 2;
31628 //            
31629 //            item.el.position('absolute');
31630 //                
31631 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31632 //
31633 //            item.el.setWidth(width);
31634 //
31635 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31636 //
31637 //            item.el.setHeight(height);
31638 //            
31639 //            if(c == 0){
31640 //                item.el.setXY([x, y], isInstant ? false : true);
31641 //            } else {
31642 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31643 //            }
31644 //            
31645 //            y = y + height + this.alternativePadWidth;
31646 //            
31647 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31648 //            
31649 //        }, this);
31650 //        
31651 //        this.el.setHeight(maxHeight);
31652 //        
31653 //    },
31654     
31655     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31656     {
31657         var pos = this.el.getBox(true);
31658         
31659         var minX = pos.x;
31660         var minY = pos.y;
31661         
31662         var maxX = pos.right;
31663         
31664         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31665         
31666         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31667         
31668         Roo.each(queue, function(box, k){
31669             
31670             Roo.each(box, function(b, kk){
31671                 
31672                 b.el.position('absolute');
31673                 
31674                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31675                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31676                 
31677                 if(b.size == 'md-left' || b.size == 'md-right'){
31678                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31679                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31680                 }
31681                 
31682                 b.el.setWidth(width);
31683                 b.el.setHeight(height);
31684                 
31685             }, this);
31686             
31687             if(!box.length){
31688                 return;
31689             }
31690             
31691             var positions = [];
31692             
31693             switch (box.length){
31694                 case 1 :
31695                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31696                     break;
31697                 case 2 :
31698                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31699                     break;
31700                 case 3 :
31701                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31702                     break;
31703                 case 4 :
31704                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31705                     break;
31706                 default :
31707                     break;
31708             }
31709             
31710             Roo.each(box, function(b,kk){
31711                 
31712                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31713                 
31714                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31715                 
31716             }, this);
31717             
31718         }, this);
31719         
31720     },
31721     
31722     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31723     {
31724         Roo.each(eItems, function(b,k){
31725             
31726             b.size = (k == 0) ? 'sm' : 'xs';
31727             b.x = (k == 0) ? 2 : 1;
31728             b.y = (k == 0) ? 2 : 1;
31729             
31730             b.el.position('absolute');
31731             
31732             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31733                 
31734             b.el.setWidth(width);
31735             
31736             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31737             
31738             b.el.setHeight(height);
31739             
31740         }, this);
31741
31742         var positions = [];
31743         
31744         positions.push({
31745             x : maxX - this.unitWidth * 2 - this.gutter,
31746             y : minY
31747         });
31748         
31749         positions.push({
31750             x : maxX - this.unitWidth,
31751             y : minY + (this.unitWidth + this.gutter) * 2
31752         });
31753         
31754         positions.push({
31755             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31756             y : minY
31757         });
31758         
31759         Roo.each(eItems, function(b,k){
31760             
31761             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31762
31763         }, this);
31764         
31765     },
31766     
31767     getVerticalOneBoxColPositions : function(x, y, box)
31768     {
31769         var pos = [];
31770         
31771         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31772         
31773         if(box[0].size == 'md-left'){
31774             rand = 0;
31775         }
31776         
31777         if(box[0].size == 'md-right'){
31778             rand = 1;
31779         }
31780         
31781         pos.push({
31782             x : x + (this.unitWidth + this.gutter) * rand,
31783             y : y
31784         });
31785         
31786         return pos;
31787     },
31788     
31789     getVerticalTwoBoxColPositions : function(x, y, box)
31790     {
31791         var pos = [];
31792         
31793         if(box[0].size == 'xs'){
31794             
31795             pos.push({
31796                 x : x,
31797                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31798             });
31799
31800             pos.push({
31801                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31802                 y : y
31803             });
31804             
31805             return pos;
31806             
31807         }
31808         
31809         pos.push({
31810             x : x,
31811             y : y
31812         });
31813
31814         pos.push({
31815             x : x + (this.unitWidth + this.gutter) * 2,
31816             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31817         });
31818         
31819         return pos;
31820         
31821     },
31822     
31823     getVerticalThreeBoxColPositions : function(x, y, box)
31824     {
31825         var pos = [];
31826         
31827         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31828             
31829             pos.push({
31830                 x : x,
31831                 y : y
31832             });
31833
31834             pos.push({
31835                 x : x + (this.unitWidth + this.gutter) * 1,
31836                 y : y
31837             });
31838             
31839             pos.push({
31840                 x : x + (this.unitWidth + this.gutter) * 2,
31841                 y : y
31842             });
31843             
31844             return pos;
31845             
31846         }
31847         
31848         if(box[0].size == 'xs' && box[1].size == 'xs'){
31849             
31850             pos.push({
31851                 x : x,
31852                 y : y
31853             });
31854
31855             pos.push({
31856                 x : x,
31857                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31858             });
31859             
31860             pos.push({
31861                 x : x + (this.unitWidth + this.gutter) * 1,
31862                 y : y
31863             });
31864             
31865             return pos;
31866             
31867         }
31868         
31869         pos.push({
31870             x : x,
31871             y : y
31872         });
31873
31874         pos.push({
31875             x : x + (this.unitWidth + this.gutter) * 2,
31876             y : y
31877         });
31878
31879         pos.push({
31880             x : x + (this.unitWidth + this.gutter) * 2,
31881             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31882         });
31883             
31884         return pos;
31885         
31886     },
31887     
31888     getVerticalFourBoxColPositions : function(x, y, box)
31889     {
31890         var pos = [];
31891         
31892         if(box[0].size == 'xs'){
31893             
31894             pos.push({
31895                 x : x,
31896                 y : y
31897             });
31898
31899             pos.push({
31900                 x : x,
31901                 y : y + (this.unitHeight + this.gutter) * 1
31902             });
31903             
31904             pos.push({
31905                 x : x,
31906                 y : y + (this.unitHeight + this.gutter) * 2
31907             });
31908             
31909             pos.push({
31910                 x : x + (this.unitWidth + this.gutter) * 1,
31911                 y : y
31912             });
31913             
31914             return pos;
31915             
31916         }
31917         
31918         pos.push({
31919             x : x,
31920             y : y
31921         });
31922
31923         pos.push({
31924             x : x + (this.unitWidth + this.gutter) * 2,
31925             y : y
31926         });
31927
31928         pos.push({
31929             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31930             y : y + (this.unitHeight + this.gutter) * 1
31931         });
31932
31933         pos.push({
31934             x : x + (this.unitWidth + this.gutter) * 2,
31935             y : y + (this.unitWidth + this.gutter) * 2
31936         });
31937
31938         return pos;
31939         
31940     },
31941     
31942     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31943     {
31944         var pos = [];
31945         
31946         if(box[0].size == 'md-left'){
31947             pos.push({
31948                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31949                 y : minY
31950             });
31951             
31952             return pos;
31953         }
31954         
31955         if(box[0].size == 'md-right'){
31956             pos.push({
31957                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31958                 y : minY + (this.unitWidth + this.gutter) * 1
31959             });
31960             
31961             return pos;
31962         }
31963         
31964         var rand = Math.floor(Math.random() * (4 - box[0].y));
31965         
31966         pos.push({
31967             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31968             y : minY + (this.unitWidth + this.gutter) * rand
31969         });
31970         
31971         return pos;
31972         
31973     },
31974     
31975     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31976     {
31977         var pos = [];
31978         
31979         if(box[0].size == 'xs'){
31980             
31981             pos.push({
31982                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31983                 y : minY
31984             });
31985
31986             pos.push({
31987                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31988                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31989             });
31990             
31991             return pos;
31992             
31993         }
31994         
31995         pos.push({
31996             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31997             y : minY
31998         });
31999
32000         pos.push({
32001             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32002             y : minY + (this.unitWidth + this.gutter) * 2
32003         });
32004         
32005         return pos;
32006         
32007     },
32008     
32009     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32010     {
32011         var pos = [];
32012         
32013         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32014             
32015             pos.push({
32016                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32017                 y : minY
32018             });
32019
32020             pos.push({
32021                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32022                 y : minY + (this.unitWidth + this.gutter) * 1
32023             });
32024             
32025             pos.push({
32026                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32027                 y : minY + (this.unitWidth + this.gutter) * 2
32028             });
32029             
32030             return pos;
32031             
32032         }
32033         
32034         if(box[0].size == 'xs' && box[1].size == 'xs'){
32035             
32036             pos.push({
32037                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32038                 y : minY
32039             });
32040
32041             pos.push({
32042                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32043                 y : minY
32044             });
32045             
32046             pos.push({
32047                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32048                 y : minY + (this.unitWidth + this.gutter) * 1
32049             });
32050             
32051             return pos;
32052             
32053         }
32054         
32055         pos.push({
32056             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32057             y : minY
32058         });
32059
32060         pos.push({
32061             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32062             y : minY + (this.unitWidth + this.gutter) * 2
32063         });
32064
32065         pos.push({
32066             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32067             y : minY + (this.unitWidth + this.gutter) * 2
32068         });
32069             
32070         return pos;
32071         
32072     },
32073     
32074     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32075     {
32076         var pos = [];
32077         
32078         if(box[0].size == 'xs'){
32079             
32080             pos.push({
32081                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32082                 y : minY
32083             });
32084
32085             pos.push({
32086                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32087                 y : minY
32088             });
32089             
32090             pos.push({
32091                 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),
32092                 y : minY
32093             });
32094             
32095             pos.push({
32096                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32097                 y : minY + (this.unitWidth + this.gutter) * 1
32098             });
32099             
32100             return pos;
32101             
32102         }
32103         
32104         pos.push({
32105             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32106             y : minY
32107         });
32108         
32109         pos.push({
32110             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32111             y : minY + (this.unitWidth + this.gutter) * 2
32112         });
32113         
32114         pos.push({
32115             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32116             y : minY + (this.unitWidth + this.gutter) * 2
32117         });
32118         
32119         pos.push({
32120             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),
32121             y : minY + (this.unitWidth + this.gutter) * 2
32122         });
32123
32124         return pos;
32125         
32126     },
32127     
32128     /**
32129     * remove a Masonry Brick
32130     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32131     */
32132     removeBrick : function(brick_id)
32133     {
32134         if (!brick_id) {
32135             return;
32136         }
32137         
32138         for (var i = 0; i<this.bricks.length; i++) {
32139             if (this.bricks[i].id == brick_id) {
32140                 this.bricks.splice(i,1);
32141                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32142                 this.initial();
32143             }
32144         }
32145     },
32146     
32147     /**
32148     * adds a Masonry Brick
32149     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32150     */
32151     addBrick : function(cfg)
32152     {
32153         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32154         //this.register(cn);
32155         cn.parentId = this.id;
32156         cn.render(this.el);
32157         return cn;
32158     },
32159     
32160     /**
32161     * register a Masonry Brick
32162     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32163     */
32164     
32165     register : function(brick)
32166     {
32167         this.bricks.push(brick);
32168         brick.masonryId = this.id;
32169     },
32170     
32171     /**
32172     * clear all the Masonry Brick
32173     */
32174     clearAll : function()
32175     {
32176         this.bricks = [];
32177         //this.getChildContainer().dom.innerHTML = "";
32178         this.el.dom.innerHTML = '';
32179     },
32180     
32181     getSelected : function()
32182     {
32183         if (!this.selectedBrick) {
32184             return false;
32185         }
32186         
32187         return this.selectedBrick;
32188     }
32189 });
32190
32191 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32192     
32193     groups: {},
32194      /**
32195     * register a Masonry Layout
32196     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32197     */
32198     
32199     register : function(layout)
32200     {
32201         this.groups[layout.id] = layout;
32202     },
32203     /**
32204     * fetch a  Masonry Layout based on the masonry layout ID
32205     * @param {string} the masonry layout to add
32206     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32207     */
32208     
32209     get: function(layout_id) {
32210         if (typeof(this.groups[layout_id]) == 'undefined') {
32211             return false;
32212         }
32213         return this.groups[layout_id] ;
32214     }
32215     
32216     
32217     
32218 });
32219
32220  
32221
32222  /**
32223  *
32224  * This is based on 
32225  * http://masonry.desandro.com
32226  *
32227  * The idea is to render all the bricks based on vertical width...
32228  *
32229  * The original code extends 'outlayer' - we might need to use that....
32230  * 
32231  */
32232
32233
32234 /**
32235  * @class Roo.bootstrap.LayoutMasonryAuto
32236  * @extends Roo.bootstrap.Component
32237  * Bootstrap Layout Masonry class
32238  * 
32239  * @constructor
32240  * Create a new Element
32241  * @param {Object} config The config object
32242  */
32243
32244 Roo.bootstrap.LayoutMasonryAuto = function(config){
32245     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32246 };
32247
32248 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32249     
32250       /**
32251      * @cfg {Boolean} isFitWidth  - resize the width..
32252      */   
32253     isFitWidth : false,  // options..
32254     /**
32255      * @cfg {Boolean} isOriginLeft = left align?
32256      */   
32257     isOriginLeft : true,
32258     /**
32259      * @cfg {Boolean} isOriginTop = top align?
32260      */   
32261     isOriginTop : false,
32262     /**
32263      * @cfg {Boolean} isLayoutInstant = no animation?
32264      */   
32265     isLayoutInstant : false, // needed?
32266     /**
32267      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32268      */   
32269     isResizingContainer : true,
32270     /**
32271      * @cfg {Number} columnWidth  width of the columns 
32272      */   
32273     
32274     columnWidth : 0,
32275     
32276     /**
32277      * @cfg {Number} maxCols maximum number of columns
32278      */   
32279     
32280     maxCols: 0,
32281     /**
32282      * @cfg {Number} padHeight padding below box..
32283      */   
32284     
32285     padHeight : 10, 
32286     
32287     /**
32288      * @cfg {Boolean} isAutoInitial defalut true
32289      */   
32290     
32291     isAutoInitial : true, 
32292     
32293     // private?
32294     gutter : 0,
32295     
32296     containerWidth: 0,
32297     initialColumnWidth : 0,
32298     currentSize : null,
32299     
32300     colYs : null, // array.
32301     maxY : 0,
32302     padWidth: 10,
32303     
32304     
32305     tag: 'div',
32306     cls: '',
32307     bricks: null, //CompositeElement
32308     cols : 0, // array?
32309     // element : null, // wrapped now this.el
32310     _isLayoutInited : null, 
32311     
32312     
32313     getAutoCreate : function(){
32314         
32315         var cfg = {
32316             tag: this.tag,
32317             cls: 'blog-masonary-wrapper ' + this.cls,
32318             cn : {
32319                 cls : 'mas-boxes masonary'
32320             }
32321         };
32322         
32323         return cfg;
32324     },
32325     
32326     getChildContainer: function( )
32327     {
32328         if (this.boxesEl) {
32329             return this.boxesEl;
32330         }
32331         
32332         this.boxesEl = this.el.select('.mas-boxes').first();
32333         
32334         return this.boxesEl;
32335     },
32336     
32337     
32338     initEvents : function()
32339     {
32340         var _this = this;
32341         
32342         if(this.isAutoInitial){
32343             Roo.log('hook children rendered');
32344             this.on('childrenrendered', function() {
32345                 Roo.log('children rendered');
32346                 _this.initial();
32347             } ,this);
32348         }
32349         
32350     },
32351     
32352     initial : function()
32353     {
32354         this.reloadItems();
32355
32356         this.currentSize = this.el.getBox(true);
32357
32358         /// was window resize... - let's see if this works..
32359         Roo.EventManager.onWindowResize(this.resize, this); 
32360
32361         if(!this.isAutoInitial){
32362             this.layout();
32363             return;
32364         }
32365         
32366         this.layout.defer(500,this);
32367     },
32368     
32369     reloadItems: function()
32370     {
32371         this.bricks = this.el.select('.masonry-brick', true);
32372         
32373         this.bricks.each(function(b) {
32374             //Roo.log(b.getSize());
32375             if (!b.attr('originalwidth')) {
32376                 b.attr('originalwidth',  b.getSize().width);
32377             }
32378             
32379         });
32380         
32381         Roo.log(this.bricks.elements.length);
32382     },
32383     
32384     resize : function()
32385     {
32386         Roo.log('resize');
32387         var cs = this.el.getBox(true);
32388         
32389         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32390             Roo.log("no change in with or X");
32391             return;
32392         }
32393         this.currentSize = cs;
32394         this.layout();
32395     },
32396     
32397     layout : function()
32398     {
32399          Roo.log('layout');
32400         this._resetLayout();
32401         //this._manageStamps();
32402       
32403         // don't animate first layout
32404         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32405         this.layoutItems( isInstant );
32406       
32407         // flag for initalized
32408         this._isLayoutInited = true;
32409     },
32410     
32411     layoutItems : function( isInstant )
32412     {
32413         //var items = this._getItemsForLayout( this.items );
32414         // original code supports filtering layout items.. we just ignore it..
32415         
32416         this._layoutItems( this.bricks , isInstant );
32417       
32418         this._postLayout();
32419     },
32420     _layoutItems : function ( items , isInstant)
32421     {
32422        //this.fireEvent( 'layout', this, items );
32423     
32424
32425         if ( !items || !items.elements.length ) {
32426           // no items, emit event with empty array
32427             return;
32428         }
32429
32430         var queue = [];
32431         items.each(function(item) {
32432             Roo.log("layout item");
32433             Roo.log(item);
32434             // get x/y object from method
32435             var position = this._getItemLayoutPosition( item );
32436             // enqueue
32437             position.item = item;
32438             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32439             queue.push( position );
32440         }, this);
32441       
32442         this._processLayoutQueue( queue );
32443     },
32444     /** Sets position of item in DOM
32445     * @param {Element} item
32446     * @param {Number} x - horizontal position
32447     * @param {Number} y - vertical position
32448     * @param {Boolean} isInstant - disables transitions
32449     */
32450     _processLayoutQueue : function( queue )
32451     {
32452         for ( var i=0, len = queue.length; i < len; i++ ) {
32453             var obj = queue[i];
32454             obj.item.position('absolute');
32455             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32456         }
32457     },
32458       
32459     
32460     /**
32461     * Any logic you want to do after each layout,
32462     * i.e. size the container
32463     */
32464     _postLayout : function()
32465     {
32466         this.resizeContainer();
32467     },
32468     
32469     resizeContainer : function()
32470     {
32471         if ( !this.isResizingContainer ) {
32472             return;
32473         }
32474         var size = this._getContainerSize();
32475         if ( size ) {
32476             this.el.setSize(size.width,size.height);
32477             this.boxesEl.setSize(size.width,size.height);
32478         }
32479     },
32480     
32481     
32482     
32483     _resetLayout : function()
32484     {
32485         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32486         this.colWidth = this.el.getWidth();
32487         //this.gutter = this.el.getWidth(); 
32488         
32489         this.measureColumns();
32490
32491         // reset column Y
32492         var i = this.cols;
32493         this.colYs = [];
32494         while (i--) {
32495             this.colYs.push( 0 );
32496         }
32497     
32498         this.maxY = 0;
32499     },
32500
32501     measureColumns : function()
32502     {
32503         this.getContainerWidth();
32504       // if columnWidth is 0, default to outerWidth of first item
32505         if ( !this.columnWidth ) {
32506             var firstItem = this.bricks.first();
32507             Roo.log(firstItem);
32508             this.columnWidth  = this.containerWidth;
32509             if (firstItem && firstItem.attr('originalwidth') ) {
32510                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32511             }
32512             // columnWidth fall back to item of first element
32513             Roo.log("set column width?");
32514                         this.initialColumnWidth = this.columnWidth  ;
32515
32516             // if first elem has no width, default to size of container
32517             
32518         }
32519         
32520         
32521         if (this.initialColumnWidth) {
32522             this.columnWidth = this.initialColumnWidth;
32523         }
32524         
32525         
32526             
32527         // column width is fixed at the top - however if container width get's smaller we should
32528         // reduce it...
32529         
32530         // this bit calcs how man columns..
32531             
32532         var columnWidth = this.columnWidth += this.gutter;
32533       
32534         // calculate columns
32535         var containerWidth = this.containerWidth + this.gutter;
32536         
32537         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32538         // fix rounding errors, typically with gutters
32539         var excess = columnWidth - containerWidth % columnWidth;
32540         
32541         
32542         // if overshoot is less than a pixel, round up, otherwise floor it
32543         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32544         cols = Math[ mathMethod ]( cols );
32545         this.cols = Math.max( cols, 1 );
32546         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32547         
32548          // padding positioning..
32549         var totalColWidth = this.cols * this.columnWidth;
32550         var padavail = this.containerWidth - totalColWidth;
32551         // so for 2 columns - we need 3 'pads'
32552         
32553         var padNeeded = (1+this.cols) * this.padWidth;
32554         
32555         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32556         
32557         this.columnWidth += padExtra
32558         //this.padWidth = Math.floor(padavail /  ( this.cols));
32559         
32560         // adjust colum width so that padding is fixed??
32561         
32562         // we have 3 columns ... total = width * 3
32563         // we have X left over... that should be used by 
32564         
32565         //if (this.expandC) {
32566             
32567         //}
32568         
32569         
32570         
32571     },
32572     
32573     getContainerWidth : function()
32574     {
32575        /* // container is parent if fit width
32576         var container = this.isFitWidth ? this.element.parentNode : this.element;
32577         // check that this.size and size are there
32578         // IE8 triggers resize on body size change, so they might not be
32579         
32580         var size = getSize( container );  //FIXME
32581         this.containerWidth = size && size.innerWidth; //FIXME
32582         */
32583          
32584         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32585         
32586     },
32587     
32588     _getItemLayoutPosition : function( item )  // what is item?
32589     {
32590         // we resize the item to our columnWidth..
32591       
32592         item.setWidth(this.columnWidth);
32593         item.autoBoxAdjust  = false;
32594         
32595         var sz = item.getSize();
32596  
32597         // how many columns does this brick span
32598         var remainder = this.containerWidth % this.columnWidth;
32599         
32600         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32601         // round if off by 1 pixel, otherwise use ceil
32602         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32603         colSpan = Math.min( colSpan, this.cols );
32604         
32605         // normally this should be '1' as we dont' currently allow multi width columns..
32606         
32607         var colGroup = this._getColGroup( colSpan );
32608         // get the minimum Y value from the columns
32609         var minimumY = Math.min.apply( Math, colGroup );
32610         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32611         
32612         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32613          
32614         // position the brick
32615         var position = {
32616             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32617             y: this.currentSize.y + minimumY + this.padHeight
32618         };
32619         
32620         Roo.log(position);
32621         // apply setHeight to necessary columns
32622         var setHeight = minimumY + sz.height + this.padHeight;
32623         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32624         
32625         var setSpan = this.cols + 1 - colGroup.length;
32626         for ( var i = 0; i < setSpan; i++ ) {
32627           this.colYs[ shortColIndex + i ] = setHeight ;
32628         }
32629       
32630         return position;
32631     },
32632     
32633     /**
32634      * @param {Number} colSpan - number of columns the element spans
32635      * @returns {Array} colGroup
32636      */
32637     _getColGroup : function( colSpan )
32638     {
32639         if ( colSpan < 2 ) {
32640           // if brick spans only one column, use all the column Ys
32641           return this.colYs;
32642         }
32643       
32644         var colGroup = [];
32645         // how many different places could this brick fit horizontally
32646         var groupCount = this.cols + 1 - colSpan;
32647         // for each group potential horizontal position
32648         for ( var i = 0; i < groupCount; i++ ) {
32649           // make an array of colY values for that one group
32650           var groupColYs = this.colYs.slice( i, i + colSpan );
32651           // and get the max value of the array
32652           colGroup[i] = Math.max.apply( Math, groupColYs );
32653         }
32654         return colGroup;
32655     },
32656     /*
32657     _manageStamp : function( stamp )
32658     {
32659         var stampSize =  stamp.getSize();
32660         var offset = stamp.getBox();
32661         // get the columns that this stamp affects
32662         var firstX = this.isOriginLeft ? offset.x : offset.right;
32663         var lastX = firstX + stampSize.width;
32664         var firstCol = Math.floor( firstX / this.columnWidth );
32665         firstCol = Math.max( 0, firstCol );
32666         
32667         var lastCol = Math.floor( lastX / this.columnWidth );
32668         // lastCol should not go over if multiple of columnWidth #425
32669         lastCol -= lastX % this.columnWidth ? 0 : 1;
32670         lastCol = Math.min( this.cols - 1, lastCol );
32671         
32672         // set colYs to bottom of the stamp
32673         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32674             stampSize.height;
32675             
32676         for ( var i = firstCol; i <= lastCol; i++ ) {
32677           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32678         }
32679     },
32680     */
32681     
32682     _getContainerSize : function()
32683     {
32684         this.maxY = Math.max.apply( Math, this.colYs );
32685         var size = {
32686             height: this.maxY
32687         };
32688       
32689         if ( this.isFitWidth ) {
32690             size.width = this._getContainerFitWidth();
32691         }
32692       
32693         return size;
32694     },
32695     
32696     _getContainerFitWidth : function()
32697     {
32698         var unusedCols = 0;
32699         // count unused columns
32700         var i = this.cols;
32701         while ( --i ) {
32702           if ( this.colYs[i] !== 0 ) {
32703             break;
32704           }
32705           unusedCols++;
32706         }
32707         // fit container to columns that have been used
32708         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32709     },
32710     
32711     needsResizeLayout : function()
32712     {
32713         var previousWidth = this.containerWidth;
32714         this.getContainerWidth();
32715         return previousWidth !== this.containerWidth;
32716     }
32717  
32718 });
32719
32720  
32721
32722  /*
32723  * - LGPL
32724  *
32725  * element
32726  * 
32727  */
32728
32729 /**
32730  * @class Roo.bootstrap.MasonryBrick
32731  * @extends Roo.bootstrap.Component
32732  * Bootstrap MasonryBrick class
32733  * 
32734  * @constructor
32735  * Create a new MasonryBrick
32736  * @param {Object} config The config object
32737  */
32738
32739 Roo.bootstrap.MasonryBrick = function(config){
32740     
32741     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32742     
32743     Roo.bootstrap.MasonryBrick.register(this);
32744     
32745     this.addEvents({
32746         // raw events
32747         /**
32748          * @event click
32749          * When a MasonryBrick is clcik
32750          * @param {Roo.bootstrap.MasonryBrick} this
32751          * @param {Roo.EventObject} e
32752          */
32753         "click" : true
32754     });
32755 };
32756
32757 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32758     
32759     /**
32760      * @cfg {String} title
32761      */   
32762     title : '',
32763     /**
32764      * @cfg {String} html
32765      */   
32766     html : '',
32767     /**
32768      * @cfg {String} bgimage
32769      */   
32770     bgimage : '',
32771     /**
32772      * @cfg {String} videourl
32773      */   
32774     videourl : '',
32775     /**
32776      * @cfg {String} cls
32777      */   
32778     cls : '',
32779     /**
32780      * @cfg {String} href
32781      */   
32782     href : '',
32783     /**
32784      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32785      */   
32786     size : 'xs',
32787     
32788     /**
32789      * @cfg {String} placetitle (center|bottom)
32790      */   
32791     placetitle : '',
32792     
32793     /**
32794      * @cfg {Boolean} isFitContainer defalut true
32795      */   
32796     isFitContainer : true, 
32797     
32798     /**
32799      * @cfg {Boolean} preventDefault defalut false
32800      */   
32801     preventDefault : false, 
32802     
32803     /**
32804      * @cfg {Boolean} inverse defalut false
32805      */   
32806     maskInverse : false, 
32807     
32808     getAutoCreate : function()
32809     {
32810         if(!this.isFitContainer){
32811             return this.getSplitAutoCreate();
32812         }
32813         
32814         var cls = 'masonry-brick masonry-brick-full';
32815         
32816         if(this.href.length){
32817             cls += ' masonry-brick-link';
32818         }
32819         
32820         if(this.bgimage.length){
32821             cls += ' masonry-brick-image';
32822         }
32823         
32824         if(this.maskInverse){
32825             cls += ' mask-inverse';
32826         }
32827         
32828         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32829             cls += ' enable-mask';
32830         }
32831         
32832         if(this.size){
32833             cls += ' masonry-' + this.size + '-brick';
32834         }
32835         
32836         if(this.placetitle.length){
32837             
32838             switch (this.placetitle) {
32839                 case 'center' :
32840                     cls += ' masonry-center-title';
32841                     break;
32842                 case 'bottom' :
32843                     cls += ' masonry-bottom-title';
32844                     break;
32845                 default:
32846                     break;
32847             }
32848             
32849         } else {
32850             if(!this.html.length && !this.bgimage.length){
32851                 cls += ' masonry-center-title';
32852             }
32853
32854             if(!this.html.length && this.bgimage.length){
32855                 cls += ' masonry-bottom-title';
32856             }
32857         }
32858         
32859         if(this.cls){
32860             cls += ' ' + this.cls;
32861         }
32862         
32863         var cfg = {
32864             tag: (this.href.length) ? 'a' : 'div',
32865             cls: cls,
32866             cn: [
32867                 {
32868                     tag: 'div',
32869                     cls: 'masonry-brick-mask'
32870                 },
32871                 {
32872                     tag: 'div',
32873                     cls: 'masonry-brick-paragraph',
32874                     cn: []
32875                 }
32876             ]
32877         };
32878         
32879         if(this.href.length){
32880             cfg.href = this.href;
32881         }
32882         
32883         var cn = cfg.cn[1].cn;
32884         
32885         if(this.title.length){
32886             cn.push({
32887                 tag: 'h4',
32888                 cls: 'masonry-brick-title',
32889                 html: this.title
32890             });
32891         }
32892         
32893         if(this.html.length){
32894             cn.push({
32895                 tag: 'p',
32896                 cls: 'masonry-brick-text',
32897                 html: this.html
32898             });
32899         }
32900         
32901         if (!this.title.length && !this.html.length) {
32902             cfg.cn[1].cls += ' hide';
32903         }
32904         
32905         if(this.bgimage.length){
32906             cfg.cn.push({
32907                 tag: 'img',
32908                 cls: 'masonry-brick-image-view',
32909                 src: this.bgimage
32910             });
32911         }
32912         
32913         if(this.videourl.length){
32914             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32915             // youtube support only?
32916             cfg.cn.push({
32917                 tag: 'iframe',
32918                 cls: 'masonry-brick-image-view',
32919                 src: vurl,
32920                 frameborder : 0,
32921                 allowfullscreen : true
32922             });
32923         }
32924         
32925         return cfg;
32926         
32927     },
32928     
32929     getSplitAutoCreate : function()
32930     {
32931         var cls = 'masonry-brick masonry-brick-split';
32932         
32933         if(this.href.length){
32934             cls += ' masonry-brick-link';
32935         }
32936         
32937         if(this.bgimage.length){
32938             cls += ' masonry-brick-image';
32939         }
32940         
32941         if(this.size){
32942             cls += ' masonry-' + this.size + '-brick';
32943         }
32944         
32945         switch (this.placetitle) {
32946             case 'center' :
32947                 cls += ' masonry-center-title';
32948                 break;
32949             case 'bottom' :
32950                 cls += ' masonry-bottom-title';
32951                 break;
32952             default:
32953                 if(!this.bgimage.length){
32954                     cls += ' masonry-center-title';
32955                 }
32956
32957                 if(this.bgimage.length){
32958                     cls += ' masonry-bottom-title';
32959                 }
32960                 break;
32961         }
32962         
32963         if(this.cls){
32964             cls += ' ' + this.cls;
32965         }
32966         
32967         var cfg = {
32968             tag: (this.href.length) ? 'a' : 'div',
32969             cls: cls,
32970             cn: [
32971                 {
32972                     tag: 'div',
32973                     cls: 'masonry-brick-split-head',
32974                     cn: [
32975                         {
32976                             tag: 'div',
32977                             cls: 'masonry-brick-paragraph',
32978                             cn: []
32979                         }
32980                     ]
32981                 },
32982                 {
32983                     tag: 'div',
32984                     cls: 'masonry-brick-split-body',
32985                     cn: []
32986                 }
32987             ]
32988         };
32989         
32990         if(this.href.length){
32991             cfg.href = this.href;
32992         }
32993         
32994         if(this.title.length){
32995             cfg.cn[0].cn[0].cn.push({
32996                 tag: 'h4',
32997                 cls: 'masonry-brick-title',
32998                 html: this.title
32999             });
33000         }
33001         
33002         if(this.html.length){
33003             cfg.cn[1].cn.push({
33004                 tag: 'p',
33005                 cls: 'masonry-brick-text',
33006                 html: this.html
33007             });
33008         }
33009
33010         if(this.bgimage.length){
33011             cfg.cn[0].cn.push({
33012                 tag: 'img',
33013                 cls: 'masonry-brick-image-view',
33014                 src: this.bgimage
33015             });
33016         }
33017         
33018         if(this.videourl.length){
33019             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33020             // youtube support only?
33021             cfg.cn[0].cn.cn.push({
33022                 tag: 'iframe',
33023                 cls: 'masonry-brick-image-view',
33024                 src: vurl,
33025                 frameborder : 0,
33026                 allowfullscreen : true
33027             });
33028         }
33029         
33030         return cfg;
33031     },
33032     
33033     initEvents: function() 
33034     {
33035         switch (this.size) {
33036             case 'xs' :
33037                 this.x = 1;
33038                 this.y = 1;
33039                 break;
33040             case 'sm' :
33041                 this.x = 2;
33042                 this.y = 2;
33043                 break;
33044             case 'md' :
33045             case 'md-left' :
33046             case 'md-right' :
33047                 this.x = 3;
33048                 this.y = 3;
33049                 break;
33050             case 'tall' :
33051                 this.x = 2;
33052                 this.y = 3;
33053                 break;
33054             case 'wide' :
33055                 this.x = 3;
33056                 this.y = 2;
33057                 break;
33058             case 'wide-thin' :
33059                 this.x = 3;
33060                 this.y = 1;
33061                 break;
33062                         
33063             default :
33064                 break;
33065         }
33066         
33067         if(Roo.isTouch){
33068             this.el.on('touchstart', this.onTouchStart, this);
33069             this.el.on('touchmove', this.onTouchMove, this);
33070             this.el.on('touchend', this.onTouchEnd, this);
33071             this.el.on('contextmenu', this.onContextMenu, this);
33072         } else {
33073             this.el.on('mouseenter'  ,this.enter, this);
33074             this.el.on('mouseleave', this.leave, this);
33075             this.el.on('click', this.onClick, this);
33076         }
33077         
33078         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33079             this.parent().bricks.push(this);   
33080         }
33081         
33082     },
33083     
33084     onClick: function(e, el)
33085     {
33086         var time = this.endTimer - this.startTimer;
33087         // Roo.log(e.preventDefault());
33088         if(Roo.isTouch){
33089             if(time > 1000){
33090                 e.preventDefault();
33091                 return;
33092             }
33093         }
33094         
33095         if(!this.preventDefault){
33096             return;
33097         }
33098         
33099         e.preventDefault();
33100         
33101         if (this.activeClass != '') {
33102             this.selectBrick();
33103         }
33104         
33105         this.fireEvent('click', this, e);
33106     },
33107     
33108     enter: function(e, el)
33109     {
33110         e.preventDefault();
33111         
33112         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33113             return;
33114         }
33115         
33116         if(this.bgimage.length && this.html.length){
33117             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33118         }
33119     },
33120     
33121     leave: function(e, el)
33122     {
33123         e.preventDefault();
33124         
33125         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33126             return;
33127         }
33128         
33129         if(this.bgimage.length && this.html.length){
33130             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33131         }
33132     },
33133     
33134     onTouchStart: function(e, el)
33135     {
33136 //        e.preventDefault();
33137         
33138         this.touchmoved = false;
33139         
33140         if(!this.isFitContainer){
33141             return;
33142         }
33143         
33144         if(!this.bgimage.length || !this.html.length){
33145             return;
33146         }
33147         
33148         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33149         
33150         this.timer = new Date().getTime();
33151         
33152     },
33153     
33154     onTouchMove: function(e, el)
33155     {
33156         this.touchmoved = true;
33157     },
33158     
33159     onContextMenu : function(e,el)
33160     {
33161         e.preventDefault();
33162         e.stopPropagation();
33163         return false;
33164     },
33165     
33166     onTouchEnd: function(e, el)
33167     {
33168 //        e.preventDefault();
33169         
33170         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33171         
33172             this.leave(e,el);
33173             
33174             return;
33175         }
33176         
33177         if(!this.bgimage.length || !this.html.length){
33178             
33179             if(this.href.length){
33180                 window.location.href = this.href;
33181             }
33182             
33183             return;
33184         }
33185         
33186         if(!this.isFitContainer){
33187             return;
33188         }
33189         
33190         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33191         
33192         window.location.href = this.href;
33193     },
33194     
33195     //selection on single brick only
33196     selectBrick : function() {
33197         
33198         if (!this.parentId) {
33199             return;
33200         }
33201         
33202         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33203         var index = m.selectedBrick.indexOf(this.id);
33204         
33205         if ( index > -1) {
33206             m.selectedBrick.splice(index,1);
33207             this.el.removeClass(this.activeClass);
33208             return;
33209         }
33210         
33211         for(var i = 0; i < m.selectedBrick.length; i++) {
33212             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33213             b.el.removeClass(b.activeClass);
33214         }
33215         
33216         m.selectedBrick = [];
33217         
33218         m.selectedBrick.push(this.id);
33219         this.el.addClass(this.activeClass);
33220         return;
33221     },
33222     
33223     isSelected : function(){
33224         return this.el.hasClass(this.activeClass);
33225         
33226     }
33227 });
33228
33229 Roo.apply(Roo.bootstrap.MasonryBrick, {
33230     
33231     //groups: {},
33232     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33233      /**
33234     * register a Masonry Brick
33235     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33236     */
33237     
33238     register : function(brick)
33239     {
33240         //this.groups[brick.id] = brick;
33241         this.groups.add(brick.id, brick);
33242     },
33243     /**
33244     * fetch a  masonry brick based on the masonry brick ID
33245     * @param {string} the masonry brick to add
33246     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33247     */
33248     
33249     get: function(brick_id) 
33250     {
33251         // if (typeof(this.groups[brick_id]) == 'undefined') {
33252         //     return false;
33253         // }
33254         // return this.groups[brick_id] ;
33255         
33256         if(this.groups.key(brick_id)) {
33257             return this.groups.key(brick_id);
33258         }
33259         
33260         return false;
33261     }
33262     
33263     
33264     
33265 });
33266
33267  /*
33268  * - LGPL
33269  *
33270  * element
33271  * 
33272  */
33273
33274 /**
33275  * @class Roo.bootstrap.Brick
33276  * @extends Roo.bootstrap.Component
33277  * Bootstrap Brick class
33278  * 
33279  * @constructor
33280  * Create a new Brick
33281  * @param {Object} config The config object
33282  */
33283
33284 Roo.bootstrap.Brick = function(config){
33285     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33286     
33287     this.addEvents({
33288         // raw events
33289         /**
33290          * @event click
33291          * When a Brick is click
33292          * @param {Roo.bootstrap.Brick} this
33293          * @param {Roo.EventObject} e
33294          */
33295         "click" : true
33296     });
33297 };
33298
33299 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33300     
33301     /**
33302      * @cfg {String} title
33303      */   
33304     title : '',
33305     /**
33306      * @cfg {String} html
33307      */   
33308     html : '',
33309     /**
33310      * @cfg {String} bgimage
33311      */   
33312     bgimage : '',
33313     /**
33314      * @cfg {String} cls
33315      */   
33316     cls : '',
33317     /**
33318      * @cfg {String} href
33319      */   
33320     href : '',
33321     /**
33322      * @cfg {String} video
33323      */   
33324     video : '',
33325     /**
33326      * @cfg {Boolean} square
33327      */   
33328     square : true,
33329     
33330     getAutoCreate : function()
33331     {
33332         var cls = 'roo-brick';
33333         
33334         if(this.href.length){
33335             cls += ' roo-brick-link';
33336         }
33337         
33338         if(this.bgimage.length){
33339             cls += ' roo-brick-image';
33340         }
33341         
33342         if(!this.html.length && !this.bgimage.length){
33343             cls += ' roo-brick-center-title';
33344         }
33345         
33346         if(!this.html.length && this.bgimage.length){
33347             cls += ' roo-brick-bottom-title';
33348         }
33349         
33350         if(this.cls){
33351             cls += ' ' + this.cls;
33352         }
33353         
33354         var cfg = {
33355             tag: (this.href.length) ? 'a' : 'div',
33356             cls: cls,
33357             cn: [
33358                 {
33359                     tag: 'div',
33360                     cls: 'roo-brick-paragraph',
33361                     cn: []
33362                 }
33363             ]
33364         };
33365         
33366         if(this.href.length){
33367             cfg.href = this.href;
33368         }
33369         
33370         var cn = cfg.cn[0].cn;
33371         
33372         if(this.title.length){
33373             cn.push({
33374                 tag: 'h4',
33375                 cls: 'roo-brick-title',
33376                 html: this.title
33377             });
33378         }
33379         
33380         if(this.html.length){
33381             cn.push({
33382                 tag: 'p',
33383                 cls: 'roo-brick-text',
33384                 html: this.html
33385             });
33386         } else {
33387             cn.cls += ' hide';
33388         }
33389         
33390         if(this.bgimage.length){
33391             cfg.cn.push({
33392                 tag: 'img',
33393                 cls: 'roo-brick-image-view',
33394                 src: this.bgimage
33395             });
33396         }
33397         
33398         return cfg;
33399     },
33400     
33401     initEvents: function() 
33402     {
33403         if(this.title.length || this.html.length){
33404             this.el.on('mouseenter'  ,this.enter, this);
33405             this.el.on('mouseleave', this.leave, this);
33406         }
33407         
33408         Roo.EventManager.onWindowResize(this.resize, this); 
33409         
33410         if(this.bgimage.length){
33411             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33412             this.imageEl.on('load', this.onImageLoad, this);
33413             return;
33414         }
33415         
33416         this.resize();
33417     },
33418     
33419     onImageLoad : function()
33420     {
33421         this.resize();
33422     },
33423     
33424     resize : function()
33425     {
33426         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33427         
33428         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33429         
33430         if(this.bgimage.length){
33431             var image = this.el.select('.roo-brick-image-view', true).first();
33432             
33433             image.setWidth(paragraph.getWidth());
33434             
33435             if(this.square){
33436                 image.setHeight(paragraph.getWidth());
33437             }
33438             
33439             this.el.setHeight(image.getHeight());
33440             paragraph.setHeight(image.getHeight());
33441             
33442         }
33443         
33444     },
33445     
33446     enter: function(e, el)
33447     {
33448         e.preventDefault();
33449         
33450         if(this.bgimage.length){
33451             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33452             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33453         }
33454     },
33455     
33456     leave: function(e, el)
33457     {
33458         e.preventDefault();
33459         
33460         if(this.bgimage.length){
33461             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33462             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33463         }
33464     }
33465     
33466 });
33467
33468  
33469
33470  /*
33471  * - LGPL
33472  *
33473  * Number field 
33474  */
33475
33476 /**
33477  * @class Roo.bootstrap.NumberField
33478  * @extends Roo.bootstrap.Input
33479  * Bootstrap NumberField class
33480  * 
33481  * 
33482  * 
33483  * 
33484  * @constructor
33485  * Create a new NumberField
33486  * @param {Object} config The config object
33487  */
33488
33489 Roo.bootstrap.NumberField = function(config){
33490     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33491 };
33492
33493 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33494     
33495     /**
33496      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33497      */
33498     allowDecimals : true,
33499     /**
33500      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33501      */
33502     decimalSeparator : ".",
33503     /**
33504      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33505      */
33506     decimalPrecision : 2,
33507     /**
33508      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33509      */
33510     allowNegative : true,
33511     
33512     /**
33513      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33514      */
33515     allowZero: true,
33516     /**
33517      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33518      */
33519     minValue : Number.NEGATIVE_INFINITY,
33520     /**
33521      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33522      */
33523     maxValue : Number.MAX_VALUE,
33524     /**
33525      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33526      */
33527     minText : "The minimum value for this field is {0}",
33528     /**
33529      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33530      */
33531     maxText : "The maximum value for this field is {0}",
33532     /**
33533      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33534      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33535      */
33536     nanText : "{0} is not a valid number",
33537     /**
33538      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33539      */
33540     thousandsDelimiter : false,
33541     /**
33542      * @cfg {String} valueAlign alignment of value
33543      */
33544     valueAlign : "left",
33545
33546     getAutoCreate : function()
33547     {
33548         var hiddenInput = {
33549             tag: 'input',
33550             type: 'hidden',
33551             id: Roo.id(),
33552             cls: 'hidden-number-input'
33553         };
33554         
33555         if (this.name) {
33556             hiddenInput.name = this.name;
33557         }
33558         
33559         this.name = '';
33560         
33561         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33562         
33563         this.name = hiddenInput.name;
33564         
33565         if(cfg.cn.length > 0) {
33566             cfg.cn.push(hiddenInput);
33567         }
33568         
33569         return cfg;
33570     },
33571
33572     // private
33573     initEvents : function()
33574     {   
33575         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33576         
33577         var allowed = "0123456789";
33578         
33579         if(this.allowDecimals){
33580             allowed += this.decimalSeparator;
33581         }
33582         
33583         if(this.allowNegative){
33584             allowed += "-";
33585         }
33586         
33587         if(this.thousandsDelimiter) {
33588             allowed += ",";
33589         }
33590         
33591         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33592         
33593         var keyPress = function(e){
33594             
33595             var k = e.getKey();
33596             
33597             var c = e.getCharCode();
33598             
33599             if(
33600                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33601                     allowed.indexOf(String.fromCharCode(c)) === -1
33602             ){
33603                 e.stopEvent();
33604                 return;
33605             }
33606             
33607             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33608                 return;
33609             }
33610             
33611             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33612                 e.stopEvent();
33613             }
33614         };
33615         
33616         this.el.on("keypress", keyPress, this);
33617     },
33618     
33619     validateValue : function(value)
33620     {
33621         
33622         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33623             return false;
33624         }
33625         
33626         var num = this.parseValue(value);
33627         
33628         if(isNaN(num)){
33629             this.markInvalid(String.format(this.nanText, value));
33630             return false;
33631         }
33632         
33633         if(num < this.minValue){
33634             this.markInvalid(String.format(this.minText, this.minValue));
33635             return false;
33636         }
33637         
33638         if(num > this.maxValue){
33639             this.markInvalid(String.format(this.maxText, this.maxValue));
33640             return false;
33641         }
33642         
33643         return true;
33644     },
33645
33646     getValue : function()
33647     {
33648         var v = this.hiddenEl().getValue();
33649         
33650         return this.fixPrecision(this.parseValue(v));
33651     },
33652
33653     parseValue : function(value)
33654     {
33655         if(this.thousandsDelimiter) {
33656             value += "";
33657             r = new RegExp(",", "g");
33658             value = value.replace(r, "");
33659         }
33660         
33661         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33662         return isNaN(value) ? '' : value;
33663     },
33664
33665     fixPrecision : function(value)
33666     {
33667         if(this.thousandsDelimiter) {
33668             value += "";
33669             r = new RegExp(",", "g");
33670             value = value.replace(r, "");
33671         }
33672         
33673         var nan = isNaN(value);
33674         
33675         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33676             return nan ? '' : value;
33677         }
33678         return parseFloat(value).toFixed(this.decimalPrecision);
33679     },
33680
33681     setValue : function(v)
33682     {
33683         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33684         
33685         this.value = v;
33686         
33687         if(this.rendered){
33688             
33689             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33690             
33691             this.inputEl().dom.value = (v == '') ? '' :
33692                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33693             
33694             if(!this.allowZero && v === '0') {
33695                 this.hiddenEl().dom.value = '';
33696                 this.inputEl().dom.value = '';
33697             }
33698             
33699             this.validate();
33700         }
33701     },
33702
33703     decimalPrecisionFcn : function(v)
33704     {
33705         return Math.floor(v);
33706     },
33707
33708     beforeBlur : function()
33709     {
33710         var v = this.parseValue(this.getRawValue());
33711         
33712         if(v || v === 0 || v === ''){
33713             this.setValue(v);
33714         }
33715     },
33716     
33717     hiddenEl : function()
33718     {
33719         return this.el.select('input.hidden-number-input',true).first();
33720     }
33721     
33722 });
33723
33724  
33725
33726 /*
33727 * Licence: LGPL
33728 */
33729
33730 /**
33731  * @class Roo.bootstrap.DocumentSlider
33732  * @extends Roo.bootstrap.Component
33733  * Bootstrap DocumentSlider class
33734  * 
33735  * @constructor
33736  * Create a new DocumentViewer
33737  * @param {Object} config The config object
33738  */
33739
33740 Roo.bootstrap.DocumentSlider = function(config){
33741     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33742     
33743     this.files = [];
33744     
33745     this.addEvents({
33746         /**
33747          * @event initial
33748          * Fire after initEvent
33749          * @param {Roo.bootstrap.DocumentSlider} this
33750          */
33751         "initial" : true,
33752         /**
33753          * @event update
33754          * Fire after update
33755          * @param {Roo.bootstrap.DocumentSlider} this
33756          */
33757         "update" : true,
33758         /**
33759          * @event click
33760          * Fire after click
33761          * @param {Roo.bootstrap.DocumentSlider} this
33762          */
33763         "click" : true
33764     });
33765 };
33766
33767 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33768     
33769     files : false,
33770     
33771     indicator : 0,
33772     
33773     getAutoCreate : function()
33774     {
33775         var cfg = {
33776             tag : 'div',
33777             cls : 'roo-document-slider',
33778             cn : [
33779                 {
33780                     tag : 'div',
33781                     cls : 'roo-document-slider-header',
33782                     cn : [
33783                         {
33784                             tag : 'div',
33785                             cls : 'roo-document-slider-header-title'
33786                         }
33787                     ]
33788                 },
33789                 {
33790                     tag : 'div',
33791                     cls : 'roo-document-slider-body',
33792                     cn : [
33793                         {
33794                             tag : 'div',
33795                             cls : 'roo-document-slider-prev',
33796                             cn : [
33797                                 {
33798                                     tag : 'i',
33799                                     cls : 'fa fa-chevron-left'
33800                                 }
33801                             ]
33802                         },
33803                         {
33804                             tag : 'div',
33805                             cls : 'roo-document-slider-thumb',
33806                             cn : [
33807                                 {
33808                                     tag : 'img',
33809                                     cls : 'roo-document-slider-image'
33810                                 }
33811                             ]
33812                         },
33813                         {
33814                             tag : 'div',
33815                             cls : 'roo-document-slider-next',
33816                             cn : [
33817                                 {
33818                                     tag : 'i',
33819                                     cls : 'fa fa-chevron-right'
33820                                 }
33821                             ]
33822                         }
33823                     ]
33824                 }
33825             ]
33826         };
33827         
33828         return cfg;
33829     },
33830     
33831     initEvents : function()
33832     {
33833         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33834         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33835         
33836         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33837         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33838         
33839         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33840         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33841         
33842         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33843         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33844         
33845         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33846         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33847         
33848         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33849         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33850         
33851         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33852         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33853         
33854         this.thumbEl.on('click', this.onClick, this);
33855         
33856         this.prevIndicator.on('click', this.prev, this);
33857         
33858         this.nextIndicator.on('click', this.next, this);
33859         
33860     },
33861     
33862     initial : function()
33863     {
33864         if(this.files.length){
33865             this.indicator = 1;
33866             this.update()
33867         }
33868         
33869         this.fireEvent('initial', this);
33870     },
33871     
33872     update : function()
33873     {
33874         this.imageEl.attr('src', this.files[this.indicator - 1]);
33875         
33876         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33877         
33878         this.prevIndicator.show();
33879         
33880         if(this.indicator == 1){
33881             this.prevIndicator.hide();
33882         }
33883         
33884         this.nextIndicator.show();
33885         
33886         if(this.indicator == this.files.length){
33887             this.nextIndicator.hide();
33888         }
33889         
33890         this.thumbEl.scrollTo('top');
33891         
33892         this.fireEvent('update', this);
33893     },
33894     
33895     onClick : function(e)
33896     {
33897         e.preventDefault();
33898         
33899         this.fireEvent('click', this);
33900     },
33901     
33902     prev : function(e)
33903     {
33904         e.preventDefault();
33905         
33906         this.indicator = Math.max(1, this.indicator - 1);
33907         
33908         this.update();
33909     },
33910     
33911     next : function(e)
33912     {
33913         e.preventDefault();
33914         
33915         this.indicator = Math.min(this.files.length, this.indicator + 1);
33916         
33917         this.update();
33918     }
33919 });
33920 /*
33921  * - LGPL
33922  *
33923  * RadioSet
33924  *
33925  *
33926  */
33927
33928 /**
33929  * @class Roo.bootstrap.RadioSet
33930  * @extends Roo.bootstrap.Input
33931  * Bootstrap RadioSet class
33932  * @cfg {String} indicatorpos (left|right) default left
33933  * @cfg {Boolean} inline (true|false) inline the element (default true)
33934  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33935  * @constructor
33936  * Create a new RadioSet
33937  * @param {Object} config The config object
33938  */
33939
33940 Roo.bootstrap.RadioSet = function(config){
33941     
33942     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33943     
33944     this.radioes = [];
33945     
33946     Roo.bootstrap.RadioSet.register(this);
33947     
33948     this.addEvents({
33949         /**
33950         * @event check
33951         * Fires when the element is checked or unchecked.
33952         * @param {Roo.bootstrap.RadioSet} this This radio
33953         * @param {Roo.bootstrap.Radio} item The checked item
33954         */
33955        check : true,
33956        /**
33957         * @event click
33958         * Fires when the element is click.
33959         * @param {Roo.bootstrap.RadioSet} this This radio set
33960         * @param {Roo.bootstrap.Radio} item The checked item
33961         * @param {Roo.EventObject} e The event object
33962         */
33963        click : true
33964     });
33965     
33966 };
33967
33968 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33969
33970     radioes : false,
33971     
33972     inline : true,
33973     
33974     weight : '',
33975     
33976     indicatorpos : 'left',
33977     
33978     getAutoCreate : function()
33979     {
33980         var label = {
33981             tag : 'label',
33982             cls : 'roo-radio-set-label',
33983             cn : [
33984                 {
33985                     tag : 'span',
33986                     html : this.fieldLabel
33987                 }
33988             ]
33989         };
33990         
33991         if(this.indicatorpos == 'left'){
33992             label.cn.unshift({
33993                 tag : 'i',
33994                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33995                 tooltip : 'This field is required'
33996             });
33997         } else {
33998             label.cn.push({
33999                 tag : 'i',
34000                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34001                 tooltip : 'This field is required'
34002             });
34003         }
34004         
34005         var items = {
34006             tag : 'div',
34007             cls : 'roo-radio-set-items'
34008         };
34009         
34010         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34011         
34012         if (align === 'left' && this.fieldLabel.length) {
34013             
34014             items = {
34015                 cls : "roo-radio-set-right", 
34016                 cn: [
34017                     items
34018                 ]
34019             };
34020             
34021             if(this.labelWidth > 12){
34022                 label.style = "width: " + this.labelWidth + 'px';
34023             }
34024             
34025             if(this.labelWidth < 13 && this.labelmd == 0){
34026                 this.labelmd = this.labelWidth;
34027             }
34028             
34029             if(this.labellg > 0){
34030                 label.cls += ' col-lg-' + this.labellg;
34031                 items.cls += ' col-lg-' + (12 - this.labellg);
34032             }
34033             
34034             if(this.labelmd > 0){
34035                 label.cls += ' col-md-' + this.labelmd;
34036                 items.cls += ' col-md-' + (12 - this.labelmd);
34037             }
34038             
34039             if(this.labelsm > 0){
34040                 label.cls += ' col-sm-' + this.labelsm;
34041                 items.cls += ' col-sm-' + (12 - this.labelsm);
34042             }
34043             
34044             if(this.labelxs > 0){
34045                 label.cls += ' col-xs-' + this.labelxs;
34046                 items.cls += ' col-xs-' + (12 - this.labelxs);
34047             }
34048         }
34049         
34050         var cfg = {
34051             tag : 'div',
34052             cls : 'roo-radio-set',
34053             cn : [
34054                 {
34055                     tag : 'input',
34056                     cls : 'roo-radio-set-input',
34057                     type : 'hidden',
34058                     name : this.name,
34059                     value : this.value ? this.value :  ''
34060                 },
34061                 label,
34062                 items
34063             ]
34064         };
34065         
34066         if(this.weight.length){
34067             cfg.cls += ' roo-radio-' + this.weight;
34068         }
34069         
34070         if(this.inline) {
34071             cfg.cls += ' roo-radio-set-inline';
34072         }
34073         
34074         var settings=this;
34075         ['xs','sm','md','lg'].map(function(size){
34076             if (settings[size]) {
34077                 cfg.cls += ' col-' + size + '-' + settings[size];
34078             }
34079         });
34080         
34081         return cfg;
34082         
34083     },
34084
34085     initEvents : function()
34086     {
34087         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34088         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34089         
34090         if(!this.fieldLabel.length){
34091             this.labelEl.hide();
34092         }
34093         
34094         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34095         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34096         
34097         this.indicator = this.indicatorEl();
34098         
34099         if(this.indicator){
34100             this.indicator.addClass('invisible');
34101         }
34102         
34103         this.originalValue = this.getValue();
34104         
34105     },
34106     
34107     inputEl: function ()
34108     {
34109         return this.el.select('.roo-radio-set-input', true).first();
34110     },
34111     
34112     getChildContainer : function()
34113     {
34114         return this.itemsEl;
34115     },
34116     
34117     register : function(item)
34118     {
34119         this.radioes.push(item);
34120         
34121     },
34122     
34123     validate : function()
34124     {   
34125         if(this.getVisibilityEl().hasClass('hidden')){
34126             return true;
34127         }
34128         
34129         var valid = false;
34130         
34131         Roo.each(this.radioes, function(i){
34132             if(!i.checked){
34133                 return;
34134             }
34135             
34136             valid = true;
34137             return false;
34138         });
34139         
34140         if(this.allowBlank) {
34141             return true;
34142         }
34143         
34144         if(this.disabled || valid){
34145             this.markValid();
34146             return true;
34147         }
34148         
34149         this.markInvalid();
34150         return false;
34151         
34152     },
34153     
34154     markValid : function()
34155     {
34156         if(this.labelEl.isVisible(true)){
34157             this.indicatorEl().removeClass('visible');
34158             this.indicatorEl().addClass('invisible');
34159         }
34160         
34161         this.el.removeClass([this.invalidClass, this.validClass]);
34162         this.el.addClass(this.validClass);
34163         
34164         this.fireEvent('valid', this);
34165     },
34166     
34167     markInvalid : function(msg)
34168     {
34169         if(this.allowBlank || this.disabled){
34170             return;
34171         }
34172         
34173         if(this.labelEl.isVisible(true)){
34174             this.indicatorEl().removeClass('invisible');
34175             this.indicatorEl().addClass('visible');
34176         }
34177         
34178         this.el.removeClass([this.invalidClass, this.validClass]);
34179         this.el.addClass(this.invalidClass);
34180         
34181         this.fireEvent('invalid', this, msg);
34182         
34183     },
34184     
34185     setValue : function(v, suppressEvent)
34186     {   
34187         if(this.value === v){
34188             return;
34189         }
34190         
34191         this.value = v;
34192         
34193         if(this.rendered){
34194             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34195         }
34196         
34197         Roo.each(this.radioes, function(i){
34198             i.checked = false;
34199             i.el.removeClass('checked');
34200         });
34201         
34202         Roo.each(this.radioes, function(i){
34203             
34204             if(i.value === v || i.value.toString() === v.toString()){
34205                 i.checked = true;
34206                 i.el.addClass('checked');
34207                 
34208                 if(suppressEvent !== true){
34209                     this.fireEvent('check', this, i);
34210                 }
34211                 
34212                 return false;
34213             }
34214             
34215         }, this);
34216         
34217         this.validate();
34218     },
34219     
34220     clearInvalid : function(){
34221         
34222         if(!this.el || this.preventMark){
34223             return;
34224         }
34225         
34226         this.el.removeClass([this.invalidClass]);
34227         
34228         this.fireEvent('valid', this);
34229     }
34230     
34231 });
34232
34233 Roo.apply(Roo.bootstrap.RadioSet, {
34234     
34235     groups: {},
34236     
34237     register : function(set)
34238     {
34239         this.groups[set.name] = set;
34240     },
34241     
34242     get: function(name) 
34243     {
34244         if (typeof(this.groups[name]) == 'undefined') {
34245             return false;
34246         }
34247         
34248         return this.groups[name] ;
34249     }
34250     
34251 });
34252 /*
34253  * Based on:
34254  * Ext JS Library 1.1.1
34255  * Copyright(c) 2006-2007, Ext JS, LLC.
34256  *
34257  * Originally Released Under LGPL - original licence link has changed is not relivant.
34258  *
34259  * Fork - LGPL
34260  * <script type="text/javascript">
34261  */
34262
34263
34264 /**
34265  * @class Roo.bootstrap.SplitBar
34266  * @extends Roo.util.Observable
34267  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34268  * <br><br>
34269  * Usage:
34270  * <pre><code>
34271 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34272                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34273 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34274 split.minSize = 100;
34275 split.maxSize = 600;
34276 split.animate = true;
34277 split.on('moved', splitterMoved);
34278 </code></pre>
34279  * @constructor
34280  * Create a new SplitBar
34281  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34282  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34283  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34284  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34285                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34286                         position of the SplitBar).
34287  */
34288 Roo.bootstrap.SplitBar = function(cfg){
34289     
34290     /** @private */
34291     
34292     //{
34293     //  dragElement : elm
34294     //  resizingElement: el,
34295         // optional..
34296     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34297     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34298         // existingProxy ???
34299     //}
34300     
34301     this.el = Roo.get(cfg.dragElement, true);
34302     this.el.dom.unselectable = "on";
34303     /** @private */
34304     this.resizingEl = Roo.get(cfg.resizingElement, true);
34305
34306     /**
34307      * @private
34308      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34309      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34310      * @type Number
34311      */
34312     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34313     
34314     /**
34315      * The minimum size of the resizing element. (Defaults to 0)
34316      * @type Number
34317      */
34318     this.minSize = 0;
34319     
34320     /**
34321      * The maximum size of the resizing element. (Defaults to 2000)
34322      * @type Number
34323      */
34324     this.maxSize = 2000;
34325     
34326     /**
34327      * Whether to animate the transition to the new size
34328      * @type Boolean
34329      */
34330     this.animate = false;
34331     
34332     /**
34333      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34334      * @type Boolean
34335      */
34336     this.useShim = false;
34337     
34338     /** @private */
34339     this.shim = null;
34340     
34341     if(!cfg.existingProxy){
34342         /** @private */
34343         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34344     }else{
34345         this.proxy = Roo.get(cfg.existingProxy).dom;
34346     }
34347     /** @private */
34348     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34349     
34350     /** @private */
34351     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34352     
34353     /** @private */
34354     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34355     
34356     /** @private */
34357     this.dragSpecs = {};
34358     
34359     /**
34360      * @private The adapter to use to positon and resize elements
34361      */
34362     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34363     this.adapter.init(this);
34364     
34365     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34366         /** @private */
34367         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34368         this.el.addClass("roo-splitbar-h");
34369     }else{
34370         /** @private */
34371         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34372         this.el.addClass("roo-splitbar-v");
34373     }
34374     
34375     this.addEvents({
34376         /**
34377          * @event resize
34378          * Fires when the splitter is moved (alias for {@link #event-moved})
34379          * @param {Roo.bootstrap.SplitBar} this
34380          * @param {Number} newSize the new width or height
34381          */
34382         "resize" : true,
34383         /**
34384          * @event moved
34385          * Fires when the splitter is moved
34386          * @param {Roo.bootstrap.SplitBar} this
34387          * @param {Number} newSize the new width or height
34388          */
34389         "moved" : true,
34390         /**
34391          * @event beforeresize
34392          * Fires before the splitter is dragged
34393          * @param {Roo.bootstrap.SplitBar} this
34394          */
34395         "beforeresize" : true,
34396
34397         "beforeapply" : true
34398     });
34399
34400     Roo.util.Observable.call(this);
34401 };
34402
34403 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34404     onStartProxyDrag : function(x, y){
34405         this.fireEvent("beforeresize", this);
34406         if(!this.overlay){
34407             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34408             o.unselectable();
34409             o.enableDisplayMode("block");
34410             // all splitbars share the same overlay
34411             Roo.bootstrap.SplitBar.prototype.overlay = o;
34412         }
34413         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34414         this.overlay.show();
34415         Roo.get(this.proxy).setDisplayed("block");
34416         var size = this.adapter.getElementSize(this);
34417         this.activeMinSize = this.getMinimumSize();;
34418         this.activeMaxSize = this.getMaximumSize();;
34419         var c1 = size - this.activeMinSize;
34420         var c2 = Math.max(this.activeMaxSize - size, 0);
34421         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34422             this.dd.resetConstraints();
34423             this.dd.setXConstraint(
34424                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34425                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34426             );
34427             this.dd.setYConstraint(0, 0);
34428         }else{
34429             this.dd.resetConstraints();
34430             this.dd.setXConstraint(0, 0);
34431             this.dd.setYConstraint(
34432                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34433                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34434             );
34435          }
34436         this.dragSpecs.startSize = size;
34437         this.dragSpecs.startPoint = [x, y];
34438         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34439     },
34440     
34441     /** 
34442      * @private Called after the drag operation by the DDProxy
34443      */
34444     onEndProxyDrag : function(e){
34445         Roo.get(this.proxy).setDisplayed(false);
34446         var endPoint = Roo.lib.Event.getXY(e);
34447         if(this.overlay){
34448             this.overlay.hide();
34449         }
34450         var newSize;
34451         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34452             newSize = this.dragSpecs.startSize + 
34453                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34454                     endPoint[0] - this.dragSpecs.startPoint[0] :
34455                     this.dragSpecs.startPoint[0] - endPoint[0]
34456                 );
34457         }else{
34458             newSize = this.dragSpecs.startSize + 
34459                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34460                     endPoint[1] - this.dragSpecs.startPoint[1] :
34461                     this.dragSpecs.startPoint[1] - endPoint[1]
34462                 );
34463         }
34464         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34465         if(newSize != this.dragSpecs.startSize){
34466             if(this.fireEvent('beforeapply', this, newSize) !== false){
34467                 this.adapter.setElementSize(this, newSize);
34468                 this.fireEvent("moved", this, newSize);
34469                 this.fireEvent("resize", this, newSize);
34470             }
34471         }
34472     },
34473     
34474     /**
34475      * Get the adapter this SplitBar uses
34476      * @return The adapter object
34477      */
34478     getAdapter : function(){
34479         return this.adapter;
34480     },
34481     
34482     /**
34483      * Set the adapter this SplitBar uses
34484      * @param {Object} adapter A SplitBar adapter object
34485      */
34486     setAdapter : function(adapter){
34487         this.adapter = adapter;
34488         this.adapter.init(this);
34489     },
34490     
34491     /**
34492      * Gets the minimum size for the resizing element
34493      * @return {Number} The minimum size
34494      */
34495     getMinimumSize : function(){
34496         return this.minSize;
34497     },
34498     
34499     /**
34500      * Sets the minimum size for the resizing element
34501      * @param {Number} minSize The minimum size
34502      */
34503     setMinimumSize : function(minSize){
34504         this.minSize = minSize;
34505     },
34506     
34507     /**
34508      * Gets the maximum size for the resizing element
34509      * @return {Number} The maximum size
34510      */
34511     getMaximumSize : function(){
34512         return this.maxSize;
34513     },
34514     
34515     /**
34516      * Sets the maximum size for the resizing element
34517      * @param {Number} maxSize The maximum size
34518      */
34519     setMaximumSize : function(maxSize){
34520         this.maxSize = maxSize;
34521     },
34522     
34523     /**
34524      * Sets the initialize size for the resizing element
34525      * @param {Number} size The initial size
34526      */
34527     setCurrentSize : function(size){
34528         var oldAnimate = this.animate;
34529         this.animate = false;
34530         this.adapter.setElementSize(this, size);
34531         this.animate = oldAnimate;
34532     },
34533     
34534     /**
34535      * Destroy this splitbar. 
34536      * @param {Boolean} removeEl True to remove the element
34537      */
34538     destroy : function(removeEl){
34539         if(this.shim){
34540             this.shim.remove();
34541         }
34542         this.dd.unreg();
34543         this.proxy.parentNode.removeChild(this.proxy);
34544         if(removeEl){
34545             this.el.remove();
34546         }
34547     }
34548 });
34549
34550 /**
34551  * @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.
34552  */
34553 Roo.bootstrap.SplitBar.createProxy = function(dir){
34554     var proxy = new Roo.Element(document.createElement("div"));
34555     proxy.unselectable();
34556     var cls = 'roo-splitbar-proxy';
34557     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34558     document.body.appendChild(proxy.dom);
34559     return proxy.dom;
34560 };
34561
34562 /** 
34563  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34564  * Default Adapter. It assumes the splitter and resizing element are not positioned
34565  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34566  */
34567 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34568 };
34569
34570 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34571     // do nothing for now
34572     init : function(s){
34573     
34574     },
34575     /**
34576      * Called before drag operations to get the current size of the resizing element. 
34577      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34578      */
34579      getElementSize : function(s){
34580         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34581             return s.resizingEl.getWidth();
34582         }else{
34583             return s.resizingEl.getHeight();
34584         }
34585     },
34586     
34587     /**
34588      * Called after drag operations to set the size of the resizing element.
34589      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34590      * @param {Number} newSize The new size to set
34591      * @param {Function} onComplete A function to be invoked when resizing is complete
34592      */
34593     setElementSize : function(s, newSize, onComplete){
34594         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34595             if(!s.animate){
34596                 s.resizingEl.setWidth(newSize);
34597                 if(onComplete){
34598                     onComplete(s, newSize);
34599                 }
34600             }else{
34601                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34602             }
34603         }else{
34604             
34605             if(!s.animate){
34606                 s.resizingEl.setHeight(newSize);
34607                 if(onComplete){
34608                     onComplete(s, newSize);
34609                 }
34610             }else{
34611                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34612             }
34613         }
34614     }
34615 };
34616
34617 /** 
34618  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34619  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34620  * Adapter that  moves the splitter element to align with the resized sizing element. 
34621  * Used with an absolute positioned SplitBar.
34622  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34623  * document.body, make sure you assign an id to the body element.
34624  */
34625 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34626     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34627     this.container = Roo.get(container);
34628 };
34629
34630 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34631     init : function(s){
34632         this.basic.init(s);
34633     },
34634     
34635     getElementSize : function(s){
34636         return this.basic.getElementSize(s);
34637     },
34638     
34639     setElementSize : function(s, newSize, onComplete){
34640         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34641     },
34642     
34643     moveSplitter : function(s){
34644         var yes = Roo.bootstrap.SplitBar;
34645         switch(s.placement){
34646             case yes.LEFT:
34647                 s.el.setX(s.resizingEl.getRight());
34648                 break;
34649             case yes.RIGHT:
34650                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34651                 break;
34652             case yes.TOP:
34653                 s.el.setY(s.resizingEl.getBottom());
34654                 break;
34655             case yes.BOTTOM:
34656                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34657                 break;
34658         }
34659     }
34660 };
34661
34662 /**
34663  * Orientation constant - Create a vertical SplitBar
34664  * @static
34665  * @type Number
34666  */
34667 Roo.bootstrap.SplitBar.VERTICAL = 1;
34668
34669 /**
34670  * Orientation constant - Create a horizontal SplitBar
34671  * @static
34672  * @type Number
34673  */
34674 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34675
34676 /**
34677  * Placement constant - The resizing element is to the left of the splitter element
34678  * @static
34679  * @type Number
34680  */
34681 Roo.bootstrap.SplitBar.LEFT = 1;
34682
34683 /**
34684  * Placement constant - The resizing element is to the right of the splitter element
34685  * @static
34686  * @type Number
34687  */
34688 Roo.bootstrap.SplitBar.RIGHT = 2;
34689
34690 /**
34691  * Placement constant - The resizing element is positioned above the splitter element
34692  * @static
34693  * @type Number
34694  */
34695 Roo.bootstrap.SplitBar.TOP = 3;
34696
34697 /**
34698  * Placement constant - The resizing element is positioned under splitter element
34699  * @static
34700  * @type Number
34701  */
34702 Roo.bootstrap.SplitBar.BOTTOM = 4;
34703 Roo.namespace("Roo.bootstrap.layout");/*
34704  * Based on:
34705  * Ext JS Library 1.1.1
34706  * Copyright(c) 2006-2007, Ext JS, LLC.
34707  *
34708  * Originally Released Under LGPL - original licence link has changed is not relivant.
34709  *
34710  * Fork - LGPL
34711  * <script type="text/javascript">
34712  */
34713
34714 /**
34715  * @class Roo.bootstrap.layout.Manager
34716  * @extends Roo.bootstrap.Component
34717  * Base class for layout managers.
34718  */
34719 Roo.bootstrap.layout.Manager = function(config)
34720 {
34721     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34722
34723
34724
34725
34726
34727     /** false to disable window resize monitoring @type Boolean */
34728     this.monitorWindowResize = true;
34729     this.regions = {};
34730     this.addEvents({
34731         /**
34732          * @event layout
34733          * Fires when a layout is performed.
34734          * @param {Roo.LayoutManager} this
34735          */
34736         "layout" : true,
34737         /**
34738          * @event regionresized
34739          * Fires when the user resizes a region.
34740          * @param {Roo.LayoutRegion} region The resized region
34741          * @param {Number} newSize The new size (width for east/west, height for north/south)
34742          */
34743         "regionresized" : true,
34744         /**
34745          * @event regioncollapsed
34746          * Fires when a region is collapsed.
34747          * @param {Roo.LayoutRegion} region The collapsed region
34748          */
34749         "regioncollapsed" : true,
34750         /**
34751          * @event regionexpanded
34752          * Fires when a region is expanded.
34753          * @param {Roo.LayoutRegion} region The expanded region
34754          */
34755         "regionexpanded" : true
34756     });
34757     this.updating = false;
34758
34759     if (config.el) {
34760         this.el = Roo.get(config.el);
34761         this.initEvents();
34762     }
34763
34764 };
34765
34766 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34767
34768
34769     regions : null,
34770
34771     monitorWindowResize : true,
34772
34773
34774     updating : false,
34775
34776
34777     onRender : function(ct, position)
34778     {
34779         if(!this.el){
34780             this.el = Roo.get(ct);
34781             this.initEvents();
34782         }
34783         //this.fireEvent('render',this);
34784     },
34785
34786
34787     initEvents: function()
34788     {
34789
34790
34791         // ie scrollbar fix
34792         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34793             document.body.scroll = "no";
34794         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34795             this.el.position('relative');
34796         }
34797         this.id = this.el.id;
34798         this.el.addClass("roo-layout-container");
34799         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34800         if(this.el.dom != document.body ) {
34801             this.el.on('resize', this.layout,this);
34802             this.el.on('show', this.layout,this);
34803         }
34804
34805     },
34806
34807     /**
34808      * Returns true if this layout is currently being updated
34809      * @return {Boolean}
34810      */
34811     isUpdating : function(){
34812         return this.updating;
34813     },
34814
34815     /**
34816      * Suspend the LayoutManager from doing auto-layouts while
34817      * making multiple add or remove calls
34818      */
34819     beginUpdate : function(){
34820         this.updating = true;
34821     },
34822
34823     /**
34824      * Restore auto-layouts and optionally disable the manager from performing a layout
34825      * @param {Boolean} noLayout true to disable a layout update
34826      */
34827     endUpdate : function(noLayout){
34828         this.updating = false;
34829         if(!noLayout){
34830             this.layout();
34831         }
34832     },
34833
34834     layout: function(){
34835         // abstract...
34836     },
34837
34838     onRegionResized : function(region, newSize){
34839         this.fireEvent("regionresized", region, newSize);
34840         this.layout();
34841     },
34842
34843     onRegionCollapsed : function(region){
34844         this.fireEvent("regioncollapsed", region);
34845     },
34846
34847     onRegionExpanded : function(region){
34848         this.fireEvent("regionexpanded", region);
34849     },
34850
34851     /**
34852      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34853      * performs box-model adjustments.
34854      * @return {Object} The size as an object {width: (the width), height: (the height)}
34855      */
34856     getViewSize : function()
34857     {
34858         var size;
34859         if(this.el.dom != document.body){
34860             size = this.el.getSize();
34861         }else{
34862             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34863         }
34864         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34865         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34866         return size;
34867     },
34868
34869     /**
34870      * Returns the Element this layout is bound to.
34871      * @return {Roo.Element}
34872      */
34873     getEl : function(){
34874         return this.el;
34875     },
34876
34877     /**
34878      * Returns the specified region.
34879      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34880      * @return {Roo.LayoutRegion}
34881      */
34882     getRegion : function(target){
34883         return this.regions[target.toLowerCase()];
34884     },
34885
34886     onWindowResize : function(){
34887         if(this.monitorWindowResize){
34888             this.layout();
34889         }
34890     }
34891 });
34892 /*
34893  * Based on:
34894  * Ext JS Library 1.1.1
34895  * Copyright(c) 2006-2007, Ext JS, LLC.
34896  *
34897  * Originally Released Under LGPL - original licence link has changed is not relivant.
34898  *
34899  * Fork - LGPL
34900  * <script type="text/javascript">
34901  */
34902 /**
34903  * @class Roo.bootstrap.layout.Border
34904  * @extends Roo.bootstrap.layout.Manager
34905  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34906  * please see: examples/bootstrap/nested.html<br><br>
34907  
34908 <b>The container the layout is rendered into can be either the body element or any other element.
34909 If it is not the body element, the container needs to either be an absolute positioned element,
34910 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34911 the container size if it is not the body element.</b>
34912
34913 * @constructor
34914 * Create a new Border
34915 * @param {Object} config Configuration options
34916  */
34917 Roo.bootstrap.layout.Border = function(config){
34918     config = config || {};
34919     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34920     
34921     
34922     
34923     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34924         if(config[region]){
34925             config[region].region = region;
34926             this.addRegion(config[region]);
34927         }
34928     },this);
34929     
34930 };
34931
34932 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34933
34934 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34935     /**
34936      * Creates and adds a new region if it doesn't already exist.
34937      * @param {String} target The target region key (north, south, east, west or center).
34938      * @param {Object} config The regions config object
34939      * @return {BorderLayoutRegion} The new region
34940      */
34941     addRegion : function(config)
34942     {
34943         if(!this.regions[config.region]){
34944             var r = this.factory(config);
34945             this.bindRegion(r);
34946         }
34947         return this.regions[config.region];
34948     },
34949
34950     // private (kinda)
34951     bindRegion : function(r){
34952         this.regions[r.config.region] = r;
34953         
34954         r.on("visibilitychange",    this.layout, this);
34955         r.on("paneladded",          this.layout, this);
34956         r.on("panelremoved",        this.layout, this);
34957         r.on("invalidated",         this.layout, this);
34958         r.on("resized",             this.onRegionResized, this);
34959         r.on("collapsed",           this.onRegionCollapsed, this);
34960         r.on("expanded",            this.onRegionExpanded, this);
34961     },
34962
34963     /**
34964      * Performs a layout update.
34965      */
34966     layout : function()
34967     {
34968         if(this.updating) {
34969             return;
34970         }
34971         
34972         // render all the rebions if they have not been done alreayd?
34973         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34974             if(this.regions[region] && !this.regions[region].bodyEl){
34975                 this.regions[region].onRender(this.el)
34976             }
34977         },this);
34978         
34979         var size = this.getViewSize();
34980         var w = size.width;
34981         var h = size.height;
34982         var centerW = w;
34983         var centerH = h;
34984         var centerY = 0;
34985         var centerX = 0;
34986         //var x = 0, y = 0;
34987
34988         var rs = this.regions;
34989         var north = rs["north"];
34990         var south = rs["south"]; 
34991         var west = rs["west"];
34992         var east = rs["east"];
34993         var center = rs["center"];
34994         //if(this.hideOnLayout){ // not supported anymore
34995             //c.el.setStyle("display", "none");
34996         //}
34997         if(north && north.isVisible()){
34998             var b = north.getBox();
34999             var m = north.getMargins();
35000             b.width = w - (m.left+m.right);
35001             b.x = m.left;
35002             b.y = m.top;
35003             centerY = b.height + b.y + m.bottom;
35004             centerH -= centerY;
35005             north.updateBox(this.safeBox(b));
35006         }
35007         if(south && south.isVisible()){
35008             var b = south.getBox();
35009             var m = south.getMargins();
35010             b.width = w - (m.left+m.right);
35011             b.x = m.left;
35012             var totalHeight = (b.height + m.top + m.bottom);
35013             b.y = h - totalHeight + m.top;
35014             centerH -= totalHeight;
35015             south.updateBox(this.safeBox(b));
35016         }
35017         if(west && west.isVisible()){
35018             var b = west.getBox();
35019             var m = west.getMargins();
35020             b.height = centerH - (m.top+m.bottom);
35021             b.x = m.left;
35022             b.y = centerY + m.top;
35023             var totalWidth = (b.width + m.left + m.right);
35024             centerX += totalWidth;
35025             centerW -= totalWidth;
35026             west.updateBox(this.safeBox(b));
35027         }
35028         if(east && east.isVisible()){
35029             var b = east.getBox();
35030             var m = east.getMargins();
35031             b.height = centerH - (m.top+m.bottom);
35032             var totalWidth = (b.width + m.left + m.right);
35033             b.x = w - totalWidth + m.left;
35034             b.y = centerY + m.top;
35035             centerW -= totalWidth;
35036             east.updateBox(this.safeBox(b));
35037         }
35038         if(center){
35039             var m = center.getMargins();
35040             var centerBox = {
35041                 x: centerX + m.left,
35042                 y: centerY + m.top,
35043                 width: centerW - (m.left+m.right),
35044                 height: centerH - (m.top+m.bottom)
35045             };
35046             //if(this.hideOnLayout){
35047                 //center.el.setStyle("display", "block");
35048             //}
35049             center.updateBox(this.safeBox(centerBox));
35050         }
35051         this.el.repaint();
35052         this.fireEvent("layout", this);
35053     },
35054
35055     // private
35056     safeBox : function(box){
35057         box.width = Math.max(0, box.width);
35058         box.height = Math.max(0, box.height);
35059         return box;
35060     },
35061
35062     /**
35063      * Adds a ContentPanel (or subclass) to this layout.
35064      * @param {String} target The target region key (north, south, east, west or center).
35065      * @param {Roo.ContentPanel} panel The panel to add
35066      * @return {Roo.ContentPanel} The added panel
35067      */
35068     add : function(target, panel){
35069          
35070         target = target.toLowerCase();
35071         return this.regions[target].add(panel);
35072     },
35073
35074     /**
35075      * Remove a ContentPanel (or subclass) to this layout.
35076      * @param {String} target The target region key (north, south, east, west or center).
35077      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35078      * @return {Roo.ContentPanel} The removed panel
35079      */
35080     remove : function(target, panel){
35081         target = target.toLowerCase();
35082         return this.regions[target].remove(panel);
35083     },
35084
35085     /**
35086      * Searches all regions for a panel with the specified id
35087      * @param {String} panelId
35088      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35089      */
35090     findPanel : function(panelId){
35091         var rs = this.regions;
35092         for(var target in rs){
35093             if(typeof rs[target] != "function"){
35094                 var p = rs[target].getPanel(panelId);
35095                 if(p){
35096                     return p;
35097                 }
35098             }
35099         }
35100         return null;
35101     },
35102
35103     /**
35104      * Searches all regions for a panel with the specified id and activates (shows) it.
35105      * @param {String/ContentPanel} panelId The panels id or the panel itself
35106      * @return {Roo.ContentPanel} The shown panel or null
35107      */
35108     showPanel : function(panelId) {
35109       var rs = this.regions;
35110       for(var target in rs){
35111          var r = rs[target];
35112          if(typeof r != "function"){
35113             if(r.hasPanel(panelId)){
35114                return r.showPanel(panelId);
35115             }
35116          }
35117       }
35118       return null;
35119    },
35120
35121    /**
35122      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35123      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35124      */
35125    /*
35126     restoreState : function(provider){
35127         if(!provider){
35128             provider = Roo.state.Manager;
35129         }
35130         var sm = new Roo.LayoutStateManager();
35131         sm.init(this, provider);
35132     },
35133 */
35134  
35135  
35136     /**
35137      * Adds a xtype elements to the layout.
35138      * <pre><code>
35139
35140 layout.addxtype({
35141        xtype : 'ContentPanel',
35142        region: 'west',
35143        items: [ .... ]
35144    }
35145 );
35146
35147 layout.addxtype({
35148         xtype : 'NestedLayoutPanel',
35149         region: 'west',
35150         layout: {
35151            center: { },
35152            west: { }   
35153         },
35154         items : [ ... list of content panels or nested layout panels.. ]
35155    }
35156 );
35157 </code></pre>
35158      * @param {Object} cfg Xtype definition of item to add.
35159      */
35160     addxtype : function(cfg)
35161     {
35162         // basically accepts a pannel...
35163         // can accept a layout region..!?!?
35164         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35165         
35166         
35167         // theory?  children can only be panels??
35168         
35169         //if (!cfg.xtype.match(/Panel$/)) {
35170         //    return false;
35171         //}
35172         var ret = false;
35173         
35174         if (typeof(cfg.region) == 'undefined') {
35175             Roo.log("Failed to add Panel, region was not set");
35176             Roo.log(cfg);
35177             return false;
35178         }
35179         var region = cfg.region;
35180         delete cfg.region;
35181         
35182           
35183         var xitems = [];
35184         if (cfg.items) {
35185             xitems = cfg.items;
35186             delete cfg.items;
35187         }
35188         var nb = false;
35189         
35190         switch(cfg.xtype) 
35191         {
35192             case 'Content':  // ContentPanel (el, cfg)
35193             case 'Scroll':  // ContentPanel (el, cfg)
35194             case 'View': 
35195                 cfg.autoCreate = true;
35196                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35197                 //} else {
35198                 //    var el = this.el.createChild();
35199                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35200                 //}
35201                 
35202                 this.add(region, ret);
35203                 break;
35204             
35205             /*
35206             case 'TreePanel': // our new panel!
35207                 cfg.el = this.el.createChild();
35208                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35209                 this.add(region, ret);
35210                 break;
35211             */
35212             
35213             case 'Nest': 
35214                 // create a new Layout (which is  a Border Layout...
35215                 
35216                 var clayout = cfg.layout;
35217                 clayout.el  = this.el.createChild();
35218                 clayout.items   = clayout.items  || [];
35219                 
35220                 delete cfg.layout;
35221                 
35222                 // replace this exitems with the clayout ones..
35223                 xitems = clayout.items;
35224                  
35225                 // force background off if it's in center...
35226                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35227                     cfg.background = false;
35228                 }
35229                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35230                 
35231                 
35232                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35233                 //console.log('adding nested layout panel '  + cfg.toSource());
35234                 this.add(region, ret);
35235                 nb = {}; /// find first...
35236                 break;
35237             
35238             case 'Grid':
35239                 
35240                 // needs grid and region
35241                 
35242                 //var el = this.getRegion(region).el.createChild();
35243                 /*
35244                  *var el = this.el.createChild();
35245                 // create the grid first...
35246                 cfg.grid.container = el;
35247                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35248                 */
35249                 
35250                 if (region == 'center' && this.active ) {
35251                     cfg.background = false;
35252                 }
35253                 
35254                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35255                 
35256                 this.add(region, ret);
35257                 /*
35258                 if (cfg.background) {
35259                     // render grid on panel activation (if panel background)
35260                     ret.on('activate', function(gp) {
35261                         if (!gp.grid.rendered) {
35262                     //        gp.grid.render(el);
35263                         }
35264                     });
35265                 } else {
35266                   //  cfg.grid.render(el);
35267                 }
35268                 */
35269                 break;
35270            
35271            
35272             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35273                 // it was the old xcomponent building that caused this before.
35274                 // espeically if border is the top element in the tree.
35275                 ret = this;
35276                 break; 
35277                 
35278                     
35279                 
35280                 
35281                 
35282             default:
35283                 /*
35284                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35285                     
35286                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35287                     this.add(region, ret);
35288                 } else {
35289                 */
35290                     Roo.log(cfg);
35291                     throw "Can not add '" + cfg.xtype + "' to Border";
35292                     return null;
35293              
35294                                 
35295              
35296         }
35297         this.beginUpdate();
35298         // add children..
35299         var region = '';
35300         var abn = {};
35301         Roo.each(xitems, function(i)  {
35302             region = nb && i.region ? i.region : false;
35303             
35304             var add = ret.addxtype(i);
35305            
35306             if (region) {
35307                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35308                 if (!i.background) {
35309                     abn[region] = nb[region] ;
35310                 }
35311             }
35312             
35313         });
35314         this.endUpdate();
35315
35316         // make the last non-background panel active..
35317         //if (nb) { Roo.log(abn); }
35318         if (nb) {
35319             
35320             for(var r in abn) {
35321                 region = this.getRegion(r);
35322                 if (region) {
35323                     // tried using nb[r], but it does not work..
35324                      
35325                     region.showPanel(abn[r]);
35326                    
35327                 }
35328             }
35329         }
35330         return ret;
35331         
35332     },
35333     
35334     
35335 // private
35336     factory : function(cfg)
35337     {
35338         
35339         var validRegions = Roo.bootstrap.layout.Border.regions;
35340
35341         var target = cfg.region;
35342         cfg.mgr = this;
35343         
35344         var r = Roo.bootstrap.layout;
35345         Roo.log(target);
35346         switch(target){
35347             case "north":
35348                 return new r.North(cfg);
35349             case "south":
35350                 return new r.South(cfg);
35351             case "east":
35352                 return new r.East(cfg);
35353             case "west":
35354                 return new r.West(cfg);
35355             case "center":
35356                 return new r.Center(cfg);
35357         }
35358         throw 'Layout region "'+target+'" not supported.';
35359     }
35360     
35361     
35362 });
35363  /*
35364  * Based on:
35365  * Ext JS Library 1.1.1
35366  * Copyright(c) 2006-2007, Ext JS, LLC.
35367  *
35368  * Originally Released Under LGPL - original licence link has changed is not relivant.
35369  *
35370  * Fork - LGPL
35371  * <script type="text/javascript">
35372  */
35373  
35374 /**
35375  * @class Roo.bootstrap.layout.Basic
35376  * @extends Roo.util.Observable
35377  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35378  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35379  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35380  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35381  * @cfg {string}   region  the region that it inhabits..
35382  * @cfg {bool}   skipConfig skip config?
35383  * 
35384
35385  */
35386 Roo.bootstrap.layout.Basic = function(config){
35387     
35388     this.mgr = config.mgr;
35389     
35390     this.position = config.region;
35391     
35392     var skipConfig = config.skipConfig;
35393     
35394     this.events = {
35395         /**
35396          * @scope Roo.BasicLayoutRegion
35397          */
35398         
35399         /**
35400          * @event beforeremove
35401          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35402          * @param {Roo.LayoutRegion} this
35403          * @param {Roo.ContentPanel} panel The panel
35404          * @param {Object} e The cancel event object
35405          */
35406         "beforeremove" : true,
35407         /**
35408          * @event invalidated
35409          * Fires when the layout for this region is changed.
35410          * @param {Roo.LayoutRegion} this
35411          */
35412         "invalidated" : true,
35413         /**
35414          * @event visibilitychange
35415          * Fires when this region is shown or hidden 
35416          * @param {Roo.LayoutRegion} this
35417          * @param {Boolean} visibility true or false
35418          */
35419         "visibilitychange" : true,
35420         /**
35421          * @event paneladded
35422          * Fires when a panel is added. 
35423          * @param {Roo.LayoutRegion} this
35424          * @param {Roo.ContentPanel} panel The panel
35425          */
35426         "paneladded" : true,
35427         /**
35428          * @event panelremoved
35429          * Fires when a panel is removed. 
35430          * @param {Roo.LayoutRegion} this
35431          * @param {Roo.ContentPanel} panel The panel
35432          */
35433         "panelremoved" : true,
35434         /**
35435          * @event beforecollapse
35436          * Fires when this region before collapse.
35437          * @param {Roo.LayoutRegion} this
35438          */
35439         "beforecollapse" : true,
35440         /**
35441          * @event collapsed
35442          * Fires when this region is collapsed.
35443          * @param {Roo.LayoutRegion} this
35444          */
35445         "collapsed" : true,
35446         /**
35447          * @event expanded
35448          * Fires when this region is expanded.
35449          * @param {Roo.LayoutRegion} this
35450          */
35451         "expanded" : true,
35452         /**
35453          * @event slideshow
35454          * Fires when this region is slid into view.
35455          * @param {Roo.LayoutRegion} this
35456          */
35457         "slideshow" : true,
35458         /**
35459          * @event slidehide
35460          * Fires when this region slides out of view. 
35461          * @param {Roo.LayoutRegion} this
35462          */
35463         "slidehide" : true,
35464         /**
35465          * @event panelactivated
35466          * Fires when a panel is activated. 
35467          * @param {Roo.LayoutRegion} this
35468          * @param {Roo.ContentPanel} panel The activated panel
35469          */
35470         "panelactivated" : true,
35471         /**
35472          * @event resized
35473          * Fires when the user resizes this region. 
35474          * @param {Roo.LayoutRegion} this
35475          * @param {Number} newSize The new size (width for east/west, height for north/south)
35476          */
35477         "resized" : true
35478     };
35479     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35480     this.panels = new Roo.util.MixedCollection();
35481     this.panels.getKey = this.getPanelId.createDelegate(this);
35482     this.box = null;
35483     this.activePanel = null;
35484     // ensure listeners are added...
35485     
35486     if (config.listeners || config.events) {
35487         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35488             listeners : config.listeners || {},
35489             events : config.events || {}
35490         });
35491     }
35492     
35493     if(skipConfig !== true){
35494         this.applyConfig(config);
35495     }
35496 };
35497
35498 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35499 {
35500     getPanelId : function(p){
35501         return p.getId();
35502     },
35503     
35504     applyConfig : function(config){
35505         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35506         this.config = config;
35507         
35508     },
35509     
35510     /**
35511      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35512      * the width, for horizontal (north, south) the height.
35513      * @param {Number} newSize The new width or height
35514      */
35515     resizeTo : function(newSize){
35516         var el = this.el ? this.el :
35517                  (this.activePanel ? this.activePanel.getEl() : null);
35518         if(el){
35519             switch(this.position){
35520                 case "east":
35521                 case "west":
35522                     el.setWidth(newSize);
35523                     this.fireEvent("resized", this, newSize);
35524                 break;
35525                 case "north":
35526                 case "south":
35527                     el.setHeight(newSize);
35528                     this.fireEvent("resized", this, newSize);
35529                 break;                
35530             }
35531         }
35532     },
35533     
35534     getBox : function(){
35535         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35536     },
35537     
35538     getMargins : function(){
35539         return this.margins;
35540     },
35541     
35542     updateBox : function(box){
35543         this.box = box;
35544         var el = this.activePanel.getEl();
35545         el.dom.style.left = box.x + "px";
35546         el.dom.style.top = box.y + "px";
35547         this.activePanel.setSize(box.width, box.height);
35548     },
35549     
35550     /**
35551      * Returns the container element for this region.
35552      * @return {Roo.Element}
35553      */
35554     getEl : function(){
35555         return this.activePanel;
35556     },
35557     
35558     /**
35559      * Returns true if this region is currently visible.
35560      * @return {Boolean}
35561      */
35562     isVisible : function(){
35563         return this.activePanel ? true : false;
35564     },
35565     
35566     setActivePanel : function(panel){
35567         panel = this.getPanel(panel);
35568         if(this.activePanel && this.activePanel != panel){
35569             this.activePanel.setActiveState(false);
35570             this.activePanel.getEl().setLeftTop(-10000,-10000);
35571         }
35572         this.activePanel = panel;
35573         panel.setActiveState(true);
35574         if(this.box){
35575             panel.setSize(this.box.width, this.box.height);
35576         }
35577         this.fireEvent("panelactivated", this, panel);
35578         this.fireEvent("invalidated");
35579     },
35580     
35581     /**
35582      * Show the specified panel.
35583      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35584      * @return {Roo.ContentPanel} The shown panel or null
35585      */
35586     showPanel : function(panel){
35587         panel = this.getPanel(panel);
35588         if(panel){
35589             this.setActivePanel(panel);
35590         }
35591         return panel;
35592     },
35593     
35594     /**
35595      * Get the active panel for this region.
35596      * @return {Roo.ContentPanel} The active panel or null
35597      */
35598     getActivePanel : function(){
35599         return this.activePanel;
35600     },
35601     
35602     /**
35603      * Add the passed ContentPanel(s)
35604      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35605      * @return {Roo.ContentPanel} The panel added (if only one was added)
35606      */
35607     add : function(panel){
35608         if(arguments.length > 1){
35609             for(var i = 0, len = arguments.length; i < len; i++) {
35610                 this.add(arguments[i]);
35611             }
35612             return null;
35613         }
35614         if(this.hasPanel(panel)){
35615             this.showPanel(panel);
35616             return panel;
35617         }
35618         var el = panel.getEl();
35619         if(el.dom.parentNode != this.mgr.el.dom){
35620             this.mgr.el.dom.appendChild(el.dom);
35621         }
35622         if(panel.setRegion){
35623             panel.setRegion(this);
35624         }
35625         this.panels.add(panel);
35626         el.setStyle("position", "absolute");
35627         if(!panel.background){
35628             this.setActivePanel(panel);
35629             if(this.config.initialSize && this.panels.getCount()==1){
35630                 this.resizeTo(this.config.initialSize);
35631             }
35632         }
35633         this.fireEvent("paneladded", this, panel);
35634         return panel;
35635     },
35636     
35637     /**
35638      * Returns true if the panel is in this region.
35639      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35640      * @return {Boolean}
35641      */
35642     hasPanel : function(panel){
35643         if(typeof panel == "object"){ // must be panel obj
35644             panel = panel.getId();
35645         }
35646         return this.getPanel(panel) ? true : false;
35647     },
35648     
35649     /**
35650      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35651      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35652      * @param {Boolean} preservePanel Overrides the config preservePanel option
35653      * @return {Roo.ContentPanel} The panel that was removed
35654      */
35655     remove : function(panel, preservePanel){
35656         panel = this.getPanel(panel);
35657         if(!panel){
35658             return null;
35659         }
35660         var e = {};
35661         this.fireEvent("beforeremove", this, panel, e);
35662         if(e.cancel === true){
35663             return null;
35664         }
35665         var panelId = panel.getId();
35666         this.panels.removeKey(panelId);
35667         return panel;
35668     },
35669     
35670     /**
35671      * Returns the panel specified or null if it's not in this region.
35672      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35673      * @return {Roo.ContentPanel}
35674      */
35675     getPanel : function(id){
35676         if(typeof id == "object"){ // must be panel obj
35677             return id;
35678         }
35679         return this.panels.get(id);
35680     },
35681     
35682     /**
35683      * Returns this regions position (north/south/east/west/center).
35684      * @return {String} 
35685      */
35686     getPosition: function(){
35687         return this.position;    
35688     }
35689 });/*
35690  * Based on:
35691  * Ext JS Library 1.1.1
35692  * Copyright(c) 2006-2007, Ext JS, LLC.
35693  *
35694  * Originally Released Under LGPL - original licence link has changed is not relivant.
35695  *
35696  * Fork - LGPL
35697  * <script type="text/javascript">
35698  */
35699  
35700 /**
35701  * @class Roo.bootstrap.layout.Region
35702  * @extends Roo.bootstrap.layout.Basic
35703  * This class represents a region in a layout manager.
35704  
35705  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35706  * @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})
35707  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35708  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35709  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35710  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35711  * @cfg {String}    title           The title for the region (overrides panel titles)
35712  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35713  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35714  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35715  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35716  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35717  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35718  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35719  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35720  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35721  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35722
35723  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35724  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35725  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35726  * @cfg {Number}    width           For East/West panels
35727  * @cfg {Number}    height          For North/South panels
35728  * @cfg {Boolean}   split           To show the splitter
35729  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35730  * 
35731  * @cfg {string}   cls             Extra CSS classes to add to region
35732  * 
35733  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35734  * @cfg {string}   region  the region that it inhabits..
35735  *
35736
35737  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35738  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35739
35740  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35741  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35742  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35743  */
35744 Roo.bootstrap.layout.Region = function(config)
35745 {
35746     this.applyConfig(config);
35747
35748     var mgr = config.mgr;
35749     var pos = config.region;
35750     config.skipConfig = true;
35751     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35752     
35753     if (mgr.el) {
35754         this.onRender(mgr.el);   
35755     }
35756      
35757     this.visible = true;
35758     this.collapsed = false;
35759     this.unrendered_panels = [];
35760 };
35761
35762 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35763
35764     position: '', // set by wrapper (eg. north/south etc..)
35765     unrendered_panels : null,  // unrendered panels.
35766     createBody : function(){
35767         /** This region's body element 
35768         * @type Roo.Element */
35769         this.bodyEl = this.el.createChild({
35770                 tag: "div",
35771                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35772         });
35773     },
35774
35775     onRender: function(ctr, pos)
35776     {
35777         var dh = Roo.DomHelper;
35778         /** This region's container element 
35779         * @type Roo.Element */
35780         this.el = dh.append(ctr.dom, {
35781                 tag: "div",
35782                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35783             }, true);
35784         /** This region's title element 
35785         * @type Roo.Element */
35786     
35787         this.titleEl = dh.append(this.el.dom,
35788             {
35789                     tag: "div",
35790                     unselectable: "on",
35791                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35792                     children:[
35793                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35794                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35795                     ]}, true);
35796         
35797         this.titleEl.enableDisplayMode();
35798         /** This region's title text element 
35799         * @type HTMLElement */
35800         this.titleTextEl = this.titleEl.dom.firstChild;
35801         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35802         /*
35803         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35804         this.closeBtn.enableDisplayMode();
35805         this.closeBtn.on("click", this.closeClicked, this);
35806         this.closeBtn.hide();
35807     */
35808         this.createBody(this.config);
35809         if(this.config.hideWhenEmpty){
35810             this.hide();
35811             this.on("paneladded", this.validateVisibility, this);
35812             this.on("panelremoved", this.validateVisibility, this);
35813         }
35814         if(this.autoScroll){
35815             this.bodyEl.setStyle("overflow", "auto");
35816         }else{
35817             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35818         }
35819         //if(c.titlebar !== false){
35820             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35821                 this.titleEl.hide();
35822             }else{
35823                 this.titleEl.show();
35824                 if(this.config.title){
35825                     this.titleTextEl.innerHTML = this.config.title;
35826                 }
35827             }
35828         //}
35829         if(this.config.collapsed){
35830             this.collapse(true);
35831         }
35832         if(this.config.hidden){
35833             this.hide();
35834         }
35835         
35836         if (this.unrendered_panels && this.unrendered_panels.length) {
35837             for (var i =0;i< this.unrendered_panels.length; i++) {
35838                 this.add(this.unrendered_panels[i]);
35839             }
35840             this.unrendered_panels = null;
35841             
35842         }
35843         
35844     },
35845     
35846     applyConfig : function(c)
35847     {
35848         /*
35849          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35850             var dh = Roo.DomHelper;
35851             if(c.titlebar !== false){
35852                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35853                 this.collapseBtn.on("click", this.collapse, this);
35854                 this.collapseBtn.enableDisplayMode();
35855                 /*
35856                 if(c.showPin === true || this.showPin){
35857                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35858                     this.stickBtn.enableDisplayMode();
35859                     this.stickBtn.on("click", this.expand, this);
35860                     this.stickBtn.hide();
35861                 }
35862                 
35863             }
35864             */
35865             /** This region's collapsed element
35866             * @type Roo.Element */
35867             /*
35868              *
35869             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35870                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35871             ]}, true);
35872             
35873             if(c.floatable !== false){
35874                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35875                this.collapsedEl.on("click", this.collapseClick, this);
35876             }
35877
35878             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35879                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35880                    id: "message", unselectable: "on", style:{"float":"left"}});
35881                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35882              }
35883             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35884             this.expandBtn.on("click", this.expand, this);
35885             
35886         }
35887         
35888         if(this.collapseBtn){
35889             this.collapseBtn.setVisible(c.collapsible == true);
35890         }
35891         
35892         this.cmargins = c.cmargins || this.cmargins ||
35893                          (this.position == "west" || this.position == "east" ?
35894                              {top: 0, left: 2, right:2, bottom: 0} :
35895                              {top: 2, left: 0, right:0, bottom: 2});
35896         */
35897         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35898         
35899         
35900         this.bottomTabs = c.tabPosition != "top";
35901         
35902         this.autoScroll = c.autoScroll || false;
35903         
35904         
35905        
35906         
35907         this.duration = c.duration || .30;
35908         this.slideDuration = c.slideDuration || .45;
35909         this.config = c;
35910        
35911     },
35912     /**
35913      * Returns true if this region is currently visible.
35914      * @return {Boolean}
35915      */
35916     isVisible : function(){
35917         return this.visible;
35918     },
35919
35920     /**
35921      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35922      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35923      */
35924     //setCollapsedTitle : function(title){
35925     //    title = title || "&#160;";
35926      //   if(this.collapsedTitleTextEl){
35927       //      this.collapsedTitleTextEl.innerHTML = title;
35928        // }
35929     //},
35930
35931     getBox : function(){
35932         var b;
35933       //  if(!this.collapsed){
35934             b = this.el.getBox(false, true);
35935        // }else{
35936           //  b = this.collapsedEl.getBox(false, true);
35937         //}
35938         return b;
35939     },
35940
35941     getMargins : function(){
35942         return this.margins;
35943         //return this.collapsed ? this.cmargins : this.margins;
35944     },
35945 /*
35946     highlight : function(){
35947         this.el.addClass("x-layout-panel-dragover");
35948     },
35949
35950     unhighlight : function(){
35951         this.el.removeClass("x-layout-panel-dragover");
35952     },
35953 */
35954     updateBox : function(box)
35955     {
35956         if (!this.bodyEl) {
35957             return; // not rendered yet..
35958         }
35959         
35960         this.box = box;
35961         if(!this.collapsed){
35962             this.el.dom.style.left = box.x + "px";
35963             this.el.dom.style.top = box.y + "px";
35964             this.updateBody(box.width, box.height);
35965         }else{
35966             this.collapsedEl.dom.style.left = box.x + "px";
35967             this.collapsedEl.dom.style.top = box.y + "px";
35968             this.collapsedEl.setSize(box.width, box.height);
35969         }
35970         if(this.tabs){
35971             this.tabs.autoSizeTabs();
35972         }
35973     },
35974
35975     updateBody : function(w, h)
35976     {
35977         if(w !== null){
35978             this.el.setWidth(w);
35979             w -= this.el.getBorderWidth("rl");
35980             if(this.config.adjustments){
35981                 w += this.config.adjustments[0];
35982             }
35983         }
35984         if(h !== null && h > 0){
35985             this.el.setHeight(h);
35986             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35987             h -= this.el.getBorderWidth("tb");
35988             if(this.config.adjustments){
35989                 h += this.config.adjustments[1];
35990             }
35991             this.bodyEl.setHeight(h);
35992             if(this.tabs){
35993                 h = this.tabs.syncHeight(h);
35994             }
35995         }
35996         if(this.panelSize){
35997             w = w !== null ? w : this.panelSize.width;
35998             h = h !== null ? h : this.panelSize.height;
35999         }
36000         if(this.activePanel){
36001             var el = this.activePanel.getEl();
36002             w = w !== null ? w : el.getWidth();
36003             h = h !== null ? h : el.getHeight();
36004             this.panelSize = {width: w, height: h};
36005             this.activePanel.setSize(w, h);
36006         }
36007         if(Roo.isIE && this.tabs){
36008             this.tabs.el.repaint();
36009         }
36010     },
36011
36012     /**
36013      * Returns the container element for this region.
36014      * @return {Roo.Element}
36015      */
36016     getEl : function(){
36017         return this.el;
36018     },
36019
36020     /**
36021      * Hides this region.
36022      */
36023     hide : function(){
36024         //if(!this.collapsed){
36025             this.el.dom.style.left = "-2000px";
36026             this.el.hide();
36027         //}else{
36028          //   this.collapsedEl.dom.style.left = "-2000px";
36029          //   this.collapsedEl.hide();
36030        // }
36031         this.visible = false;
36032         this.fireEvent("visibilitychange", this, false);
36033     },
36034
36035     /**
36036      * Shows this region if it was previously hidden.
36037      */
36038     show : function(){
36039         //if(!this.collapsed){
36040             this.el.show();
36041         //}else{
36042         //    this.collapsedEl.show();
36043        // }
36044         this.visible = true;
36045         this.fireEvent("visibilitychange", this, true);
36046     },
36047 /*
36048     closeClicked : function(){
36049         if(this.activePanel){
36050             this.remove(this.activePanel);
36051         }
36052     },
36053
36054     collapseClick : function(e){
36055         if(this.isSlid){
36056            e.stopPropagation();
36057            this.slideIn();
36058         }else{
36059            e.stopPropagation();
36060            this.slideOut();
36061         }
36062     },
36063 */
36064     /**
36065      * Collapses this region.
36066      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36067      */
36068     /*
36069     collapse : function(skipAnim, skipCheck = false){
36070         if(this.collapsed) {
36071             return;
36072         }
36073         
36074         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36075             
36076             this.collapsed = true;
36077             if(this.split){
36078                 this.split.el.hide();
36079             }
36080             if(this.config.animate && skipAnim !== true){
36081                 this.fireEvent("invalidated", this);
36082                 this.animateCollapse();
36083             }else{
36084                 this.el.setLocation(-20000,-20000);
36085                 this.el.hide();
36086                 this.collapsedEl.show();
36087                 this.fireEvent("collapsed", this);
36088                 this.fireEvent("invalidated", this);
36089             }
36090         }
36091         
36092     },
36093 */
36094     animateCollapse : function(){
36095         // overridden
36096     },
36097
36098     /**
36099      * Expands this region if it was previously collapsed.
36100      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36101      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36102      */
36103     /*
36104     expand : function(e, skipAnim){
36105         if(e) {
36106             e.stopPropagation();
36107         }
36108         if(!this.collapsed || this.el.hasActiveFx()) {
36109             return;
36110         }
36111         if(this.isSlid){
36112             this.afterSlideIn();
36113             skipAnim = true;
36114         }
36115         this.collapsed = false;
36116         if(this.config.animate && skipAnim !== true){
36117             this.animateExpand();
36118         }else{
36119             this.el.show();
36120             if(this.split){
36121                 this.split.el.show();
36122             }
36123             this.collapsedEl.setLocation(-2000,-2000);
36124             this.collapsedEl.hide();
36125             this.fireEvent("invalidated", this);
36126             this.fireEvent("expanded", this);
36127         }
36128     },
36129 */
36130     animateExpand : function(){
36131         // overridden
36132     },
36133
36134     initTabs : function()
36135     {
36136         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36137         
36138         var ts = new Roo.bootstrap.panel.Tabs({
36139                 el: this.bodyEl.dom,
36140                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36141                 disableTooltips: this.config.disableTabTips,
36142                 toolbar : this.config.toolbar
36143             });
36144         
36145         if(this.config.hideTabs){
36146             ts.stripWrap.setDisplayed(false);
36147         }
36148         this.tabs = ts;
36149         ts.resizeTabs = this.config.resizeTabs === true;
36150         ts.minTabWidth = this.config.minTabWidth || 40;
36151         ts.maxTabWidth = this.config.maxTabWidth || 250;
36152         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36153         ts.monitorResize = false;
36154         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36155         ts.bodyEl.addClass('roo-layout-tabs-body');
36156         this.panels.each(this.initPanelAsTab, this);
36157     },
36158
36159     initPanelAsTab : function(panel){
36160         var ti = this.tabs.addTab(
36161             panel.getEl().id,
36162             panel.getTitle(),
36163             null,
36164             this.config.closeOnTab && panel.isClosable(),
36165             panel.tpl
36166         );
36167         if(panel.tabTip !== undefined){
36168             ti.setTooltip(panel.tabTip);
36169         }
36170         ti.on("activate", function(){
36171               this.setActivePanel(panel);
36172         }, this);
36173         
36174         if(this.config.closeOnTab){
36175             ti.on("beforeclose", function(t, e){
36176                 e.cancel = true;
36177                 this.remove(panel);
36178             }, this);
36179         }
36180         
36181         panel.tabItem = ti;
36182         
36183         return ti;
36184     },
36185
36186     updatePanelTitle : function(panel, title)
36187     {
36188         if(this.activePanel == panel){
36189             this.updateTitle(title);
36190         }
36191         if(this.tabs){
36192             var ti = this.tabs.getTab(panel.getEl().id);
36193             ti.setText(title);
36194             if(panel.tabTip !== undefined){
36195                 ti.setTooltip(panel.tabTip);
36196             }
36197         }
36198     },
36199
36200     updateTitle : function(title){
36201         if(this.titleTextEl && !this.config.title){
36202             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36203         }
36204     },
36205
36206     setActivePanel : function(panel)
36207     {
36208         panel = this.getPanel(panel);
36209         if(this.activePanel && this.activePanel != panel){
36210             if(this.activePanel.setActiveState(false) === false){
36211                 return;
36212             }
36213         }
36214         this.activePanel = panel;
36215         panel.setActiveState(true);
36216         if(this.panelSize){
36217             panel.setSize(this.panelSize.width, this.panelSize.height);
36218         }
36219         if(this.closeBtn){
36220             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36221         }
36222         this.updateTitle(panel.getTitle());
36223         if(this.tabs){
36224             this.fireEvent("invalidated", this);
36225         }
36226         this.fireEvent("panelactivated", this, panel);
36227     },
36228
36229     /**
36230      * Shows the specified panel.
36231      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36232      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36233      */
36234     showPanel : function(panel)
36235     {
36236         panel = this.getPanel(panel);
36237         if(panel){
36238             if(this.tabs){
36239                 var tab = this.tabs.getTab(panel.getEl().id);
36240                 if(tab.isHidden()){
36241                     this.tabs.unhideTab(tab.id);
36242                 }
36243                 tab.activate();
36244             }else{
36245                 this.setActivePanel(panel);
36246             }
36247         }
36248         return panel;
36249     },
36250
36251     /**
36252      * Get the active panel for this region.
36253      * @return {Roo.ContentPanel} The active panel or null
36254      */
36255     getActivePanel : function(){
36256         return this.activePanel;
36257     },
36258
36259     validateVisibility : function(){
36260         if(this.panels.getCount() < 1){
36261             this.updateTitle("&#160;");
36262             this.closeBtn.hide();
36263             this.hide();
36264         }else{
36265             if(!this.isVisible()){
36266                 this.show();
36267             }
36268         }
36269     },
36270
36271     /**
36272      * Adds the passed ContentPanel(s) to this region.
36273      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36274      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36275      */
36276     add : function(panel)
36277     {
36278         if(arguments.length > 1){
36279             for(var i = 0, len = arguments.length; i < len; i++) {
36280                 this.add(arguments[i]);
36281             }
36282             return null;
36283         }
36284         
36285         // if we have not been rendered yet, then we can not really do much of this..
36286         if (!this.bodyEl) {
36287             this.unrendered_panels.push(panel);
36288             return panel;
36289         }
36290         
36291         
36292         
36293         
36294         if(this.hasPanel(panel)){
36295             this.showPanel(panel);
36296             return panel;
36297         }
36298         panel.setRegion(this);
36299         this.panels.add(panel);
36300        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36301             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36302             // and hide them... ???
36303             this.bodyEl.dom.appendChild(panel.getEl().dom);
36304             if(panel.background !== true){
36305                 this.setActivePanel(panel);
36306             }
36307             this.fireEvent("paneladded", this, panel);
36308             return panel;
36309         }
36310         */
36311         if(!this.tabs){
36312             this.initTabs();
36313         }else{
36314             this.initPanelAsTab(panel);
36315         }
36316         
36317         
36318         if(panel.background !== true){
36319             this.tabs.activate(panel.getEl().id);
36320         }
36321         this.fireEvent("paneladded", this, panel);
36322         return panel;
36323     },
36324
36325     /**
36326      * Hides the tab for the specified panel.
36327      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36328      */
36329     hidePanel : function(panel){
36330         if(this.tabs && (panel = this.getPanel(panel))){
36331             this.tabs.hideTab(panel.getEl().id);
36332         }
36333     },
36334
36335     /**
36336      * Unhides the tab for a previously hidden panel.
36337      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36338      */
36339     unhidePanel : function(panel){
36340         if(this.tabs && (panel = this.getPanel(panel))){
36341             this.tabs.unhideTab(panel.getEl().id);
36342         }
36343     },
36344
36345     clearPanels : function(){
36346         while(this.panels.getCount() > 0){
36347              this.remove(this.panels.first());
36348         }
36349     },
36350
36351     /**
36352      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36353      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36354      * @param {Boolean} preservePanel Overrides the config preservePanel option
36355      * @return {Roo.ContentPanel} The panel that was removed
36356      */
36357     remove : function(panel, preservePanel)
36358     {
36359         panel = this.getPanel(panel);
36360         if(!panel){
36361             return null;
36362         }
36363         var e = {};
36364         this.fireEvent("beforeremove", this, panel, e);
36365         if(e.cancel === true){
36366             return null;
36367         }
36368         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36369         var panelId = panel.getId();
36370         this.panels.removeKey(panelId);
36371         if(preservePanel){
36372             document.body.appendChild(panel.getEl().dom);
36373         }
36374         if(this.tabs){
36375             this.tabs.removeTab(panel.getEl().id);
36376         }else if (!preservePanel){
36377             this.bodyEl.dom.removeChild(panel.getEl().dom);
36378         }
36379         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36380             var p = this.panels.first();
36381             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36382             tempEl.appendChild(p.getEl().dom);
36383             this.bodyEl.update("");
36384             this.bodyEl.dom.appendChild(p.getEl().dom);
36385             tempEl = null;
36386             this.updateTitle(p.getTitle());
36387             this.tabs = null;
36388             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36389             this.setActivePanel(p);
36390         }
36391         panel.setRegion(null);
36392         if(this.activePanel == panel){
36393             this.activePanel = null;
36394         }
36395         if(this.config.autoDestroy !== false && preservePanel !== true){
36396             try{panel.destroy();}catch(e){}
36397         }
36398         this.fireEvent("panelremoved", this, panel);
36399         return panel;
36400     },
36401
36402     /**
36403      * Returns the TabPanel component used by this region
36404      * @return {Roo.TabPanel}
36405      */
36406     getTabs : function(){
36407         return this.tabs;
36408     },
36409
36410     createTool : function(parentEl, className){
36411         var btn = Roo.DomHelper.append(parentEl, {
36412             tag: "div",
36413             cls: "x-layout-tools-button",
36414             children: [ {
36415                 tag: "div",
36416                 cls: "roo-layout-tools-button-inner " + className,
36417                 html: "&#160;"
36418             }]
36419         }, true);
36420         btn.addClassOnOver("roo-layout-tools-button-over");
36421         return btn;
36422     }
36423 });/*
36424  * Based on:
36425  * Ext JS Library 1.1.1
36426  * Copyright(c) 2006-2007, Ext JS, LLC.
36427  *
36428  * Originally Released Under LGPL - original licence link has changed is not relivant.
36429  *
36430  * Fork - LGPL
36431  * <script type="text/javascript">
36432  */
36433  
36434
36435
36436 /**
36437  * @class Roo.SplitLayoutRegion
36438  * @extends Roo.LayoutRegion
36439  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36440  */
36441 Roo.bootstrap.layout.Split = function(config){
36442     this.cursor = config.cursor;
36443     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36444 };
36445
36446 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36447 {
36448     splitTip : "Drag to resize.",
36449     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36450     useSplitTips : false,
36451
36452     applyConfig : function(config){
36453         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36454     },
36455     
36456     onRender : function(ctr,pos) {
36457         
36458         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36459         if(!this.config.split){
36460             return;
36461         }
36462         if(!this.split){
36463             
36464             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36465                             tag: "div",
36466                             id: this.el.id + "-split",
36467                             cls: "roo-layout-split roo-layout-split-"+this.position,
36468                             html: "&#160;"
36469             });
36470             /** The SplitBar for this region 
36471             * @type Roo.SplitBar */
36472             // does not exist yet...
36473             Roo.log([this.position, this.orientation]);
36474             
36475             this.split = new Roo.bootstrap.SplitBar({
36476                 dragElement : splitEl,
36477                 resizingElement: this.el,
36478                 orientation : this.orientation
36479             });
36480             
36481             this.split.on("moved", this.onSplitMove, this);
36482             this.split.useShim = this.config.useShim === true;
36483             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36484             if(this.useSplitTips){
36485                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36486             }
36487             //if(config.collapsible){
36488             //    this.split.el.on("dblclick", this.collapse,  this);
36489             //}
36490         }
36491         if(typeof this.config.minSize != "undefined"){
36492             this.split.minSize = this.config.minSize;
36493         }
36494         if(typeof this.config.maxSize != "undefined"){
36495             this.split.maxSize = this.config.maxSize;
36496         }
36497         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36498             this.hideSplitter();
36499         }
36500         
36501     },
36502
36503     getHMaxSize : function(){
36504          var cmax = this.config.maxSize || 10000;
36505          var center = this.mgr.getRegion("center");
36506          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36507     },
36508
36509     getVMaxSize : function(){
36510          var cmax = this.config.maxSize || 10000;
36511          var center = this.mgr.getRegion("center");
36512          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36513     },
36514
36515     onSplitMove : function(split, newSize){
36516         this.fireEvent("resized", this, newSize);
36517     },
36518     
36519     /** 
36520      * Returns the {@link Roo.SplitBar} for this region.
36521      * @return {Roo.SplitBar}
36522      */
36523     getSplitBar : function(){
36524         return this.split;
36525     },
36526     
36527     hide : function(){
36528         this.hideSplitter();
36529         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36530     },
36531
36532     hideSplitter : function(){
36533         if(this.split){
36534             this.split.el.setLocation(-2000,-2000);
36535             this.split.el.hide();
36536         }
36537     },
36538
36539     show : function(){
36540         if(this.split){
36541             this.split.el.show();
36542         }
36543         Roo.bootstrap.layout.Split.superclass.show.call(this);
36544     },
36545     
36546     beforeSlide: function(){
36547         if(Roo.isGecko){// firefox overflow auto bug workaround
36548             this.bodyEl.clip();
36549             if(this.tabs) {
36550                 this.tabs.bodyEl.clip();
36551             }
36552             if(this.activePanel){
36553                 this.activePanel.getEl().clip();
36554                 
36555                 if(this.activePanel.beforeSlide){
36556                     this.activePanel.beforeSlide();
36557                 }
36558             }
36559         }
36560     },
36561     
36562     afterSlide : function(){
36563         if(Roo.isGecko){// firefox overflow auto bug workaround
36564             this.bodyEl.unclip();
36565             if(this.tabs) {
36566                 this.tabs.bodyEl.unclip();
36567             }
36568             if(this.activePanel){
36569                 this.activePanel.getEl().unclip();
36570                 if(this.activePanel.afterSlide){
36571                     this.activePanel.afterSlide();
36572                 }
36573             }
36574         }
36575     },
36576
36577     initAutoHide : function(){
36578         if(this.autoHide !== false){
36579             if(!this.autoHideHd){
36580                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36581                 this.autoHideHd = {
36582                     "mouseout": function(e){
36583                         if(!e.within(this.el, true)){
36584                             st.delay(500);
36585                         }
36586                     },
36587                     "mouseover" : function(e){
36588                         st.cancel();
36589                     },
36590                     scope : this
36591                 };
36592             }
36593             this.el.on(this.autoHideHd);
36594         }
36595     },
36596
36597     clearAutoHide : function(){
36598         if(this.autoHide !== false){
36599             this.el.un("mouseout", this.autoHideHd.mouseout);
36600             this.el.un("mouseover", this.autoHideHd.mouseover);
36601         }
36602     },
36603
36604     clearMonitor : function(){
36605         Roo.get(document).un("click", this.slideInIf, this);
36606     },
36607
36608     // these names are backwards but not changed for compat
36609     slideOut : function(){
36610         if(this.isSlid || this.el.hasActiveFx()){
36611             return;
36612         }
36613         this.isSlid = true;
36614         if(this.collapseBtn){
36615             this.collapseBtn.hide();
36616         }
36617         this.closeBtnState = this.closeBtn.getStyle('display');
36618         this.closeBtn.hide();
36619         if(this.stickBtn){
36620             this.stickBtn.show();
36621         }
36622         this.el.show();
36623         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36624         this.beforeSlide();
36625         this.el.setStyle("z-index", 10001);
36626         this.el.slideIn(this.getSlideAnchor(), {
36627             callback: function(){
36628                 this.afterSlide();
36629                 this.initAutoHide();
36630                 Roo.get(document).on("click", this.slideInIf, this);
36631                 this.fireEvent("slideshow", this);
36632             },
36633             scope: this,
36634             block: true
36635         });
36636     },
36637
36638     afterSlideIn : function(){
36639         this.clearAutoHide();
36640         this.isSlid = false;
36641         this.clearMonitor();
36642         this.el.setStyle("z-index", "");
36643         if(this.collapseBtn){
36644             this.collapseBtn.show();
36645         }
36646         this.closeBtn.setStyle('display', this.closeBtnState);
36647         if(this.stickBtn){
36648             this.stickBtn.hide();
36649         }
36650         this.fireEvent("slidehide", this);
36651     },
36652
36653     slideIn : function(cb){
36654         if(!this.isSlid || this.el.hasActiveFx()){
36655             Roo.callback(cb);
36656             return;
36657         }
36658         this.isSlid = false;
36659         this.beforeSlide();
36660         this.el.slideOut(this.getSlideAnchor(), {
36661             callback: function(){
36662                 this.el.setLeftTop(-10000, -10000);
36663                 this.afterSlide();
36664                 this.afterSlideIn();
36665                 Roo.callback(cb);
36666             },
36667             scope: this,
36668             block: true
36669         });
36670     },
36671     
36672     slideInIf : function(e){
36673         if(!e.within(this.el)){
36674             this.slideIn();
36675         }
36676     },
36677
36678     animateCollapse : function(){
36679         this.beforeSlide();
36680         this.el.setStyle("z-index", 20000);
36681         var anchor = this.getSlideAnchor();
36682         this.el.slideOut(anchor, {
36683             callback : function(){
36684                 this.el.setStyle("z-index", "");
36685                 this.collapsedEl.slideIn(anchor, {duration:.3});
36686                 this.afterSlide();
36687                 this.el.setLocation(-10000,-10000);
36688                 this.el.hide();
36689                 this.fireEvent("collapsed", this);
36690             },
36691             scope: this,
36692             block: true
36693         });
36694     },
36695
36696     animateExpand : function(){
36697         this.beforeSlide();
36698         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36699         this.el.setStyle("z-index", 20000);
36700         this.collapsedEl.hide({
36701             duration:.1
36702         });
36703         this.el.slideIn(this.getSlideAnchor(), {
36704             callback : function(){
36705                 this.el.setStyle("z-index", "");
36706                 this.afterSlide();
36707                 if(this.split){
36708                     this.split.el.show();
36709                 }
36710                 this.fireEvent("invalidated", this);
36711                 this.fireEvent("expanded", this);
36712             },
36713             scope: this,
36714             block: true
36715         });
36716     },
36717
36718     anchors : {
36719         "west" : "left",
36720         "east" : "right",
36721         "north" : "top",
36722         "south" : "bottom"
36723     },
36724
36725     sanchors : {
36726         "west" : "l",
36727         "east" : "r",
36728         "north" : "t",
36729         "south" : "b"
36730     },
36731
36732     canchors : {
36733         "west" : "tl-tr",
36734         "east" : "tr-tl",
36735         "north" : "tl-bl",
36736         "south" : "bl-tl"
36737     },
36738
36739     getAnchor : function(){
36740         return this.anchors[this.position];
36741     },
36742
36743     getCollapseAnchor : function(){
36744         return this.canchors[this.position];
36745     },
36746
36747     getSlideAnchor : function(){
36748         return this.sanchors[this.position];
36749     },
36750
36751     getAlignAdj : function(){
36752         var cm = this.cmargins;
36753         switch(this.position){
36754             case "west":
36755                 return [0, 0];
36756             break;
36757             case "east":
36758                 return [0, 0];
36759             break;
36760             case "north":
36761                 return [0, 0];
36762             break;
36763             case "south":
36764                 return [0, 0];
36765             break;
36766         }
36767     },
36768
36769     getExpandAdj : function(){
36770         var c = this.collapsedEl, cm = this.cmargins;
36771         switch(this.position){
36772             case "west":
36773                 return [-(cm.right+c.getWidth()+cm.left), 0];
36774             break;
36775             case "east":
36776                 return [cm.right+c.getWidth()+cm.left, 0];
36777             break;
36778             case "north":
36779                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36780             break;
36781             case "south":
36782                 return [0, cm.top+cm.bottom+c.getHeight()];
36783             break;
36784         }
36785     }
36786 });/*
36787  * Based on:
36788  * Ext JS Library 1.1.1
36789  * Copyright(c) 2006-2007, Ext JS, LLC.
36790  *
36791  * Originally Released Under LGPL - original licence link has changed is not relivant.
36792  *
36793  * Fork - LGPL
36794  * <script type="text/javascript">
36795  */
36796 /*
36797  * These classes are private internal classes
36798  */
36799 Roo.bootstrap.layout.Center = function(config){
36800     config.region = "center";
36801     Roo.bootstrap.layout.Region.call(this, config);
36802     this.visible = true;
36803     this.minWidth = config.minWidth || 20;
36804     this.minHeight = config.minHeight || 20;
36805 };
36806
36807 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36808     hide : function(){
36809         // center panel can't be hidden
36810     },
36811     
36812     show : function(){
36813         // center panel can't be hidden
36814     },
36815     
36816     getMinWidth: function(){
36817         return this.minWidth;
36818     },
36819     
36820     getMinHeight: function(){
36821         return this.minHeight;
36822     }
36823 });
36824
36825
36826
36827
36828  
36829
36830
36831
36832
36833
36834 Roo.bootstrap.layout.North = function(config)
36835 {
36836     config.region = 'north';
36837     config.cursor = 'n-resize';
36838     
36839     Roo.bootstrap.layout.Split.call(this, config);
36840     
36841     
36842     if(this.split){
36843         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36844         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36845         this.split.el.addClass("roo-layout-split-v");
36846     }
36847     var size = config.initialSize || config.height;
36848     if(typeof size != "undefined"){
36849         this.el.setHeight(size);
36850     }
36851 };
36852 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36853 {
36854     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36855     
36856     
36857     
36858     getBox : function(){
36859         if(this.collapsed){
36860             return this.collapsedEl.getBox();
36861         }
36862         var box = this.el.getBox();
36863         if(this.split){
36864             box.height += this.split.el.getHeight();
36865         }
36866         return box;
36867     },
36868     
36869     updateBox : function(box){
36870         if(this.split && !this.collapsed){
36871             box.height -= this.split.el.getHeight();
36872             this.split.el.setLeft(box.x);
36873             this.split.el.setTop(box.y+box.height);
36874             this.split.el.setWidth(box.width);
36875         }
36876         if(this.collapsed){
36877             this.updateBody(box.width, null);
36878         }
36879         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36880     }
36881 });
36882
36883
36884
36885
36886
36887 Roo.bootstrap.layout.South = function(config){
36888     config.region = 'south';
36889     config.cursor = 's-resize';
36890     Roo.bootstrap.layout.Split.call(this, config);
36891     if(this.split){
36892         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36893         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36894         this.split.el.addClass("roo-layout-split-v");
36895     }
36896     var size = config.initialSize || config.height;
36897     if(typeof size != "undefined"){
36898         this.el.setHeight(size);
36899     }
36900 };
36901
36902 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36903     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36904     getBox : function(){
36905         if(this.collapsed){
36906             return this.collapsedEl.getBox();
36907         }
36908         var box = this.el.getBox();
36909         if(this.split){
36910             var sh = this.split.el.getHeight();
36911             box.height += sh;
36912             box.y -= sh;
36913         }
36914         return box;
36915     },
36916     
36917     updateBox : function(box){
36918         if(this.split && !this.collapsed){
36919             var sh = this.split.el.getHeight();
36920             box.height -= sh;
36921             box.y += sh;
36922             this.split.el.setLeft(box.x);
36923             this.split.el.setTop(box.y-sh);
36924             this.split.el.setWidth(box.width);
36925         }
36926         if(this.collapsed){
36927             this.updateBody(box.width, null);
36928         }
36929         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36930     }
36931 });
36932
36933 Roo.bootstrap.layout.East = function(config){
36934     config.region = "east";
36935     config.cursor = "e-resize";
36936     Roo.bootstrap.layout.Split.call(this, config);
36937     if(this.split){
36938         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36939         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36940         this.split.el.addClass("roo-layout-split-h");
36941     }
36942     var size = config.initialSize || config.width;
36943     if(typeof size != "undefined"){
36944         this.el.setWidth(size);
36945     }
36946 };
36947 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36948     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36949     getBox : function(){
36950         if(this.collapsed){
36951             return this.collapsedEl.getBox();
36952         }
36953         var box = this.el.getBox();
36954         if(this.split){
36955             var sw = this.split.el.getWidth();
36956             box.width += sw;
36957             box.x -= sw;
36958         }
36959         return box;
36960     },
36961
36962     updateBox : function(box){
36963         if(this.split && !this.collapsed){
36964             var sw = this.split.el.getWidth();
36965             box.width -= sw;
36966             this.split.el.setLeft(box.x);
36967             this.split.el.setTop(box.y);
36968             this.split.el.setHeight(box.height);
36969             box.x += sw;
36970         }
36971         if(this.collapsed){
36972             this.updateBody(null, box.height);
36973         }
36974         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36975     }
36976 });
36977
36978 Roo.bootstrap.layout.West = function(config){
36979     config.region = "west";
36980     config.cursor = "w-resize";
36981     
36982     Roo.bootstrap.layout.Split.call(this, config);
36983     if(this.split){
36984         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36985         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36986         this.split.el.addClass("roo-layout-split-h");
36987     }
36988     
36989 };
36990 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36991     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36992     
36993     onRender: function(ctr, pos)
36994     {
36995         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36996         var size = this.config.initialSize || this.config.width;
36997         if(typeof size != "undefined"){
36998             this.el.setWidth(size);
36999         }
37000     },
37001     
37002     getBox : function(){
37003         if(this.collapsed){
37004             return this.collapsedEl.getBox();
37005         }
37006         var box = this.el.getBox();
37007         if(this.split){
37008             box.width += this.split.el.getWidth();
37009         }
37010         return box;
37011     },
37012     
37013     updateBox : function(box){
37014         if(this.split && !this.collapsed){
37015             var sw = this.split.el.getWidth();
37016             box.width -= sw;
37017             this.split.el.setLeft(box.x+box.width);
37018             this.split.el.setTop(box.y);
37019             this.split.el.setHeight(box.height);
37020         }
37021         if(this.collapsed){
37022             this.updateBody(null, box.height);
37023         }
37024         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37025     }
37026 });
37027 Roo.namespace("Roo.bootstrap.panel");/*
37028  * Based on:
37029  * Ext JS Library 1.1.1
37030  * Copyright(c) 2006-2007, Ext JS, LLC.
37031  *
37032  * Originally Released Under LGPL - original licence link has changed is not relivant.
37033  *
37034  * Fork - LGPL
37035  * <script type="text/javascript">
37036  */
37037 /**
37038  * @class Roo.ContentPanel
37039  * @extends Roo.util.Observable
37040  * A basic ContentPanel element.
37041  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37042  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37043  * @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
37044  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37045  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37046  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37047  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37048  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37049  * @cfg {String} title          The title for this panel
37050  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37051  * @cfg {String} url            Calls {@link #setUrl} with this value
37052  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37053  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37054  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37055  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37056  * @cfg {Boolean} badges render the badges
37057
37058  * @constructor
37059  * Create a new ContentPanel.
37060  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37061  * @param {String/Object} config A string to set only the title or a config object
37062  * @param {String} content (optional) Set the HTML content for this panel
37063  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37064  */
37065 Roo.bootstrap.panel.Content = function( config){
37066     
37067     this.tpl = config.tpl || false;
37068     
37069     var el = config.el;
37070     var content = config.content;
37071
37072     if(config.autoCreate){ // xtype is available if this is called from factory
37073         el = Roo.id();
37074     }
37075     this.el = Roo.get(el);
37076     if(!this.el && config && config.autoCreate){
37077         if(typeof config.autoCreate == "object"){
37078             if(!config.autoCreate.id){
37079                 config.autoCreate.id = config.id||el;
37080             }
37081             this.el = Roo.DomHelper.append(document.body,
37082                         config.autoCreate, true);
37083         }else{
37084             var elcfg =  {   tag: "div",
37085                             cls: "roo-layout-inactive-content",
37086                             id: config.id||el
37087                             };
37088             if (config.html) {
37089                 elcfg.html = config.html;
37090                 
37091             }
37092                         
37093             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37094         }
37095     } 
37096     this.closable = false;
37097     this.loaded = false;
37098     this.active = false;
37099    
37100       
37101     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37102         
37103         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37104         
37105         this.wrapEl = this.el; //this.el.wrap();
37106         var ti = [];
37107         if (config.toolbar.items) {
37108             ti = config.toolbar.items ;
37109             delete config.toolbar.items ;
37110         }
37111         
37112         var nitems = [];
37113         this.toolbar.render(this.wrapEl, 'before');
37114         for(var i =0;i < ti.length;i++) {
37115           //  Roo.log(['add child', items[i]]);
37116             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37117         }
37118         this.toolbar.items = nitems;
37119         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37120         delete config.toolbar;
37121         
37122     }
37123     /*
37124     // xtype created footer. - not sure if will work as we normally have to render first..
37125     if (this.footer && !this.footer.el && this.footer.xtype) {
37126         if (!this.wrapEl) {
37127             this.wrapEl = this.el.wrap();
37128         }
37129     
37130         this.footer.container = this.wrapEl.createChild();
37131          
37132         this.footer = Roo.factory(this.footer, Roo);
37133         
37134     }
37135     */
37136     
37137      if(typeof config == "string"){
37138         this.title = config;
37139     }else{
37140         Roo.apply(this, config);
37141     }
37142     
37143     if(this.resizeEl){
37144         this.resizeEl = Roo.get(this.resizeEl, true);
37145     }else{
37146         this.resizeEl = this.el;
37147     }
37148     // handle view.xtype
37149     
37150  
37151     
37152     
37153     this.addEvents({
37154         /**
37155          * @event activate
37156          * Fires when this panel is activated. 
37157          * @param {Roo.ContentPanel} this
37158          */
37159         "activate" : true,
37160         /**
37161          * @event deactivate
37162          * Fires when this panel is activated. 
37163          * @param {Roo.ContentPanel} this
37164          */
37165         "deactivate" : true,
37166
37167         /**
37168          * @event resize
37169          * Fires when this panel is resized if fitToFrame is true.
37170          * @param {Roo.ContentPanel} this
37171          * @param {Number} width The width after any component adjustments
37172          * @param {Number} height The height after any component adjustments
37173          */
37174         "resize" : true,
37175         
37176          /**
37177          * @event render
37178          * Fires when this tab is created
37179          * @param {Roo.ContentPanel} this
37180          */
37181         "render" : true
37182         
37183         
37184         
37185     });
37186     
37187
37188     
37189     
37190     if(this.autoScroll){
37191         this.resizeEl.setStyle("overflow", "auto");
37192     } else {
37193         // fix randome scrolling
37194         //this.el.on('scroll', function() {
37195         //    Roo.log('fix random scolling');
37196         //    this.scrollTo('top',0); 
37197         //});
37198     }
37199     content = content || this.content;
37200     if(content){
37201         this.setContent(content);
37202     }
37203     if(config && config.url){
37204         this.setUrl(this.url, this.params, this.loadOnce);
37205     }
37206     
37207     
37208     
37209     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37210     
37211     if (this.view && typeof(this.view.xtype) != 'undefined') {
37212         this.view.el = this.el.appendChild(document.createElement("div"));
37213         this.view = Roo.factory(this.view); 
37214         this.view.render  &&  this.view.render(false, '');  
37215     }
37216     
37217     
37218     this.fireEvent('render', this);
37219 };
37220
37221 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37222     
37223     tabTip : '',
37224     
37225     setRegion : function(region){
37226         this.region = region;
37227         this.setActiveClass(region && !this.background);
37228     },
37229     
37230     
37231     setActiveClass: function(state)
37232     {
37233         if(state){
37234            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37235            this.el.setStyle('position','relative');
37236         }else{
37237            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37238            this.el.setStyle('position', 'absolute');
37239         } 
37240     },
37241     
37242     /**
37243      * Returns the toolbar for this Panel if one was configured. 
37244      * @return {Roo.Toolbar} 
37245      */
37246     getToolbar : function(){
37247         return this.toolbar;
37248     },
37249     
37250     setActiveState : function(active)
37251     {
37252         this.active = active;
37253         this.setActiveClass(active);
37254         if(!active){
37255             if(this.fireEvent("deactivate", this) === false){
37256                 return false;
37257             }
37258             return true;
37259         }
37260         this.fireEvent("activate", this);
37261         return true;
37262     },
37263     /**
37264      * Updates this panel's element
37265      * @param {String} content The new content
37266      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37267     */
37268     setContent : function(content, loadScripts){
37269         this.el.update(content, loadScripts);
37270     },
37271
37272     ignoreResize : function(w, h){
37273         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37274             return true;
37275         }else{
37276             this.lastSize = {width: w, height: h};
37277             return false;
37278         }
37279     },
37280     /**
37281      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37282      * @return {Roo.UpdateManager} The UpdateManager
37283      */
37284     getUpdateManager : function(){
37285         return this.el.getUpdateManager();
37286     },
37287      /**
37288      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37289      * @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:
37290 <pre><code>
37291 panel.load({
37292     url: "your-url.php",
37293     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37294     callback: yourFunction,
37295     scope: yourObject, //(optional scope)
37296     discardUrl: false,
37297     nocache: false,
37298     text: "Loading...",
37299     timeout: 30,
37300     scripts: false
37301 });
37302 </code></pre>
37303      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37304      * 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.
37305      * @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}
37306      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37307      * @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.
37308      * @return {Roo.ContentPanel} this
37309      */
37310     load : function(){
37311         var um = this.el.getUpdateManager();
37312         um.update.apply(um, arguments);
37313         return this;
37314     },
37315
37316
37317     /**
37318      * 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.
37319      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37320      * @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)
37321      * @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)
37322      * @return {Roo.UpdateManager} The UpdateManager
37323      */
37324     setUrl : function(url, params, loadOnce){
37325         if(this.refreshDelegate){
37326             this.removeListener("activate", this.refreshDelegate);
37327         }
37328         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37329         this.on("activate", this.refreshDelegate);
37330         return this.el.getUpdateManager();
37331     },
37332     
37333     _handleRefresh : function(url, params, loadOnce){
37334         if(!loadOnce || !this.loaded){
37335             var updater = this.el.getUpdateManager();
37336             updater.update(url, params, this._setLoaded.createDelegate(this));
37337         }
37338     },
37339     
37340     _setLoaded : function(){
37341         this.loaded = true;
37342     }, 
37343     
37344     /**
37345      * Returns this panel's id
37346      * @return {String} 
37347      */
37348     getId : function(){
37349         return this.el.id;
37350     },
37351     
37352     /** 
37353      * Returns this panel's element - used by regiosn to add.
37354      * @return {Roo.Element} 
37355      */
37356     getEl : function(){
37357         return this.wrapEl || this.el;
37358     },
37359     
37360    
37361     
37362     adjustForComponents : function(width, height)
37363     {
37364         //Roo.log('adjustForComponents ');
37365         if(this.resizeEl != this.el){
37366             width -= this.el.getFrameWidth('lr');
37367             height -= this.el.getFrameWidth('tb');
37368         }
37369         if(this.toolbar){
37370             var te = this.toolbar.getEl();
37371             te.setWidth(width);
37372             height -= te.getHeight();
37373         }
37374         if(this.footer){
37375             var te = this.footer.getEl();
37376             te.setWidth(width);
37377             height -= te.getHeight();
37378         }
37379         
37380         
37381         if(this.adjustments){
37382             width += this.adjustments[0];
37383             height += this.adjustments[1];
37384         }
37385         return {"width": width, "height": height};
37386     },
37387     
37388     setSize : function(width, height){
37389         if(this.fitToFrame && !this.ignoreResize(width, height)){
37390             if(this.fitContainer && this.resizeEl != this.el){
37391                 this.el.setSize(width, height);
37392             }
37393             var size = this.adjustForComponents(width, height);
37394             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37395             this.fireEvent('resize', this, size.width, size.height);
37396         }
37397     },
37398     
37399     /**
37400      * Returns this panel's title
37401      * @return {String} 
37402      */
37403     getTitle : function(){
37404         
37405         if (typeof(this.title) != 'object') {
37406             return this.title;
37407         }
37408         
37409         var t = '';
37410         for (var k in this.title) {
37411             if (!this.title.hasOwnProperty(k)) {
37412                 continue;
37413             }
37414             
37415             if (k.indexOf('-') >= 0) {
37416                 var s = k.split('-');
37417                 for (var i = 0; i<s.length; i++) {
37418                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37419                 }
37420             } else {
37421                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37422             }
37423         }
37424         return t;
37425     },
37426     
37427     /**
37428      * Set this panel's title
37429      * @param {String} title
37430      */
37431     setTitle : function(title){
37432         this.title = title;
37433         if(this.region){
37434             this.region.updatePanelTitle(this, title);
37435         }
37436     },
37437     
37438     /**
37439      * Returns true is this panel was configured to be closable
37440      * @return {Boolean} 
37441      */
37442     isClosable : function(){
37443         return this.closable;
37444     },
37445     
37446     beforeSlide : function(){
37447         this.el.clip();
37448         this.resizeEl.clip();
37449     },
37450     
37451     afterSlide : function(){
37452         this.el.unclip();
37453         this.resizeEl.unclip();
37454     },
37455     
37456     /**
37457      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37458      *   Will fail silently if the {@link #setUrl} method has not been called.
37459      *   This does not activate the panel, just updates its content.
37460      */
37461     refresh : function(){
37462         if(this.refreshDelegate){
37463            this.loaded = false;
37464            this.refreshDelegate();
37465         }
37466     },
37467     
37468     /**
37469      * Destroys this panel
37470      */
37471     destroy : function(){
37472         this.el.removeAllListeners();
37473         var tempEl = document.createElement("span");
37474         tempEl.appendChild(this.el.dom);
37475         tempEl.innerHTML = "";
37476         this.el.remove();
37477         this.el = null;
37478     },
37479     
37480     /**
37481      * form - if the content panel contains a form - this is a reference to it.
37482      * @type {Roo.form.Form}
37483      */
37484     form : false,
37485     /**
37486      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37487      *    This contains a reference to it.
37488      * @type {Roo.View}
37489      */
37490     view : false,
37491     
37492       /**
37493      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37494      * <pre><code>
37495
37496 layout.addxtype({
37497        xtype : 'Form',
37498        items: [ .... ]
37499    }
37500 );
37501
37502 </code></pre>
37503      * @param {Object} cfg Xtype definition of item to add.
37504      */
37505     
37506     
37507     getChildContainer: function () {
37508         return this.getEl();
37509     }
37510     
37511     
37512     /*
37513         var  ret = new Roo.factory(cfg);
37514         return ret;
37515         
37516         
37517         // add form..
37518         if (cfg.xtype.match(/^Form$/)) {
37519             
37520             var el;
37521             //if (this.footer) {
37522             //    el = this.footer.container.insertSibling(false, 'before');
37523             //} else {
37524                 el = this.el.createChild();
37525             //}
37526
37527             this.form = new  Roo.form.Form(cfg);
37528             
37529             
37530             if ( this.form.allItems.length) {
37531                 this.form.render(el.dom);
37532             }
37533             return this.form;
37534         }
37535         // should only have one of theses..
37536         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37537             // views.. should not be just added - used named prop 'view''
37538             
37539             cfg.el = this.el.appendChild(document.createElement("div"));
37540             // factory?
37541             
37542             var ret = new Roo.factory(cfg);
37543              
37544              ret.render && ret.render(false, ''); // render blank..
37545             this.view = ret;
37546             return ret;
37547         }
37548         return false;
37549     }
37550     \*/
37551 });
37552  
37553 /**
37554  * @class Roo.bootstrap.panel.Grid
37555  * @extends Roo.bootstrap.panel.Content
37556  * @constructor
37557  * Create a new GridPanel.
37558  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37559  * @param {Object} config A the config object
37560   
37561  */
37562
37563
37564
37565 Roo.bootstrap.panel.Grid = function(config)
37566 {
37567     
37568       
37569     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37570         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37571
37572     config.el = this.wrapper;
37573     //this.el = this.wrapper;
37574     
37575       if (config.container) {
37576         // ctor'ed from a Border/panel.grid
37577         
37578         
37579         this.wrapper.setStyle("overflow", "hidden");
37580         this.wrapper.addClass('roo-grid-container');
37581
37582     }
37583     
37584     
37585     if(config.toolbar){
37586         var tool_el = this.wrapper.createChild();    
37587         this.toolbar = Roo.factory(config.toolbar);
37588         var ti = [];
37589         if (config.toolbar.items) {
37590             ti = config.toolbar.items ;
37591             delete config.toolbar.items ;
37592         }
37593         
37594         var nitems = [];
37595         this.toolbar.render(tool_el);
37596         for(var i =0;i < ti.length;i++) {
37597           //  Roo.log(['add child', items[i]]);
37598             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37599         }
37600         this.toolbar.items = nitems;
37601         
37602         delete config.toolbar;
37603     }
37604     
37605     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37606     config.grid.scrollBody = true;;
37607     config.grid.monitorWindowResize = false; // turn off autosizing
37608     config.grid.autoHeight = false;
37609     config.grid.autoWidth = false;
37610     
37611     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37612     
37613     if (config.background) {
37614         // render grid on panel activation (if panel background)
37615         this.on('activate', function(gp) {
37616             if (!gp.grid.rendered) {
37617                 gp.grid.render(this.wrapper);
37618                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37619             }
37620         });
37621             
37622     } else {
37623         this.grid.render(this.wrapper);
37624         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37625
37626     }
37627     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37628     // ??? needed ??? config.el = this.wrapper;
37629     
37630     
37631     
37632   
37633     // xtype created footer. - not sure if will work as we normally have to render first..
37634     if (this.footer && !this.footer.el && this.footer.xtype) {
37635         
37636         var ctr = this.grid.getView().getFooterPanel(true);
37637         this.footer.dataSource = this.grid.dataSource;
37638         this.footer = Roo.factory(this.footer, Roo);
37639         this.footer.render(ctr);
37640         
37641     }
37642     
37643     
37644     
37645     
37646      
37647 };
37648
37649 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37650     getId : function(){
37651         return this.grid.id;
37652     },
37653     
37654     /**
37655      * Returns the grid for this panel
37656      * @return {Roo.bootstrap.Table} 
37657      */
37658     getGrid : function(){
37659         return this.grid;    
37660     },
37661     
37662     setSize : function(width, height){
37663         if(!this.ignoreResize(width, height)){
37664             var grid = this.grid;
37665             var size = this.adjustForComponents(width, height);
37666             var gridel = grid.getGridEl();
37667             gridel.setSize(size.width, size.height);
37668             /*
37669             var thd = grid.getGridEl().select('thead',true).first();
37670             var tbd = grid.getGridEl().select('tbody', true).first();
37671             if (tbd) {
37672                 tbd.setSize(width, height - thd.getHeight());
37673             }
37674             */
37675             grid.autoSize();
37676         }
37677     },
37678      
37679     
37680     
37681     beforeSlide : function(){
37682         this.grid.getView().scroller.clip();
37683     },
37684     
37685     afterSlide : function(){
37686         this.grid.getView().scroller.unclip();
37687     },
37688     
37689     destroy : function(){
37690         this.grid.destroy();
37691         delete this.grid;
37692         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37693     }
37694 });
37695
37696 /**
37697  * @class Roo.bootstrap.panel.Nest
37698  * @extends Roo.bootstrap.panel.Content
37699  * @constructor
37700  * Create a new Panel, that can contain a layout.Border.
37701  * 
37702  * 
37703  * @param {Roo.BorderLayout} layout The layout for this panel
37704  * @param {String/Object} config A string to set only the title or a config object
37705  */
37706 Roo.bootstrap.panel.Nest = function(config)
37707 {
37708     // construct with only one argument..
37709     /* FIXME - implement nicer consturctors
37710     if (layout.layout) {
37711         config = layout;
37712         layout = config.layout;
37713         delete config.layout;
37714     }
37715     if (layout.xtype && !layout.getEl) {
37716         // then layout needs constructing..
37717         layout = Roo.factory(layout, Roo);
37718     }
37719     */
37720     
37721     config.el =  config.layout.getEl();
37722     
37723     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37724     
37725     config.layout.monitorWindowResize = false; // turn off autosizing
37726     this.layout = config.layout;
37727     this.layout.getEl().addClass("roo-layout-nested-layout");
37728     
37729     
37730     
37731     
37732 };
37733
37734 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37735
37736     setSize : function(width, height){
37737         if(!this.ignoreResize(width, height)){
37738             var size = this.adjustForComponents(width, height);
37739             var el = this.layout.getEl();
37740             if (size.height < 1) {
37741                 el.setWidth(size.width);   
37742             } else {
37743                 el.setSize(size.width, size.height);
37744             }
37745             var touch = el.dom.offsetWidth;
37746             this.layout.layout();
37747             // ie requires a double layout on the first pass
37748             if(Roo.isIE && !this.initialized){
37749                 this.initialized = true;
37750                 this.layout.layout();
37751             }
37752         }
37753     },
37754     
37755     // activate all subpanels if not currently active..
37756     
37757     setActiveState : function(active){
37758         this.active = active;
37759         this.setActiveClass(active);
37760         
37761         if(!active){
37762             this.fireEvent("deactivate", this);
37763             return;
37764         }
37765         
37766         this.fireEvent("activate", this);
37767         // not sure if this should happen before or after..
37768         if (!this.layout) {
37769             return; // should not happen..
37770         }
37771         var reg = false;
37772         for (var r in this.layout.regions) {
37773             reg = this.layout.getRegion(r);
37774             if (reg.getActivePanel()) {
37775                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37776                 reg.setActivePanel(reg.getActivePanel());
37777                 continue;
37778             }
37779             if (!reg.panels.length) {
37780                 continue;
37781             }
37782             reg.showPanel(reg.getPanel(0));
37783         }
37784         
37785         
37786         
37787         
37788     },
37789     
37790     /**
37791      * Returns the nested BorderLayout for this panel
37792      * @return {Roo.BorderLayout} 
37793      */
37794     getLayout : function(){
37795         return this.layout;
37796     },
37797     
37798      /**
37799      * Adds a xtype elements to the layout of the nested panel
37800      * <pre><code>
37801
37802 panel.addxtype({
37803        xtype : 'ContentPanel',
37804        region: 'west',
37805        items: [ .... ]
37806    }
37807 );
37808
37809 panel.addxtype({
37810         xtype : 'NestedLayoutPanel',
37811         region: 'west',
37812         layout: {
37813            center: { },
37814            west: { }   
37815         },
37816         items : [ ... list of content panels or nested layout panels.. ]
37817    }
37818 );
37819 </code></pre>
37820      * @param {Object} cfg Xtype definition of item to add.
37821      */
37822     addxtype : function(cfg) {
37823         return this.layout.addxtype(cfg);
37824     
37825     }
37826 });        /*
37827  * Based on:
37828  * Ext JS Library 1.1.1
37829  * Copyright(c) 2006-2007, Ext JS, LLC.
37830  *
37831  * Originally Released Under LGPL - original licence link has changed is not relivant.
37832  *
37833  * Fork - LGPL
37834  * <script type="text/javascript">
37835  */
37836 /**
37837  * @class Roo.TabPanel
37838  * @extends Roo.util.Observable
37839  * A lightweight tab container.
37840  * <br><br>
37841  * Usage:
37842  * <pre><code>
37843 // basic tabs 1, built from existing content
37844 var tabs = new Roo.TabPanel("tabs1");
37845 tabs.addTab("script", "View Script");
37846 tabs.addTab("markup", "View Markup");
37847 tabs.activate("script");
37848
37849 // more advanced tabs, built from javascript
37850 var jtabs = new Roo.TabPanel("jtabs");
37851 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37852
37853 // set up the UpdateManager
37854 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37855 var updater = tab2.getUpdateManager();
37856 updater.setDefaultUrl("ajax1.htm");
37857 tab2.on('activate', updater.refresh, updater, true);
37858
37859 // Use setUrl for Ajax loading
37860 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37861 tab3.setUrl("ajax2.htm", null, true);
37862
37863 // Disabled tab
37864 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37865 tab4.disable();
37866
37867 jtabs.activate("jtabs-1");
37868  * </code></pre>
37869  * @constructor
37870  * Create a new TabPanel.
37871  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37872  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37873  */
37874 Roo.bootstrap.panel.Tabs = function(config){
37875     /**
37876     * The container element for this TabPanel.
37877     * @type Roo.Element
37878     */
37879     this.el = Roo.get(config.el);
37880     delete config.el;
37881     if(config){
37882         if(typeof config == "boolean"){
37883             this.tabPosition = config ? "bottom" : "top";
37884         }else{
37885             Roo.apply(this, config);
37886         }
37887     }
37888     
37889     if(this.tabPosition == "bottom"){
37890         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37891         this.el.addClass("roo-tabs-bottom");
37892     }
37893     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37894     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37895     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37896     if(Roo.isIE){
37897         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37898     }
37899     if(this.tabPosition != "bottom"){
37900         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37901          * @type Roo.Element
37902          */
37903         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37904         this.el.addClass("roo-tabs-top");
37905     }
37906     this.items = [];
37907
37908     this.bodyEl.setStyle("position", "relative");
37909
37910     this.active = null;
37911     this.activateDelegate = this.activate.createDelegate(this);
37912
37913     this.addEvents({
37914         /**
37915          * @event tabchange
37916          * Fires when the active tab changes
37917          * @param {Roo.TabPanel} this
37918          * @param {Roo.TabPanelItem} activePanel The new active tab
37919          */
37920         "tabchange": true,
37921         /**
37922          * @event beforetabchange
37923          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37924          * @param {Roo.TabPanel} this
37925          * @param {Object} e Set cancel to true on this object to cancel the tab change
37926          * @param {Roo.TabPanelItem} tab The tab being changed to
37927          */
37928         "beforetabchange" : true
37929     });
37930
37931     Roo.EventManager.onWindowResize(this.onResize, this);
37932     this.cpad = this.el.getPadding("lr");
37933     this.hiddenCount = 0;
37934
37935
37936     // toolbar on the tabbar support...
37937     if (this.toolbar) {
37938         alert("no toolbar support yet");
37939         this.toolbar  = false;
37940         /*
37941         var tcfg = this.toolbar;
37942         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37943         this.toolbar = new Roo.Toolbar(tcfg);
37944         if (Roo.isSafari) {
37945             var tbl = tcfg.container.child('table', true);
37946             tbl.setAttribute('width', '100%');
37947         }
37948         */
37949         
37950     }
37951    
37952
37953
37954     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37955 };
37956
37957 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37958     /*
37959      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37960      */
37961     tabPosition : "top",
37962     /*
37963      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37964      */
37965     currentTabWidth : 0,
37966     /*
37967      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37968      */
37969     minTabWidth : 40,
37970     /*
37971      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37972      */
37973     maxTabWidth : 250,
37974     /*
37975      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37976      */
37977     preferredTabWidth : 175,
37978     /*
37979      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37980      */
37981     resizeTabs : false,
37982     /*
37983      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37984      */
37985     monitorResize : true,
37986     /*
37987      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37988      */
37989     toolbar : false,
37990
37991     /**
37992      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37993      * @param {String} id The id of the div to use <b>or create</b>
37994      * @param {String} text The text for the tab
37995      * @param {String} content (optional) Content to put in the TabPanelItem body
37996      * @param {Boolean} closable (optional) True to create a close icon on the tab
37997      * @return {Roo.TabPanelItem} The created TabPanelItem
37998      */
37999     addTab : function(id, text, content, closable, tpl)
38000     {
38001         var item = new Roo.bootstrap.panel.TabItem({
38002             panel: this,
38003             id : id,
38004             text : text,
38005             closable : closable,
38006             tpl : tpl
38007         });
38008         this.addTabItem(item);
38009         if(content){
38010             item.setContent(content);
38011         }
38012         return item;
38013     },
38014
38015     /**
38016      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38017      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38018      * @return {Roo.TabPanelItem}
38019      */
38020     getTab : function(id){
38021         return this.items[id];
38022     },
38023
38024     /**
38025      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38026      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38027      */
38028     hideTab : function(id){
38029         var t = this.items[id];
38030         if(!t.isHidden()){
38031            t.setHidden(true);
38032            this.hiddenCount++;
38033            this.autoSizeTabs();
38034         }
38035     },
38036
38037     /**
38038      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38039      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38040      */
38041     unhideTab : function(id){
38042         var t = this.items[id];
38043         if(t.isHidden()){
38044            t.setHidden(false);
38045            this.hiddenCount--;
38046            this.autoSizeTabs();
38047         }
38048     },
38049
38050     /**
38051      * Adds an existing {@link Roo.TabPanelItem}.
38052      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38053      */
38054     addTabItem : function(item){
38055         this.items[item.id] = item;
38056         this.items.push(item);
38057       //  if(this.resizeTabs){
38058     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38059   //         this.autoSizeTabs();
38060 //        }else{
38061 //            item.autoSize();
38062        // }
38063     },
38064
38065     /**
38066      * Removes a {@link Roo.TabPanelItem}.
38067      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38068      */
38069     removeTab : function(id){
38070         var items = this.items;
38071         var tab = items[id];
38072         if(!tab) { return; }
38073         var index = items.indexOf(tab);
38074         if(this.active == tab && items.length > 1){
38075             var newTab = this.getNextAvailable(index);
38076             if(newTab) {
38077                 newTab.activate();
38078             }
38079         }
38080         this.stripEl.dom.removeChild(tab.pnode.dom);
38081         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38082             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38083         }
38084         items.splice(index, 1);
38085         delete this.items[tab.id];
38086         tab.fireEvent("close", tab);
38087         tab.purgeListeners();
38088         this.autoSizeTabs();
38089     },
38090
38091     getNextAvailable : function(start){
38092         var items = this.items;
38093         var index = start;
38094         // look for a next tab that will slide over to
38095         // replace the one being removed
38096         while(index < items.length){
38097             var item = items[++index];
38098             if(item && !item.isHidden()){
38099                 return item;
38100             }
38101         }
38102         // if one isn't found select the previous tab (on the left)
38103         index = start;
38104         while(index >= 0){
38105             var item = items[--index];
38106             if(item && !item.isHidden()){
38107                 return item;
38108             }
38109         }
38110         return null;
38111     },
38112
38113     /**
38114      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38115      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38116      */
38117     disableTab : function(id){
38118         var tab = this.items[id];
38119         if(tab && this.active != tab){
38120             tab.disable();
38121         }
38122     },
38123
38124     /**
38125      * Enables a {@link Roo.TabPanelItem} that is disabled.
38126      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38127      */
38128     enableTab : function(id){
38129         var tab = this.items[id];
38130         tab.enable();
38131     },
38132
38133     /**
38134      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38135      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38136      * @return {Roo.TabPanelItem} The TabPanelItem.
38137      */
38138     activate : function(id){
38139         var tab = this.items[id];
38140         if(!tab){
38141             return null;
38142         }
38143         if(tab == this.active || tab.disabled){
38144             return tab;
38145         }
38146         var e = {};
38147         this.fireEvent("beforetabchange", this, e, tab);
38148         if(e.cancel !== true && !tab.disabled){
38149             if(this.active){
38150                 this.active.hide();
38151             }
38152             this.active = this.items[id];
38153             this.active.show();
38154             this.fireEvent("tabchange", this, this.active);
38155         }
38156         return tab;
38157     },
38158
38159     /**
38160      * Gets the active {@link Roo.TabPanelItem}.
38161      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38162      */
38163     getActiveTab : function(){
38164         return this.active;
38165     },
38166
38167     /**
38168      * Updates the tab body element to fit the height of the container element
38169      * for overflow scrolling
38170      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38171      */
38172     syncHeight : function(targetHeight){
38173         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38174         var bm = this.bodyEl.getMargins();
38175         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38176         this.bodyEl.setHeight(newHeight);
38177         return newHeight;
38178     },
38179
38180     onResize : function(){
38181         if(this.monitorResize){
38182             this.autoSizeTabs();
38183         }
38184     },
38185
38186     /**
38187      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38188      */
38189     beginUpdate : function(){
38190         this.updating = true;
38191     },
38192
38193     /**
38194      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38195      */
38196     endUpdate : function(){
38197         this.updating = false;
38198         this.autoSizeTabs();
38199     },
38200
38201     /**
38202      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38203      */
38204     autoSizeTabs : function(){
38205         var count = this.items.length;
38206         var vcount = count - this.hiddenCount;
38207         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38208             return;
38209         }
38210         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38211         var availWidth = Math.floor(w / vcount);
38212         var b = this.stripBody;
38213         if(b.getWidth() > w){
38214             var tabs = this.items;
38215             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38216             if(availWidth < this.minTabWidth){
38217                 /*if(!this.sleft){    // incomplete scrolling code
38218                     this.createScrollButtons();
38219                 }
38220                 this.showScroll();
38221                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38222             }
38223         }else{
38224             if(this.currentTabWidth < this.preferredTabWidth){
38225                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38226             }
38227         }
38228     },
38229
38230     /**
38231      * Returns the number of tabs in this TabPanel.
38232      * @return {Number}
38233      */
38234      getCount : function(){
38235          return this.items.length;
38236      },
38237
38238     /**
38239      * Resizes all the tabs to the passed width
38240      * @param {Number} The new width
38241      */
38242     setTabWidth : function(width){
38243         this.currentTabWidth = width;
38244         for(var i = 0, len = this.items.length; i < len; i++) {
38245                 if(!this.items[i].isHidden()) {
38246                 this.items[i].setWidth(width);
38247             }
38248         }
38249     },
38250
38251     /**
38252      * Destroys this TabPanel
38253      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38254      */
38255     destroy : function(removeEl){
38256         Roo.EventManager.removeResizeListener(this.onResize, this);
38257         for(var i = 0, len = this.items.length; i < len; i++){
38258             this.items[i].purgeListeners();
38259         }
38260         if(removeEl === true){
38261             this.el.update("");
38262             this.el.remove();
38263         }
38264     },
38265     
38266     createStrip : function(container)
38267     {
38268         var strip = document.createElement("nav");
38269         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38270         container.appendChild(strip);
38271         return strip;
38272     },
38273     
38274     createStripList : function(strip)
38275     {
38276         // div wrapper for retard IE
38277         // returns the "tr" element.
38278         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38279         //'<div class="x-tabs-strip-wrap">'+
38280           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38281           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38282         return strip.firstChild; //.firstChild.firstChild.firstChild;
38283     },
38284     createBody : function(container)
38285     {
38286         var body = document.createElement("div");
38287         Roo.id(body, "tab-body");
38288         //Roo.fly(body).addClass("x-tabs-body");
38289         Roo.fly(body).addClass("tab-content");
38290         container.appendChild(body);
38291         return body;
38292     },
38293     createItemBody :function(bodyEl, id){
38294         var body = Roo.getDom(id);
38295         if(!body){
38296             body = document.createElement("div");
38297             body.id = id;
38298         }
38299         //Roo.fly(body).addClass("x-tabs-item-body");
38300         Roo.fly(body).addClass("tab-pane");
38301          bodyEl.insertBefore(body, bodyEl.firstChild);
38302         return body;
38303     },
38304     /** @private */
38305     createStripElements :  function(stripEl, text, closable, tpl)
38306     {
38307         var td = document.createElement("li"); // was td..
38308         
38309         
38310         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38311         
38312         
38313         stripEl.appendChild(td);
38314         /*if(closable){
38315             td.className = "x-tabs-closable";
38316             if(!this.closeTpl){
38317                 this.closeTpl = new Roo.Template(
38318                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38319                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38320                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38321                 );
38322             }
38323             var el = this.closeTpl.overwrite(td, {"text": text});
38324             var close = el.getElementsByTagName("div")[0];
38325             var inner = el.getElementsByTagName("em")[0];
38326             return {"el": el, "close": close, "inner": inner};
38327         } else {
38328         */
38329         // not sure what this is..
38330 //            if(!this.tabTpl){
38331                 //this.tabTpl = new Roo.Template(
38332                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38333                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38334                 //);
38335 //                this.tabTpl = new Roo.Template(
38336 //                   '<a href="#">' +
38337 //                   '<span unselectable="on"' +
38338 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38339 //                            ' >{text}</span></a>'
38340 //                );
38341 //                
38342 //            }
38343
38344
38345             var template = tpl || this.tabTpl || false;
38346             
38347             if(!template){
38348                 
38349                 template = new Roo.Template(
38350                    '<a href="#">' +
38351                    '<span unselectable="on"' +
38352                             (this.disableTooltips ? '' : ' title="{text}"') +
38353                             ' >{text}</span></a>'
38354                 );
38355             }
38356             
38357             switch (typeof(template)) {
38358                 case 'object' :
38359                     break;
38360                 case 'string' :
38361                     template = new Roo.Template(template);
38362                     break;
38363                 default :
38364                     break;
38365             }
38366             
38367             var el = template.overwrite(td, {"text": text});
38368             
38369             var inner = el.getElementsByTagName("span")[0];
38370             
38371             return {"el": el, "inner": inner};
38372             
38373     }
38374         
38375     
38376 });
38377
38378 /**
38379  * @class Roo.TabPanelItem
38380  * @extends Roo.util.Observable
38381  * Represents an individual item (tab plus body) in a TabPanel.
38382  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38383  * @param {String} id The id of this TabPanelItem
38384  * @param {String} text The text for the tab of this TabPanelItem
38385  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38386  */
38387 Roo.bootstrap.panel.TabItem = function(config){
38388     /**
38389      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38390      * @type Roo.TabPanel
38391      */
38392     this.tabPanel = config.panel;
38393     /**
38394      * The id for this TabPanelItem
38395      * @type String
38396      */
38397     this.id = config.id;
38398     /** @private */
38399     this.disabled = false;
38400     /** @private */
38401     this.text = config.text;
38402     /** @private */
38403     this.loaded = false;
38404     this.closable = config.closable;
38405
38406     /**
38407      * The body element for this TabPanelItem.
38408      * @type Roo.Element
38409      */
38410     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38411     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38412     this.bodyEl.setStyle("display", "block");
38413     this.bodyEl.setStyle("zoom", "1");
38414     //this.hideAction();
38415
38416     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38417     /** @private */
38418     this.el = Roo.get(els.el);
38419     this.inner = Roo.get(els.inner, true);
38420     this.textEl = Roo.get(this.el.dom.firstChild, true);
38421     this.pnode = Roo.get(els.el.parentNode, true);
38422 //    this.el.on("mousedown", this.onTabMouseDown, this);
38423     this.el.on("click", this.onTabClick, this);
38424     /** @private */
38425     if(config.closable){
38426         var c = Roo.get(els.close, true);
38427         c.dom.title = this.closeText;
38428         c.addClassOnOver("close-over");
38429         c.on("click", this.closeClick, this);
38430      }
38431
38432     this.addEvents({
38433          /**
38434          * @event activate
38435          * Fires when this tab becomes the active tab.
38436          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38437          * @param {Roo.TabPanelItem} this
38438          */
38439         "activate": true,
38440         /**
38441          * @event beforeclose
38442          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38443          * @param {Roo.TabPanelItem} this
38444          * @param {Object} e Set cancel to true on this object to cancel the close.
38445          */
38446         "beforeclose": true,
38447         /**
38448          * @event close
38449          * Fires when this tab is closed.
38450          * @param {Roo.TabPanelItem} this
38451          */
38452          "close": true,
38453         /**
38454          * @event deactivate
38455          * Fires when this tab is no longer the active tab.
38456          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38457          * @param {Roo.TabPanelItem} this
38458          */
38459          "deactivate" : true
38460     });
38461     this.hidden = false;
38462
38463     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38464 };
38465
38466 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38467            {
38468     purgeListeners : function(){
38469        Roo.util.Observable.prototype.purgeListeners.call(this);
38470        this.el.removeAllListeners();
38471     },
38472     /**
38473      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38474      */
38475     show : function(){
38476         this.pnode.addClass("active");
38477         this.showAction();
38478         if(Roo.isOpera){
38479             this.tabPanel.stripWrap.repaint();
38480         }
38481         this.fireEvent("activate", this.tabPanel, this);
38482     },
38483
38484     /**
38485      * Returns true if this tab is the active tab.
38486      * @return {Boolean}
38487      */
38488     isActive : function(){
38489         return this.tabPanel.getActiveTab() == this;
38490     },
38491
38492     /**
38493      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38494      */
38495     hide : function(){
38496         this.pnode.removeClass("active");
38497         this.hideAction();
38498         this.fireEvent("deactivate", this.tabPanel, this);
38499     },
38500
38501     hideAction : function(){
38502         this.bodyEl.hide();
38503         this.bodyEl.setStyle("position", "absolute");
38504         this.bodyEl.setLeft("-20000px");
38505         this.bodyEl.setTop("-20000px");
38506     },
38507
38508     showAction : function(){
38509         this.bodyEl.setStyle("position", "relative");
38510         this.bodyEl.setTop("");
38511         this.bodyEl.setLeft("");
38512         this.bodyEl.show();
38513     },
38514
38515     /**
38516      * Set the tooltip for the tab.
38517      * @param {String} tooltip The tab's tooltip
38518      */
38519     setTooltip : function(text){
38520         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38521             this.textEl.dom.qtip = text;
38522             this.textEl.dom.removeAttribute('title');
38523         }else{
38524             this.textEl.dom.title = text;
38525         }
38526     },
38527
38528     onTabClick : function(e){
38529         e.preventDefault();
38530         this.tabPanel.activate(this.id);
38531     },
38532
38533     onTabMouseDown : function(e){
38534         e.preventDefault();
38535         this.tabPanel.activate(this.id);
38536     },
38537 /*
38538     getWidth : function(){
38539         return this.inner.getWidth();
38540     },
38541
38542     setWidth : function(width){
38543         var iwidth = width - this.pnode.getPadding("lr");
38544         this.inner.setWidth(iwidth);
38545         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38546         this.pnode.setWidth(width);
38547     },
38548 */
38549     /**
38550      * Show or hide the tab
38551      * @param {Boolean} hidden True to hide or false to show.
38552      */
38553     setHidden : function(hidden){
38554         this.hidden = hidden;
38555         this.pnode.setStyle("display", hidden ? "none" : "");
38556     },
38557
38558     /**
38559      * Returns true if this tab is "hidden"
38560      * @return {Boolean}
38561      */
38562     isHidden : function(){
38563         return this.hidden;
38564     },
38565
38566     /**
38567      * Returns the text for this tab
38568      * @return {String}
38569      */
38570     getText : function(){
38571         return this.text;
38572     },
38573     /*
38574     autoSize : function(){
38575         //this.el.beginMeasure();
38576         this.textEl.setWidth(1);
38577         /*
38578          *  #2804 [new] Tabs in Roojs
38579          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38580          */
38581         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38582         //this.el.endMeasure();
38583     //},
38584
38585     /**
38586      * Sets the text for the tab (Note: this also sets the tooltip text)
38587      * @param {String} text The tab's text and tooltip
38588      */
38589     setText : function(text){
38590         this.text = text;
38591         this.textEl.update(text);
38592         this.setTooltip(text);
38593         //if(!this.tabPanel.resizeTabs){
38594         //    this.autoSize();
38595         //}
38596     },
38597     /**
38598      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38599      */
38600     activate : function(){
38601         this.tabPanel.activate(this.id);
38602     },
38603
38604     /**
38605      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38606      */
38607     disable : function(){
38608         if(this.tabPanel.active != this){
38609             this.disabled = true;
38610             this.pnode.addClass("disabled");
38611         }
38612     },
38613
38614     /**
38615      * Enables this TabPanelItem if it was previously disabled.
38616      */
38617     enable : function(){
38618         this.disabled = false;
38619         this.pnode.removeClass("disabled");
38620     },
38621
38622     /**
38623      * Sets the content for this TabPanelItem.
38624      * @param {String} content The content
38625      * @param {Boolean} loadScripts true to look for and load scripts
38626      */
38627     setContent : function(content, loadScripts){
38628         this.bodyEl.update(content, loadScripts);
38629     },
38630
38631     /**
38632      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38633      * @return {Roo.UpdateManager} The UpdateManager
38634      */
38635     getUpdateManager : function(){
38636         return this.bodyEl.getUpdateManager();
38637     },
38638
38639     /**
38640      * Set a URL to be used to load the content for this TabPanelItem.
38641      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38642      * @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)
38643      * @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)
38644      * @return {Roo.UpdateManager} The UpdateManager
38645      */
38646     setUrl : function(url, params, loadOnce){
38647         if(this.refreshDelegate){
38648             this.un('activate', this.refreshDelegate);
38649         }
38650         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38651         this.on("activate", this.refreshDelegate);
38652         return this.bodyEl.getUpdateManager();
38653     },
38654
38655     /** @private */
38656     _handleRefresh : function(url, params, loadOnce){
38657         if(!loadOnce || !this.loaded){
38658             var updater = this.bodyEl.getUpdateManager();
38659             updater.update(url, params, this._setLoaded.createDelegate(this));
38660         }
38661     },
38662
38663     /**
38664      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38665      *   Will fail silently if the setUrl method has not been called.
38666      *   This does not activate the panel, just updates its content.
38667      */
38668     refresh : function(){
38669         if(this.refreshDelegate){
38670            this.loaded = false;
38671            this.refreshDelegate();
38672         }
38673     },
38674
38675     /** @private */
38676     _setLoaded : function(){
38677         this.loaded = true;
38678     },
38679
38680     /** @private */
38681     closeClick : function(e){
38682         var o = {};
38683         e.stopEvent();
38684         this.fireEvent("beforeclose", this, o);
38685         if(o.cancel !== true){
38686             this.tabPanel.removeTab(this.id);
38687         }
38688     },
38689     /**
38690      * The text displayed in the tooltip for the close icon.
38691      * @type String
38692      */
38693     closeText : "Close this tab"
38694 });
38695 /**
38696 *    This script refer to:
38697 *    Title: International Telephone Input
38698 *    Author: Jack O'Connor
38699 *    Code version:  v12.1.12
38700 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38701 **/
38702
38703 Roo.bootstrap.PhoneInputData = function() {
38704     var d = [
38705       [
38706         "Afghanistan (‫افغانستان‬‎)",
38707         "af",
38708         "93"
38709       ],
38710       [
38711         "Albania (Shqipëri)",
38712         "al",
38713         "355"
38714       ],
38715       [
38716         "Algeria (‫الجزائر‬‎)",
38717         "dz",
38718         "213"
38719       ],
38720       [
38721         "American Samoa",
38722         "as",
38723         "1684"
38724       ],
38725       [
38726         "Andorra",
38727         "ad",
38728         "376"
38729       ],
38730       [
38731         "Angola",
38732         "ao",
38733         "244"
38734       ],
38735       [
38736         "Anguilla",
38737         "ai",
38738         "1264"
38739       ],
38740       [
38741         "Antigua and Barbuda",
38742         "ag",
38743         "1268"
38744       ],
38745       [
38746         "Argentina",
38747         "ar",
38748         "54"
38749       ],
38750       [
38751         "Armenia (Հայաստան)",
38752         "am",
38753         "374"
38754       ],
38755       [
38756         "Aruba",
38757         "aw",
38758         "297"
38759       ],
38760       [
38761         "Australia",
38762         "au",
38763         "61",
38764         0
38765       ],
38766       [
38767         "Austria (Österreich)",
38768         "at",
38769         "43"
38770       ],
38771       [
38772         "Azerbaijan (Azərbaycan)",
38773         "az",
38774         "994"
38775       ],
38776       [
38777         "Bahamas",
38778         "bs",
38779         "1242"
38780       ],
38781       [
38782         "Bahrain (‫البحرين‬‎)",
38783         "bh",
38784         "973"
38785       ],
38786       [
38787         "Bangladesh (বাংলাদেশ)",
38788         "bd",
38789         "880"
38790       ],
38791       [
38792         "Barbados",
38793         "bb",
38794         "1246"
38795       ],
38796       [
38797         "Belarus (Беларусь)",
38798         "by",
38799         "375"
38800       ],
38801       [
38802         "Belgium (België)",
38803         "be",
38804         "32"
38805       ],
38806       [
38807         "Belize",
38808         "bz",
38809         "501"
38810       ],
38811       [
38812         "Benin (Bénin)",
38813         "bj",
38814         "229"
38815       ],
38816       [
38817         "Bermuda",
38818         "bm",
38819         "1441"
38820       ],
38821       [
38822         "Bhutan (འབྲུག)",
38823         "bt",
38824         "975"
38825       ],
38826       [
38827         "Bolivia",
38828         "bo",
38829         "591"
38830       ],
38831       [
38832         "Bosnia and Herzegovina (Босна и Херцеговина)",
38833         "ba",
38834         "387"
38835       ],
38836       [
38837         "Botswana",
38838         "bw",
38839         "267"
38840       ],
38841       [
38842         "Brazil (Brasil)",
38843         "br",
38844         "55"
38845       ],
38846       [
38847         "British Indian Ocean Territory",
38848         "io",
38849         "246"
38850       ],
38851       [
38852         "British Virgin Islands",
38853         "vg",
38854         "1284"
38855       ],
38856       [
38857         "Brunei",
38858         "bn",
38859         "673"
38860       ],
38861       [
38862         "Bulgaria (България)",
38863         "bg",
38864         "359"
38865       ],
38866       [
38867         "Burkina Faso",
38868         "bf",
38869         "226"
38870       ],
38871       [
38872         "Burundi (Uburundi)",
38873         "bi",
38874         "257"
38875       ],
38876       [
38877         "Cambodia (កម្ពុជា)",
38878         "kh",
38879         "855"
38880       ],
38881       [
38882         "Cameroon (Cameroun)",
38883         "cm",
38884         "237"
38885       ],
38886       [
38887         "Canada",
38888         "ca",
38889         "1",
38890         1,
38891         ["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"]
38892       ],
38893       [
38894         "Cape Verde (Kabu Verdi)",
38895         "cv",
38896         "238"
38897       ],
38898       [
38899         "Caribbean Netherlands",
38900         "bq",
38901         "599",
38902         1
38903       ],
38904       [
38905         "Cayman Islands",
38906         "ky",
38907         "1345"
38908       ],
38909       [
38910         "Central African Republic (République centrafricaine)",
38911         "cf",
38912         "236"
38913       ],
38914       [
38915         "Chad (Tchad)",
38916         "td",
38917         "235"
38918       ],
38919       [
38920         "Chile",
38921         "cl",
38922         "56"
38923       ],
38924       [
38925         "China (中国)",
38926         "cn",
38927         "86"
38928       ],
38929       [
38930         "Christmas Island",
38931         "cx",
38932         "61",
38933         2
38934       ],
38935       [
38936         "Cocos (Keeling) Islands",
38937         "cc",
38938         "61",
38939         1
38940       ],
38941       [
38942         "Colombia",
38943         "co",
38944         "57"
38945       ],
38946       [
38947         "Comoros (‫جزر القمر‬‎)",
38948         "km",
38949         "269"
38950       ],
38951       [
38952         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38953         "cd",
38954         "243"
38955       ],
38956       [
38957         "Congo (Republic) (Congo-Brazzaville)",
38958         "cg",
38959         "242"
38960       ],
38961       [
38962         "Cook Islands",
38963         "ck",
38964         "682"
38965       ],
38966       [
38967         "Costa Rica",
38968         "cr",
38969         "506"
38970       ],
38971       [
38972         "Côte d’Ivoire",
38973         "ci",
38974         "225"
38975       ],
38976       [
38977         "Croatia (Hrvatska)",
38978         "hr",
38979         "385"
38980       ],
38981       [
38982         "Cuba",
38983         "cu",
38984         "53"
38985       ],
38986       [
38987         "Curaçao",
38988         "cw",
38989         "599",
38990         0
38991       ],
38992       [
38993         "Cyprus (Κύπρος)",
38994         "cy",
38995         "357"
38996       ],
38997       [
38998         "Czech Republic (Česká republika)",
38999         "cz",
39000         "420"
39001       ],
39002       [
39003         "Denmark (Danmark)",
39004         "dk",
39005         "45"
39006       ],
39007       [
39008         "Djibouti",
39009         "dj",
39010         "253"
39011       ],
39012       [
39013         "Dominica",
39014         "dm",
39015         "1767"
39016       ],
39017       [
39018         "Dominican Republic (República Dominicana)",
39019         "do",
39020         "1",
39021         2,
39022         ["809", "829", "849"]
39023       ],
39024       [
39025         "Ecuador",
39026         "ec",
39027         "593"
39028       ],
39029       [
39030         "Egypt (‫مصر‬‎)",
39031         "eg",
39032         "20"
39033       ],
39034       [
39035         "El Salvador",
39036         "sv",
39037         "503"
39038       ],
39039       [
39040         "Equatorial Guinea (Guinea Ecuatorial)",
39041         "gq",
39042         "240"
39043       ],
39044       [
39045         "Eritrea",
39046         "er",
39047         "291"
39048       ],
39049       [
39050         "Estonia (Eesti)",
39051         "ee",
39052         "372"
39053       ],
39054       [
39055         "Ethiopia",
39056         "et",
39057         "251"
39058       ],
39059       [
39060         "Falkland Islands (Islas Malvinas)",
39061         "fk",
39062         "500"
39063       ],
39064       [
39065         "Faroe Islands (Føroyar)",
39066         "fo",
39067         "298"
39068       ],
39069       [
39070         "Fiji",
39071         "fj",
39072         "679"
39073       ],
39074       [
39075         "Finland (Suomi)",
39076         "fi",
39077         "358",
39078         0
39079       ],
39080       [
39081         "France",
39082         "fr",
39083         "33"
39084       ],
39085       [
39086         "French Guiana (Guyane française)",
39087         "gf",
39088         "594"
39089       ],
39090       [
39091         "French Polynesia (Polynésie française)",
39092         "pf",
39093         "689"
39094       ],
39095       [
39096         "Gabon",
39097         "ga",
39098         "241"
39099       ],
39100       [
39101         "Gambia",
39102         "gm",
39103         "220"
39104       ],
39105       [
39106         "Georgia (საქართველო)",
39107         "ge",
39108         "995"
39109       ],
39110       [
39111         "Germany (Deutschland)",
39112         "de",
39113         "49"
39114       ],
39115       [
39116         "Ghana (Gaana)",
39117         "gh",
39118         "233"
39119       ],
39120       [
39121         "Gibraltar",
39122         "gi",
39123         "350"
39124       ],
39125       [
39126         "Greece (Ελλάδα)",
39127         "gr",
39128         "30"
39129       ],
39130       [
39131         "Greenland (Kalaallit Nunaat)",
39132         "gl",
39133         "299"
39134       ],
39135       [
39136         "Grenada",
39137         "gd",
39138         "1473"
39139       ],
39140       [
39141         "Guadeloupe",
39142         "gp",
39143         "590",
39144         0
39145       ],
39146       [
39147         "Guam",
39148         "gu",
39149         "1671"
39150       ],
39151       [
39152         "Guatemala",
39153         "gt",
39154         "502"
39155       ],
39156       [
39157         "Guernsey",
39158         "gg",
39159         "44",
39160         1
39161       ],
39162       [
39163         "Guinea (Guinée)",
39164         "gn",
39165         "224"
39166       ],
39167       [
39168         "Guinea-Bissau (Guiné Bissau)",
39169         "gw",
39170         "245"
39171       ],
39172       [
39173         "Guyana",
39174         "gy",
39175         "592"
39176       ],
39177       [
39178         "Haiti",
39179         "ht",
39180         "509"
39181       ],
39182       [
39183         "Honduras",
39184         "hn",
39185         "504"
39186       ],
39187       [
39188         "Hong Kong (香港)",
39189         "hk",
39190         "852"
39191       ],
39192       [
39193         "Hungary (Magyarország)",
39194         "hu",
39195         "36"
39196       ],
39197       [
39198         "Iceland (Ísland)",
39199         "is",
39200         "354"
39201       ],
39202       [
39203         "India (भारत)",
39204         "in",
39205         "91"
39206       ],
39207       [
39208         "Indonesia",
39209         "id",
39210         "62"
39211       ],
39212       [
39213         "Iran (‫ایران‬‎)",
39214         "ir",
39215         "98"
39216       ],
39217       [
39218         "Iraq (‫العراق‬‎)",
39219         "iq",
39220         "964"
39221       ],
39222       [
39223         "Ireland",
39224         "ie",
39225         "353"
39226       ],
39227       [
39228         "Isle of Man",
39229         "im",
39230         "44",
39231         2
39232       ],
39233       [
39234         "Israel (‫ישראל‬‎)",
39235         "il",
39236         "972"
39237       ],
39238       [
39239         "Italy (Italia)",
39240         "it",
39241         "39",
39242         0
39243       ],
39244       [
39245         "Jamaica",
39246         "jm",
39247         "1876"
39248       ],
39249       [
39250         "Japan (日本)",
39251         "jp",
39252         "81"
39253       ],
39254       [
39255         "Jersey",
39256         "je",
39257         "44",
39258         3
39259       ],
39260       [
39261         "Jordan (‫الأردن‬‎)",
39262         "jo",
39263         "962"
39264       ],
39265       [
39266         "Kazakhstan (Казахстан)",
39267         "kz",
39268         "7",
39269         1
39270       ],
39271       [
39272         "Kenya",
39273         "ke",
39274         "254"
39275       ],
39276       [
39277         "Kiribati",
39278         "ki",
39279         "686"
39280       ],
39281       [
39282         "Kosovo",
39283         "xk",
39284         "383"
39285       ],
39286       [
39287         "Kuwait (‫الكويت‬‎)",
39288         "kw",
39289         "965"
39290       ],
39291       [
39292         "Kyrgyzstan (Кыргызстан)",
39293         "kg",
39294         "996"
39295       ],
39296       [
39297         "Laos (ລາວ)",
39298         "la",
39299         "856"
39300       ],
39301       [
39302         "Latvia (Latvija)",
39303         "lv",
39304         "371"
39305       ],
39306       [
39307         "Lebanon (‫لبنان‬‎)",
39308         "lb",
39309         "961"
39310       ],
39311       [
39312         "Lesotho",
39313         "ls",
39314         "266"
39315       ],
39316       [
39317         "Liberia",
39318         "lr",
39319         "231"
39320       ],
39321       [
39322         "Libya (‫ليبيا‬‎)",
39323         "ly",
39324         "218"
39325       ],
39326       [
39327         "Liechtenstein",
39328         "li",
39329         "423"
39330       ],
39331       [
39332         "Lithuania (Lietuva)",
39333         "lt",
39334         "370"
39335       ],
39336       [
39337         "Luxembourg",
39338         "lu",
39339         "352"
39340       ],
39341       [
39342         "Macau (澳門)",
39343         "mo",
39344         "853"
39345       ],
39346       [
39347         "Macedonia (FYROM) (Македонија)",
39348         "mk",
39349         "389"
39350       ],
39351       [
39352         "Madagascar (Madagasikara)",
39353         "mg",
39354         "261"
39355       ],
39356       [
39357         "Malawi",
39358         "mw",
39359         "265"
39360       ],
39361       [
39362         "Malaysia",
39363         "my",
39364         "60"
39365       ],
39366       [
39367         "Maldives",
39368         "mv",
39369         "960"
39370       ],
39371       [
39372         "Mali",
39373         "ml",
39374         "223"
39375       ],
39376       [
39377         "Malta",
39378         "mt",
39379         "356"
39380       ],
39381       [
39382         "Marshall Islands",
39383         "mh",
39384         "692"
39385       ],
39386       [
39387         "Martinique",
39388         "mq",
39389         "596"
39390       ],
39391       [
39392         "Mauritania (‫موريتانيا‬‎)",
39393         "mr",
39394         "222"
39395       ],
39396       [
39397         "Mauritius (Moris)",
39398         "mu",
39399         "230"
39400       ],
39401       [
39402         "Mayotte",
39403         "yt",
39404         "262",
39405         1
39406       ],
39407       [
39408         "Mexico (México)",
39409         "mx",
39410         "52"
39411       ],
39412       [
39413         "Micronesia",
39414         "fm",
39415         "691"
39416       ],
39417       [
39418         "Moldova (Republica Moldova)",
39419         "md",
39420         "373"
39421       ],
39422       [
39423         "Monaco",
39424         "mc",
39425         "377"
39426       ],
39427       [
39428         "Mongolia (Монгол)",
39429         "mn",
39430         "976"
39431       ],
39432       [
39433         "Montenegro (Crna Gora)",
39434         "me",
39435         "382"
39436       ],
39437       [
39438         "Montserrat",
39439         "ms",
39440         "1664"
39441       ],
39442       [
39443         "Morocco (‫المغرب‬‎)",
39444         "ma",
39445         "212",
39446         0
39447       ],
39448       [
39449         "Mozambique (Moçambique)",
39450         "mz",
39451         "258"
39452       ],
39453       [
39454         "Myanmar (Burma) (မြန်မာ)",
39455         "mm",
39456         "95"
39457       ],
39458       [
39459         "Namibia (Namibië)",
39460         "na",
39461         "264"
39462       ],
39463       [
39464         "Nauru",
39465         "nr",
39466         "674"
39467       ],
39468       [
39469         "Nepal (नेपाल)",
39470         "np",
39471         "977"
39472       ],
39473       [
39474         "Netherlands (Nederland)",
39475         "nl",
39476         "31"
39477       ],
39478       [
39479         "New Caledonia (Nouvelle-Calédonie)",
39480         "nc",
39481         "687"
39482       ],
39483       [
39484         "New Zealand",
39485         "nz",
39486         "64"
39487       ],
39488       [
39489         "Nicaragua",
39490         "ni",
39491         "505"
39492       ],
39493       [
39494         "Niger (Nijar)",
39495         "ne",
39496         "227"
39497       ],
39498       [
39499         "Nigeria",
39500         "ng",
39501         "234"
39502       ],
39503       [
39504         "Niue",
39505         "nu",
39506         "683"
39507       ],
39508       [
39509         "Norfolk Island",
39510         "nf",
39511         "672"
39512       ],
39513       [
39514         "North Korea (조선 민주주의 인민 공화국)",
39515         "kp",
39516         "850"
39517       ],
39518       [
39519         "Northern Mariana Islands",
39520         "mp",
39521         "1670"
39522       ],
39523       [
39524         "Norway (Norge)",
39525         "no",
39526         "47",
39527         0
39528       ],
39529       [
39530         "Oman (‫عُمان‬‎)",
39531         "om",
39532         "968"
39533       ],
39534       [
39535         "Pakistan (‫پاکستان‬‎)",
39536         "pk",
39537         "92"
39538       ],
39539       [
39540         "Palau",
39541         "pw",
39542         "680"
39543       ],
39544       [
39545         "Palestine (‫فلسطين‬‎)",
39546         "ps",
39547         "970"
39548       ],
39549       [
39550         "Panama (Panamá)",
39551         "pa",
39552         "507"
39553       ],
39554       [
39555         "Papua New Guinea",
39556         "pg",
39557         "675"
39558       ],
39559       [
39560         "Paraguay",
39561         "py",
39562         "595"
39563       ],
39564       [
39565         "Peru (Perú)",
39566         "pe",
39567         "51"
39568       ],
39569       [
39570         "Philippines",
39571         "ph",
39572         "63"
39573       ],
39574       [
39575         "Poland (Polska)",
39576         "pl",
39577         "48"
39578       ],
39579       [
39580         "Portugal",
39581         "pt",
39582         "351"
39583       ],
39584       [
39585         "Puerto Rico",
39586         "pr",
39587         "1",
39588         3,
39589         ["787", "939"]
39590       ],
39591       [
39592         "Qatar (‫قطر‬‎)",
39593         "qa",
39594         "974"
39595       ],
39596       [
39597         "Réunion (La Réunion)",
39598         "re",
39599         "262",
39600         0
39601       ],
39602       [
39603         "Romania (România)",
39604         "ro",
39605         "40"
39606       ],
39607       [
39608         "Russia (Россия)",
39609         "ru",
39610         "7",
39611         0
39612       ],
39613       [
39614         "Rwanda",
39615         "rw",
39616         "250"
39617       ],
39618       [
39619         "Saint Barthélemy",
39620         "bl",
39621         "590",
39622         1
39623       ],
39624       [
39625         "Saint Helena",
39626         "sh",
39627         "290"
39628       ],
39629       [
39630         "Saint Kitts and Nevis",
39631         "kn",
39632         "1869"
39633       ],
39634       [
39635         "Saint Lucia",
39636         "lc",
39637         "1758"
39638       ],
39639       [
39640         "Saint Martin (Saint-Martin (partie française))",
39641         "mf",
39642         "590",
39643         2
39644       ],
39645       [
39646         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39647         "pm",
39648         "508"
39649       ],
39650       [
39651         "Saint Vincent and the Grenadines",
39652         "vc",
39653         "1784"
39654       ],
39655       [
39656         "Samoa",
39657         "ws",
39658         "685"
39659       ],
39660       [
39661         "San Marino",
39662         "sm",
39663         "378"
39664       ],
39665       [
39666         "São Tomé and Príncipe (São Tomé e Príncipe)",
39667         "st",
39668         "239"
39669       ],
39670       [
39671         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39672         "sa",
39673         "966"
39674       ],
39675       [
39676         "Senegal (Sénégal)",
39677         "sn",
39678         "221"
39679       ],
39680       [
39681         "Serbia (Србија)",
39682         "rs",
39683         "381"
39684       ],
39685       [
39686         "Seychelles",
39687         "sc",
39688         "248"
39689       ],
39690       [
39691         "Sierra Leone",
39692         "sl",
39693         "232"
39694       ],
39695       [
39696         "Singapore",
39697         "sg",
39698         "65"
39699       ],
39700       [
39701         "Sint Maarten",
39702         "sx",
39703         "1721"
39704       ],
39705       [
39706         "Slovakia (Slovensko)",
39707         "sk",
39708         "421"
39709       ],
39710       [
39711         "Slovenia (Slovenija)",
39712         "si",
39713         "386"
39714       ],
39715       [
39716         "Solomon Islands",
39717         "sb",
39718         "677"
39719       ],
39720       [
39721         "Somalia (Soomaaliya)",
39722         "so",
39723         "252"
39724       ],
39725       [
39726         "South Africa",
39727         "za",
39728         "27"
39729       ],
39730       [
39731         "South Korea (대한민국)",
39732         "kr",
39733         "82"
39734       ],
39735       [
39736         "South Sudan (‫جنوب السودان‬‎)",
39737         "ss",
39738         "211"
39739       ],
39740       [
39741         "Spain (España)",
39742         "es",
39743         "34"
39744       ],
39745       [
39746         "Sri Lanka (ශ්‍රී ලංකාව)",
39747         "lk",
39748         "94"
39749       ],
39750       [
39751         "Sudan (‫السودان‬‎)",
39752         "sd",
39753         "249"
39754       ],
39755       [
39756         "Suriname",
39757         "sr",
39758         "597"
39759       ],
39760       [
39761         "Svalbard and Jan Mayen",
39762         "sj",
39763         "47",
39764         1
39765       ],
39766       [
39767         "Swaziland",
39768         "sz",
39769         "268"
39770       ],
39771       [
39772         "Sweden (Sverige)",
39773         "se",
39774         "46"
39775       ],
39776       [
39777         "Switzerland (Schweiz)",
39778         "ch",
39779         "41"
39780       ],
39781       [
39782         "Syria (‫سوريا‬‎)",
39783         "sy",
39784         "963"
39785       ],
39786       [
39787         "Taiwan (台灣)",
39788         "tw",
39789         "886"
39790       ],
39791       [
39792         "Tajikistan",
39793         "tj",
39794         "992"
39795       ],
39796       [
39797         "Tanzania",
39798         "tz",
39799         "255"
39800       ],
39801       [
39802         "Thailand (ไทย)",
39803         "th",
39804         "66"
39805       ],
39806       [
39807         "Timor-Leste",
39808         "tl",
39809         "670"
39810       ],
39811       [
39812         "Togo",
39813         "tg",
39814         "228"
39815       ],
39816       [
39817         "Tokelau",
39818         "tk",
39819         "690"
39820       ],
39821       [
39822         "Tonga",
39823         "to",
39824         "676"
39825       ],
39826       [
39827         "Trinidad and Tobago",
39828         "tt",
39829         "1868"
39830       ],
39831       [
39832         "Tunisia (‫تونس‬‎)",
39833         "tn",
39834         "216"
39835       ],
39836       [
39837         "Turkey (Türkiye)",
39838         "tr",
39839         "90"
39840       ],
39841       [
39842         "Turkmenistan",
39843         "tm",
39844         "993"
39845       ],
39846       [
39847         "Turks and Caicos Islands",
39848         "tc",
39849         "1649"
39850       ],
39851       [
39852         "Tuvalu",
39853         "tv",
39854         "688"
39855       ],
39856       [
39857         "U.S. Virgin Islands",
39858         "vi",
39859         "1340"
39860       ],
39861       [
39862         "Uganda",
39863         "ug",
39864         "256"
39865       ],
39866       [
39867         "Ukraine (Україна)",
39868         "ua",
39869         "380"
39870       ],
39871       [
39872         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39873         "ae",
39874         "971"
39875       ],
39876       [
39877         "United Kingdom",
39878         "gb",
39879         "44",
39880         0
39881       ],
39882       [
39883         "United States",
39884         "us",
39885         "1",
39886         0
39887       ],
39888       [
39889         "Uruguay",
39890         "uy",
39891         "598"
39892       ],
39893       [
39894         "Uzbekistan (Oʻzbekiston)",
39895         "uz",
39896         "998"
39897       ],
39898       [
39899         "Vanuatu",
39900         "vu",
39901         "678"
39902       ],
39903       [
39904         "Vatican City (Città del Vaticano)",
39905         "va",
39906         "39",
39907         1
39908       ],
39909       [
39910         "Venezuela",
39911         "ve",
39912         "58"
39913       ],
39914       [
39915         "Vietnam (Việt Nam)",
39916         "vn",
39917         "84"
39918       ],
39919       [
39920         "Wallis and Futuna (Wallis-et-Futuna)",
39921         "wf",
39922         "681"
39923       ],
39924       [
39925         "Western Sahara (‫الصحراء الغربية‬‎)",
39926         "eh",
39927         "212",
39928         1
39929       ],
39930       [
39931         "Yemen (‫اليمن‬‎)",
39932         "ye",
39933         "967"
39934       ],
39935       [
39936         "Zambia",
39937         "zm",
39938         "260"
39939       ],
39940       [
39941         "Zimbabwe",
39942         "zw",
39943         "263"
39944       ],
39945       [
39946         "Åland Islands",
39947         "ax",
39948         "358",
39949         1
39950       ]
39951   ];
39952   
39953   return d;
39954 }/**
39955 *    This script refer to:
39956 *    Title: International Telephone Input
39957 *    Author: Jack O'Connor
39958 *    Code version:  v12.1.12
39959 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39960 **/
39961
39962 /**
39963  * @class Roo.bootstrap.PhoneInput
39964  * @extends Roo.bootstrap.TriggerField
39965  * An input with International dial-code selection
39966  
39967  * @cfg {String} defaultDialCode default '+852'
39968  * @cfg {Array} preferedCountries default []
39969   
39970  * @constructor
39971  * Create a new PhoneInput.
39972  * @param {Object} config Configuration options
39973  */
39974
39975 Roo.bootstrap.PhoneInput = function(config) {
39976     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39977 };
39978
39979 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39980         
39981         listWidth: undefined,
39982         
39983         selectedClass: 'active',
39984         
39985         invalidClass : "has-warning",
39986         
39987         validClass: 'has-success',
39988         
39989         allowed: '0123456789',
39990         
39991         max_length: 15,
39992         
39993         /**
39994          * @cfg {String} defaultDialCode The default dial code when initializing the input
39995          */
39996         defaultDialCode: '+852',
39997         
39998         /**
39999          * @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
40000          */
40001         preferedCountries: false,
40002         
40003         getAutoCreate : function()
40004         {
40005             var data = Roo.bootstrap.PhoneInputData();
40006             var align = this.labelAlign || this.parentLabelAlign();
40007             var id = Roo.id();
40008             
40009             this.allCountries = [];
40010             this.dialCodeMapping = [];
40011             
40012             for (var i = 0; i < data.length; i++) {
40013               var c = data[i];
40014               this.allCountries[i] = {
40015                 name: c[0],
40016                 iso2: c[1],
40017                 dialCode: c[2],
40018                 priority: c[3] || 0,
40019                 areaCodes: c[4] || null
40020               };
40021               this.dialCodeMapping[c[2]] = {
40022                   name: c[0],
40023                   iso2: c[1],
40024                   priority: c[3] || 0,
40025                   areaCodes: c[4] || null
40026               };
40027             }
40028             
40029             var cfg = {
40030                 cls: 'form-group',
40031                 cn: []
40032             };
40033             
40034             var input =  {
40035                 tag: 'input',
40036                 id : id,
40037                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40038                 maxlength: this.max_length,
40039                 cls : 'form-control tel-input',
40040                 autocomplete: 'new-password'
40041             };
40042             
40043             var hiddenInput = {
40044                 tag: 'input',
40045                 type: 'hidden',
40046                 cls: 'hidden-tel-input'
40047             };
40048             
40049             if (this.name) {
40050                 hiddenInput.name = this.name;
40051             }
40052             
40053             if (this.disabled) {
40054                 input.disabled = true;
40055             }
40056             
40057             var flag_container = {
40058                 tag: 'div',
40059                 cls: 'flag-box',
40060                 cn: [
40061                     {
40062                         tag: 'div',
40063                         cls: 'flag'
40064                     },
40065                     {
40066                         tag: 'div',
40067                         cls: 'caret'
40068                     }
40069                 ]
40070             };
40071             
40072             var box = {
40073                 tag: 'div',
40074                 cls: this.hasFeedback ? 'has-feedback' : '',
40075                 cn: [
40076                     hiddenInput,
40077                     input,
40078                     {
40079                         tag: 'input',
40080                         cls: 'dial-code-holder',
40081                         disabled: true
40082                     }
40083                 ]
40084             };
40085             
40086             var container = {
40087                 cls: 'roo-select2-container input-group',
40088                 cn: [
40089                     flag_container,
40090                     box
40091                 ]
40092             };
40093             
40094             if (this.fieldLabel.length) {
40095                 var indicator = {
40096                     tag: 'i',
40097                     tooltip: 'This field is required'
40098                 };
40099                 
40100                 var label = {
40101                     tag: 'label',
40102                     'for':  id,
40103                     cls: 'control-label',
40104                     cn: []
40105                 };
40106                 
40107                 var label_text = {
40108                     tag: 'span',
40109                     html: this.fieldLabel
40110                 };
40111                 
40112                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40113                 label.cn = [
40114                     indicator,
40115                     label_text
40116                 ];
40117                 
40118                 if(this.indicatorpos == 'right') {
40119                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40120                     label.cn = [
40121                         label_text,
40122                         indicator
40123                     ];
40124                 }
40125                 
40126                 if(align == 'left') {
40127                     container = {
40128                         tag: 'div',
40129                         cn: [
40130                             container
40131                         ]
40132                     };
40133                     
40134                     if(this.labelWidth > 12){
40135                         label.style = "width: " + this.labelWidth + 'px';
40136                     }
40137                     if(this.labelWidth < 13 && this.labelmd == 0){
40138                         this.labelmd = this.labelWidth;
40139                     }
40140                     if(this.labellg > 0){
40141                         label.cls += ' col-lg-' + this.labellg;
40142                         input.cls += ' col-lg-' + (12 - this.labellg);
40143                     }
40144                     if(this.labelmd > 0){
40145                         label.cls += ' col-md-' + this.labelmd;
40146                         container.cls += ' col-md-' + (12 - this.labelmd);
40147                     }
40148                     if(this.labelsm > 0){
40149                         label.cls += ' col-sm-' + this.labelsm;
40150                         container.cls += ' col-sm-' + (12 - this.labelsm);
40151                     }
40152                     if(this.labelxs > 0){
40153                         label.cls += ' col-xs-' + this.labelxs;
40154                         container.cls += ' col-xs-' + (12 - this.labelxs);
40155                     }
40156                 }
40157             }
40158             
40159             cfg.cn = [
40160                 label,
40161                 container
40162             ];
40163             
40164             var settings = this;
40165             
40166             ['xs','sm','md','lg'].map(function(size){
40167                 if (settings[size]) {
40168                     cfg.cls += ' col-' + size + '-' + settings[size];
40169                 }
40170             });
40171             
40172             this.store = new Roo.data.Store({
40173                 proxy : new Roo.data.MemoryProxy({}),
40174                 reader : new Roo.data.JsonReader({
40175                     fields : [
40176                         {
40177                             'name' : 'name',
40178                             'type' : 'string'
40179                         },
40180                         {
40181                             'name' : 'iso2',
40182                             'type' : 'string'
40183                         },
40184                         {
40185                             'name' : 'dialCode',
40186                             'type' : 'string'
40187                         },
40188                         {
40189                             'name' : 'priority',
40190                             'type' : 'string'
40191                         },
40192                         {
40193                             'name' : 'areaCodes',
40194                             'type' : 'string'
40195                         }
40196                     ]
40197                 })
40198             });
40199             
40200             if(!this.preferedCountries) {
40201                 this.preferedCountries = [
40202                     'hk',
40203                     'gb',
40204                     'us'
40205                 ];
40206             }
40207             
40208             var p = this.preferedCountries.reverse();
40209             
40210             if(p) {
40211                 for (var i = 0; i < p.length; i++) {
40212                     for (var j = 0; j < this.allCountries.length; j++) {
40213                         if(this.allCountries[j].iso2 == p[i]) {
40214                             var t = this.allCountries[j];
40215                             this.allCountries.splice(j,1);
40216                             this.allCountries.unshift(t);
40217                         }
40218                     } 
40219                 }
40220             }
40221             
40222             this.store.proxy.data = {
40223                 success: true,
40224                 data: this.allCountries
40225             };
40226             
40227             return cfg;
40228         },
40229         
40230         initEvents : function()
40231         {
40232             this.createList();
40233             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40234             
40235             this.indicator = this.indicatorEl();
40236             this.flag = this.flagEl();
40237             this.dialCodeHolder = this.dialCodeHolderEl();
40238             
40239             this.trigger = this.el.select('div.flag-box',true).first();
40240             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40241             
40242             var _this = this;
40243             
40244             (function(){
40245                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40246                 _this.list.setWidth(lw);
40247             }).defer(100);
40248             
40249             this.list.on('mouseover', this.onViewOver, this);
40250             this.list.on('mousemove', this.onViewMove, this);
40251             this.inputEl().on("keyup", this.onKeyUp, this);
40252             this.inputEl().on("keypress", this.onKeyPress, this);
40253             
40254             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40255
40256             this.view = new Roo.View(this.list, this.tpl, {
40257                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40258             });
40259             
40260             this.view.on('click', this.onViewClick, this);
40261             this.setValue(this.defaultDialCode);
40262         },
40263         
40264         onTriggerClick : function(e)
40265         {
40266             Roo.log('trigger click');
40267             if(this.disabled){
40268                 return;
40269             }
40270             
40271             if(this.isExpanded()){
40272                 this.collapse();
40273                 this.hasFocus = false;
40274             }else {
40275                 this.store.load({});
40276                 this.hasFocus = true;
40277                 this.expand();
40278             }
40279         },
40280         
40281         isExpanded : function()
40282         {
40283             return this.list.isVisible();
40284         },
40285         
40286         collapse : function()
40287         {
40288             if(!this.isExpanded()){
40289                 return;
40290             }
40291             this.list.hide();
40292             Roo.get(document).un('mousedown', this.collapseIf, this);
40293             Roo.get(document).un('mousewheel', this.collapseIf, this);
40294             this.fireEvent('collapse', this);
40295             this.validate();
40296         },
40297         
40298         expand : function()
40299         {
40300             Roo.log('expand');
40301
40302             if(this.isExpanded() || !this.hasFocus){
40303                 return;
40304             }
40305             
40306             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40307             this.list.setWidth(lw);
40308             
40309             this.list.show();
40310             this.restrictHeight();
40311             
40312             Roo.get(document).on('mousedown', this.collapseIf, this);
40313             Roo.get(document).on('mousewheel', this.collapseIf, this);
40314             
40315             this.fireEvent('expand', this);
40316         },
40317         
40318         restrictHeight : function()
40319         {
40320             this.list.alignTo(this.inputEl(), this.listAlign);
40321             this.list.alignTo(this.inputEl(), this.listAlign);
40322         },
40323         
40324         onViewOver : function(e, t)
40325         {
40326             if(this.inKeyMode){
40327                 return;
40328             }
40329             var item = this.view.findItemFromChild(t);
40330             
40331             if(item){
40332                 var index = this.view.indexOf(item);
40333                 this.select(index, false);
40334             }
40335         },
40336
40337         // private
40338         onViewClick : function(view, doFocus, el, e)
40339         {
40340             var index = this.view.getSelectedIndexes()[0];
40341             
40342             var r = this.store.getAt(index);
40343             
40344             if(r){
40345                 this.onSelect(r, index);
40346             }
40347             if(doFocus !== false && !this.blockFocus){
40348                 this.inputEl().focus();
40349             }
40350         },
40351         
40352         onViewMove : function(e, t)
40353         {
40354             this.inKeyMode = false;
40355         },
40356         
40357         select : function(index, scrollIntoView)
40358         {
40359             this.selectedIndex = index;
40360             this.view.select(index);
40361             if(scrollIntoView !== false){
40362                 var el = this.view.getNode(index);
40363                 if(el){
40364                     this.list.scrollChildIntoView(el, false);
40365                 }
40366             }
40367         },
40368         
40369         createList : function()
40370         {
40371             this.list = Roo.get(document.body).createChild({
40372                 tag: 'ul',
40373                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40374                 style: 'display:none'
40375             });
40376             
40377             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40378         },
40379         
40380         collapseIf : function(e)
40381         {
40382             var in_combo  = e.within(this.el);
40383             var in_list =  e.within(this.list);
40384             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40385             
40386             if (in_combo || in_list || is_list) {
40387                 return;
40388             }
40389             this.collapse();
40390         },
40391         
40392         onSelect : function(record, index)
40393         {
40394             if(this.fireEvent('beforeselect', this, record, index) !== false){
40395                 
40396                 this.setFlagClass(record.data.iso2);
40397                 this.setDialCode(record.data.dialCode);
40398                 this.hasFocus = false;
40399                 this.collapse();
40400                 this.fireEvent('select', this, record, index);
40401             }
40402         },
40403         
40404         flagEl : function()
40405         {
40406             var flag = this.el.select('div.flag',true).first();
40407             if(!flag){
40408                 return false;
40409             }
40410             return flag;
40411         },
40412         
40413         dialCodeHolderEl : function()
40414         {
40415             var d = this.el.select('input.dial-code-holder',true).first();
40416             if(!d){
40417                 return false;
40418             }
40419             return d;
40420         },
40421         
40422         setDialCode : function(v)
40423         {
40424             this.dialCodeHolder.dom.value = '+'+v;
40425         },
40426         
40427         setFlagClass : function(n)
40428         {
40429             this.flag.dom.className = 'flag '+n;
40430         },
40431         
40432         getValue : function()
40433         {
40434             var v = this.inputEl().getValue();
40435             if(this.dialCodeHolder) {
40436                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40437             }
40438             return v;
40439         },
40440         
40441         setValue : function(v)
40442         {
40443             var d = this.getDialCode(v);
40444             
40445             //invalid dial code
40446             if(v.length == 0 || !d || d.length == 0) {
40447                 if(this.rendered){
40448                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40449                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40450                 }
40451                 return;
40452             }
40453             
40454             //valid dial code
40455             this.setFlagClass(this.dialCodeMapping[d].iso2);
40456             this.setDialCode(d);
40457             this.inputEl().dom.value = v.replace('+'+d,'');
40458             this.hiddenEl().dom.value = this.getValue();
40459             
40460             this.validate();
40461         },
40462         
40463         getDialCode : function(v)
40464         {
40465             v = v ||  '';
40466             
40467             if (v.length == 0) {
40468                 return this.dialCodeHolder.dom.value;
40469             }
40470             
40471             var dialCode = "";
40472             if (v.charAt(0) != "+") {
40473                 return false;
40474             }
40475             var numericChars = "";
40476             for (var i = 1; i < v.length; i++) {
40477               var c = v.charAt(i);
40478               if (!isNaN(c)) {
40479                 numericChars += c;
40480                 if (this.dialCodeMapping[numericChars]) {
40481                   dialCode = v.substr(1, i);
40482                 }
40483                 if (numericChars.length == 4) {
40484                   break;
40485                 }
40486               }
40487             }
40488             return dialCode;
40489         },
40490         
40491         reset : function()
40492         {
40493             this.setValue(this.defaultDialCode);
40494             this.validate();
40495         },
40496         
40497         hiddenEl : function()
40498         {
40499             return this.el.select('input.hidden-tel-input',true).first();
40500         },
40501         
40502         // after setting val
40503         onKeyUp : function(e){
40504             this.setValue(this.getValue());
40505         },
40506         
40507         onKeyPress : function(e){
40508             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40509                 e.stopEvent();
40510             }
40511         }
40512         
40513 });
40514 /**
40515  * @class Roo.bootstrap.MoneyField
40516  * @extends Roo.bootstrap.ComboBox
40517  * Bootstrap MoneyField class
40518  * 
40519  * @constructor
40520  * Create a new MoneyField.
40521  * @param {Object} config Configuration options
40522  */
40523
40524 Roo.bootstrap.MoneyField = function(config) {
40525     
40526     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40527     
40528 };
40529
40530 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40531     
40532     /**
40533      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40534      */
40535     allowDecimals : true,
40536     /**
40537      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40538      */
40539     decimalSeparator : ".",
40540     /**
40541      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40542      */
40543     decimalPrecision : 0,
40544     /**
40545      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40546      */
40547     allowNegative : true,
40548     /**
40549      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40550      */
40551     allowZero: true,
40552     /**
40553      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40554      */
40555     minValue : Number.NEGATIVE_INFINITY,
40556     /**
40557      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40558      */
40559     maxValue : Number.MAX_VALUE,
40560     /**
40561      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40562      */
40563     minText : "The minimum value for this field is {0}",
40564     /**
40565      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40566      */
40567     maxText : "The maximum value for this field is {0}",
40568     /**
40569      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40570      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40571      */
40572     nanText : "{0} is not a valid number",
40573     /**
40574      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40575      */
40576     castInt : true,
40577     /**
40578      * @cfg {String} defaults currency of the MoneyField
40579      * value should be in lkey
40580      */
40581     defaultCurrency : false,
40582     /**
40583      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40584      */
40585     thousandsDelimiter : false,
40586     /**
40587      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40588      */
40589     max_length: false,
40590     
40591     inputlg : 9,
40592     inputmd : 9,
40593     inputsm : 9,
40594     inputxs : 6,
40595     
40596     store : false,
40597     
40598     getAutoCreate : function()
40599     {
40600         var align = this.labelAlign || this.parentLabelAlign();
40601         
40602         var id = Roo.id();
40603
40604         var cfg = {
40605             cls: 'form-group',
40606             cn: []
40607         };
40608
40609         var input =  {
40610             tag: 'input',
40611             id : id,
40612             cls : 'form-control roo-money-amount-input',
40613             autocomplete: 'new-password'
40614         };
40615         
40616         var hiddenInput = {
40617             tag: 'input',
40618             type: 'hidden',
40619             id: Roo.id(),
40620             cls: 'hidden-number-input'
40621         };
40622         
40623         if(this.max_length) {
40624             input.maxlength = this.max_length; 
40625         }
40626         
40627         if (this.name) {
40628             hiddenInput.name = this.name;
40629         }
40630
40631         if (this.disabled) {
40632             input.disabled = true;
40633         }
40634
40635         var clg = 12 - this.inputlg;
40636         var cmd = 12 - this.inputmd;
40637         var csm = 12 - this.inputsm;
40638         var cxs = 12 - this.inputxs;
40639         
40640         var container = {
40641             tag : 'div',
40642             cls : 'row roo-money-field',
40643             cn : [
40644                 {
40645                     tag : 'div',
40646                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40647                     cn : [
40648                         {
40649                             tag : 'div',
40650                             cls: 'roo-select2-container input-group',
40651                             cn: [
40652                                 {
40653                                     tag : 'input',
40654                                     cls : 'form-control roo-money-currency-input',
40655                                     autocomplete: 'new-password',
40656                                     readOnly : 1,
40657                                     name : this.currencyName
40658                                 },
40659                                 {
40660                                     tag :'span',
40661                                     cls : 'input-group-addon',
40662                                     cn : [
40663                                         {
40664                                             tag: 'span',
40665                                             cls: 'caret'
40666                                         }
40667                                     ]
40668                                 }
40669                             ]
40670                         }
40671                     ]
40672                 },
40673                 {
40674                     tag : 'div',
40675                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40676                     cn : [
40677                         {
40678                             tag: 'div',
40679                             cls: this.hasFeedback ? 'has-feedback' : '',
40680                             cn: [
40681                                 input
40682                             ]
40683                         }
40684                     ]
40685                 }
40686             ]
40687             
40688         };
40689         
40690         if (this.fieldLabel.length) {
40691             var indicator = {
40692                 tag: 'i',
40693                 tooltip: 'This field is required'
40694             };
40695
40696             var label = {
40697                 tag: 'label',
40698                 'for':  id,
40699                 cls: 'control-label',
40700                 cn: []
40701             };
40702
40703             var label_text = {
40704                 tag: 'span',
40705                 html: this.fieldLabel
40706             };
40707
40708             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40709             label.cn = [
40710                 indicator,
40711                 label_text
40712             ];
40713
40714             if(this.indicatorpos == 'right') {
40715                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40716                 label.cn = [
40717                     label_text,
40718                     indicator
40719                 ];
40720             }
40721
40722             if(align == 'left') {
40723                 container = {
40724                     tag: 'div',
40725                     cn: [
40726                         container
40727                     ]
40728                 };
40729
40730                 if(this.labelWidth > 12){
40731                     label.style = "width: " + this.labelWidth + 'px';
40732                 }
40733                 if(this.labelWidth < 13 && this.labelmd == 0){
40734                     this.labelmd = this.labelWidth;
40735                 }
40736                 if(this.labellg > 0){
40737                     label.cls += ' col-lg-' + this.labellg;
40738                     input.cls += ' col-lg-' + (12 - this.labellg);
40739                 }
40740                 if(this.labelmd > 0){
40741                     label.cls += ' col-md-' + this.labelmd;
40742                     container.cls += ' col-md-' + (12 - this.labelmd);
40743                 }
40744                 if(this.labelsm > 0){
40745                     label.cls += ' col-sm-' + this.labelsm;
40746                     container.cls += ' col-sm-' + (12 - this.labelsm);
40747                 }
40748                 if(this.labelxs > 0){
40749                     label.cls += ' col-xs-' + this.labelxs;
40750                     container.cls += ' col-xs-' + (12 - this.labelxs);
40751                 }
40752             }
40753         }
40754
40755         cfg.cn = [
40756             label,
40757             container,
40758             hiddenInput
40759         ];
40760         
40761         var settings = this;
40762
40763         ['xs','sm','md','lg'].map(function(size){
40764             if (settings[size]) {
40765                 cfg.cls += ' col-' + size + '-' + settings[size];
40766             }
40767         });
40768         
40769         return cfg;
40770     },
40771     
40772     initEvents : function()
40773     {
40774         this.indicator = this.indicatorEl();
40775         
40776         this.initCurrencyEvent();
40777         
40778         this.initNumberEvent();
40779     },
40780     
40781     initCurrencyEvent : function()
40782     {
40783         if (!this.store) {
40784             throw "can not find store for combo";
40785         }
40786         
40787         this.store = Roo.factory(this.store, Roo.data);
40788         this.store.parent = this;
40789         
40790         this.createList();
40791         
40792         this.triggerEl = this.el.select('.input-group-addon', true).first();
40793         
40794         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40795         
40796         var _this = this;
40797         
40798         (function(){
40799             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40800             _this.list.setWidth(lw);
40801         }).defer(100);
40802         
40803         this.list.on('mouseover', this.onViewOver, this);
40804         this.list.on('mousemove', this.onViewMove, this);
40805         this.list.on('scroll', this.onViewScroll, this);
40806         
40807         if(!this.tpl){
40808             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40809         }
40810         
40811         this.view = new Roo.View(this.list, this.tpl, {
40812             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40813         });
40814         
40815         this.view.on('click', this.onViewClick, this);
40816         
40817         this.store.on('beforeload', this.onBeforeLoad, this);
40818         this.store.on('load', this.onLoad, this);
40819         this.store.on('loadexception', this.onLoadException, this);
40820         
40821         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40822             "up" : function(e){
40823                 this.inKeyMode = true;
40824                 this.selectPrev();
40825             },
40826
40827             "down" : function(e){
40828                 if(!this.isExpanded()){
40829                     this.onTriggerClick();
40830                 }else{
40831                     this.inKeyMode = true;
40832                     this.selectNext();
40833                 }
40834             },
40835
40836             "enter" : function(e){
40837                 this.collapse();
40838                 
40839                 if(this.fireEvent("specialkey", this, e)){
40840                     this.onViewClick(false);
40841                 }
40842                 
40843                 return true;
40844             },
40845
40846             "esc" : function(e){
40847                 this.collapse();
40848             },
40849
40850             "tab" : function(e){
40851                 this.collapse();
40852                 
40853                 if(this.fireEvent("specialkey", this, e)){
40854                     this.onViewClick(false);
40855                 }
40856                 
40857                 return true;
40858             },
40859
40860             scope : this,
40861
40862             doRelay : function(foo, bar, hname){
40863                 if(hname == 'down' || this.scope.isExpanded()){
40864                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40865                 }
40866                 return true;
40867             },
40868
40869             forceKeyDown: true
40870         });
40871         
40872         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40873         
40874     },
40875     
40876     initNumberEvent : function(e)
40877     {
40878         this.inputEl().on("keydown" , this.fireKey,  this);
40879         this.inputEl().on("focus", this.onFocus,  this);
40880         this.inputEl().on("blur", this.onBlur,  this);
40881         
40882         this.inputEl().relayEvent('keyup', this);
40883         
40884         if(this.indicator){
40885             this.indicator.addClass('invisible');
40886         }
40887  
40888         this.originalValue = this.getValue();
40889         
40890         if(this.validationEvent == 'keyup'){
40891             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40892             this.inputEl().on('keyup', this.filterValidation, this);
40893         }
40894         else if(this.validationEvent !== false){
40895             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40896         }
40897         
40898         if(this.selectOnFocus){
40899             this.on("focus", this.preFocus, this);
40900             
40901         }
40902         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40903             this.inputEl().on("keypress", this.filterKeys, this);
40904         } else {
40905             this.inputEl().relayEvent('keypress', this);
40906         }
40907         
40908         var allowed = "0123456789";
40909         
40910         if(this.allowDecimals){
40911             allowed += this.decimalSeparator;
40912         }
40913         
40914         if(this.allowNegative){
40915             allowed += "-";
40916         }
40917         
40918         if(this.thousandsDelimiter) {
40919             allowed += ",";
40920         }
40921         
40922         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40923         
40924         var keyPress = function(e){
40925             
40926             var k = e.getKey();
40927             
40928             var c = e.getCharCode();
40929             
40930             if(
40931                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40932                     allowed.indexOf(String.fromCharCode(c)) === -1
40933             ){
40934                 e.stopEvent();
40935                 return;
40936             }
40937             
40938             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40939                 return;
40940             }
40941             
40942             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40943                 e.stopEvent();
40944             }
40945         };
40946         
40947         this.inputEl().on("keypress", keyPress, this);
40948         
40949     },
40950     
40951     onTriggerClick : function(e)
40952     {   
40953         if(this.disabled){
40954             return;
40955         }
40956         
40957         this.page = 0;
40958         this.loadNext = false;
40959         
40960         if(this.isExpanded()){
40961             this.collapse();
40962             return;
40963         }
40964         
40965         this.hasFocus = true;
40966         
40967         if(this.triggerAction == 'all') {
40968             this.doQuery(this.allQuery, true);
40969             return;
40970         }
40971         
40972         this.doQuery(this.getRawValue());
40973     },
40974     
40975     getCurrency : function()
40976     {   
40977         var v = this.currencyEl().getValue();
40978         
40979         return v;
40980     },
40981     
40982     restrictHeight : function()
40983     {
40984         this.list.alignTo(this.currencyEl(), this.listAlign);
40985         this.list.alignTo(this.currencyEl(), this.listAlign);
40986     },
40987     
40988     onViewClick : function(view, doFocus, el, e)
40989     {
40990         var index = this.view.getSelectedIndexes()[0];
40991         
40992         var r = this.store.getAt(index);
40993         
40994         if(r){
40995             this.onSelect(r, index);
40996         }
40997     },
40998     
40999     onSelect : function(record, index){
41000         
41001         if(this.fireEvent('beforeselect', this, record, index) !== false){
41002         
41003             this.setFromCurrencyData(index > -1 ? record.data : false);
41004             
41005             this.collapse();
41006             
41007             this.fireEvent('select', this, record, index);
41008         }
41009     },
41010     
41011     setFromCurrencyData : function(o)
41012     {
41013         var currency = '';
41014         
41015         this.lastCurrency = o;
41016         
41017         if (this.currencyField) {
41018             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41019         } else {
41020             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41021         }
41022         
41023         this.lastSelectionText = currency;
41024         
41025         //setting default currency
41026         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41027             this.setCurrency(this.defaultCurrency);
41028             return;
41029         }
41030         
41031         this.setCurrency(currency);
41032     },
41033     
41034     setFromData : function(o)
41035     {
41036         var c = {};
41037         
41038         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41039         
41040         this.setFromCurrencyData(c);
41041         
41042         var value = '';
41043         
41044         if (this.name) {
41045             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41046         } else {
41047             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41048         }
41049         
41050         this.setValue(value);
41051         
41052     },
41053     
41054     setCurrency : function(v)
41055     {   
41056         this.currencyValue = v;
41057         
41058         if(this.rendered){
41059             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41060             this.validate();
41061         }
41062     },
41063     
41064     setValue : function(v)
41065     {
41066         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41067         
41068         this.value = v;
41069         
41070         if(this.rendered){
41071             
41072             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41073             
41074             this.inputEl().dom.value = (v == '') ? '' :
41075                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41076             
41077             if(!this.allowZero && v === '0') {
41078                 this.hiddenEl().dom.value = '';
41079                 this.inputEl().dom.value = '';
41080             }
41081             
41082             this.validate();
41083         }
41084     },
41085     
41086     getRawValue : function()
41087     {
41088         var v = this.inputEl().getValue();
41089         
41090         return v;
41091     },
41092     
41093     getValue : function()
41094     {
41095         return this.fixPrecision(this.parseValue(this.getRawValue()));
41096     },
41097     
41098     parseValue : function(value)
41099     {
41100         if(this.thousandsDelimiter) {
41101             value += "";
41102             r = new RegExp(",", "g");
41103             value = value.replace(r, "");
41104         }
41105         
41106         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41107         return isNaN(value) ? '' : value;
41108         
41109     },
41110     
41111     fixPrecision : function(value)
41112     {
41113         if(this.thousandsDelimiter) {
41114             value += "";
41115             r = new RegExp(",", "g");
41116             value = value.replace(r, "");
41117         }
41118         
41119         var nan = isNaN(value);
41120         
41121         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41122             return nan ? '' : value;
41123         }
41124         return parseFloat(value).toFixed(this.decimalPrecision);
41125     },
41126     
41127     decimalPrecisionFcn : function(v)
41128     {
41129         return Math.floor(v);
41130     },
41131     
41132     validateValue : function(value)
41133     {
41134         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41135             return false;
41136         }
41137         
41138         var num = this.parseValue(value);
41139         
41140         if(isNaN(num)){
41141             this.markInvalid(String.format(this.nanText, value));
41142             return false;
41143         }
41144         
41145         if(num < this.minValue){
41146             this.markInvalid(String.format(this.minText, this.minValue));
41147             return false;
41148         }
41149         
41150         if(num > this.maxValue){
41151             this.markInvalid(String.format(this.maxText, this.maxValue));
41152             return false;
41153         }
41154         
41155         return true;
41156     },
41157     
41158     validate : function()
41159     {
41160         if(this.disabled || this.allowBlank){
41161             this.markValid();
41162             return true;
41163         }
41164         
41165         var currency = this.getCurrency();
41166         
41167         if(this.validateValue(this.getRawValue()) && currency.length){
41168             this.markValid();
41169             return true;
41170         }
41171         
41172         this.markInvalid();
41173         return false;
41174     },
41175     
41176     getName: function()
41177     {
41178         return this.name;
41179     },
41180     
41181     beforeBlur : function()
41182     {
41183         if(!this.castInt){
41184             return;
41185         }
41186         
41187         var v = this.parseValue(this.getRawValue());
41188         
41189         if(v || v == 0){
41190             this.setValue(v);
41191         }
41192     },
41193     
41194     onBlur : function()
41195     {
41196         this.beforeBlur();
41197         
41198         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41199             //this.el.removeClass(this.focusClass);
41200         }
41201         
41202         this.hasFocus = false;
41203         
41204         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41205             this.validate();
41206         }
41207         
41208         var v = this.getValue();
41209         
41210         if(String(v) !== String(this.startValue)){
41211             this.fireEvent('change', this, v, this.startValue);
41212         }
41213         
41214         this.fireEvent("blur", this);
41215     },
41216     
41217     inputEl : function()
41218     {
41219         return this.el.select('.roo-money-amount-input', true).first();
41220     },
41221     
41222     currencyEl : function()
41223     {
41224         return this.el.select('.roo-money-currency-input', true).first();
41225     },
41226     
41227     hiddenEl : function()
41228     {
41229         return this.el.select('input.hidden-number-input',true).first();
41230     }
41231     
41232 });