Roo/bootstrap/Component.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     tooltip : null,
43     /**
44      * Initialize Events for the element
45      */
46     initEvents : function() { },
47     
48     xattr : false,
49     
50     parentId : false,
51     
52     can_build_overlaid : true,
53     
54     dataId : false,
55     
56     name : false,
57     
58     parent: function() {
59         // returns the parent component..
60         return Roo.ComponentMgr.get(this.parentId)
61         
62         
63     },
64     
65     // private
66     onRender : function(ct, position)
67     {
68        // Roo.log("Call onRender: " + this.xtype);
69         
70         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
71         
72         if(this.el){
73             if (this.el.attr('xtype')) {
74                 this.el.attr('xtypex', this.el.attr('xtype'));
75                 this.el.dom.removeAttribute('xtype');
76                 
77                 this.initEvents();
78             }
79             
80             return;
81         }
82         
83          
84         
85         var cfg = Roo.apply({},  this.getAutoCreate());
86         cfg.id = Roo.id();
87         
88         // fill in the extra attributes 
89         if (this.xattr && typeof(this.xattr) =='object') {
90             for (var i in this.xattr) {
91                 cfg[i] = this.xattr[i];
92             }
93         }
94         
95         if(this.dataId){
96             cfg.dataId = this.dataId;
97         }
98         
99         if (this.cls) {
100             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
101         }
102         
103         if (this.style) { // fixme needs to support more complex style data.
104             cfg.style = this.style;
105         }
106         
107         if(this.name){
108             cfg.name = this.name;
109         }
110         
111         if (this.tooltip) {
112             this.tooltipEl().attr('tooltip', this.tooltip);
113         }
114         
115         this.el = ct.createChild(cfg, position);
116         
117         if(this.tabIndex !== undefined){
118             this.el.dom.setAttribute('tabIndex', this.tabIndex);
119         }
120         this.initEvents();
121         
122         
123     },
124     /**
125      * Fetch the element to add children to
126      * @return {Roo.Element} defaults to this.el
127      */
128     getChildContainer : function()
129     {
130         return this.el;
131     },
132     /**
133      * Fetch the element to display the tooltip on.
134      * @return {Roo.Element} defaults to this.el
135      */
136     getTooltipEl : function()
137     {
138         return this.el;
139     },
140         
141     addxtype  : function(tree,cntr)
142     {
143         var cn = this;
144         
145         cn = Roo.factory(tree);
146            
147         cn.parentType = this.xtype; //??
148         cn.parentId = this.id;
149         
150         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
151         
152         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
153         
154         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
155         
156         var build_from_html =  Roo.XComponent.build_from_html;
157           
158         var is_body  = (tree.xtype == 'Body') ;
159           
160         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
161           
162         var self_cntr_el = Roo.get(this[cntr](false));
163         
164         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
165             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
166                 return this.addxtypeChild(tree,cntr);
167             }
168             
169             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
170                 
171             if(echild){
172                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
173             }
174             
175             Roo.log('skipping render');
176             return cn;
177             
178         }
179         
180         var ret = false;
181         
182         while (true) {
183             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
184             
185             if (!echild) {
186                 break;
187             }
188             
189             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
190                 break;
191             }
192             
193             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
194         }
195         return ret;
196     },
197     
198     addxtypeChild : function (tree, cntr)
199     {
200         Roo.log('addxtypeChild:' + cntr);
201         var cn = this;
202         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
203         
204         
205         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
206                     (typeof(tree['flexy:foreach']) != 'undefined');
207           
208         
209         
210          skip_children = false;
211         // render the element if it's not BODY.
212         if (tree.xtype != 'Body') {
213            
214             cn = Roo.factory(tree);
215            
216             cn.parentType = this.xtype; //??
217             cn.parentId = this.id;
218             
219             var build_from_html =  Roo.XComponent.build_from_html;
220             
221             
222             // does the container contain child eleemnts with 'xtype' attributes.
223             // that match this xtype..
224             // note - when we render we create these as well..
225             // so we should check to see if body has xtype set.
226             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
227                
228                 var self_cntr_el = Roo.get(this[cntr](false));
229                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
230                 
231                 
232                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
233                 // and are not displayed -this causes this to use up the wrong element when matching.
234                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
235                 
236                 
237                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
238                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
239                   
240                   
241                   
242                     cn.el = echild;
243                   //  Roo.log("GOT");
244                     //echild.dom.removeAttribute('xtype');
245                 } else {
246                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
247                     Roo.log(self_cntr_el);
248                     Roo.log(echild);
249                     Roo.log(cn);
250                 }
251             }
252            
253             
254            
255             // if object has flexy:if - then it may or may not be rendered.
256             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
257                 // skip a flexy if element.
258                 Roo.log('skipping render');
259                 Roo.log(tree);
260                 if (!cn.el) {
261                     Roo.log('skipping all children');
262                     skip_children = true;
263                 }
264                 
265              } else {
266                  
267                 // actually if flexy:foreach is found, we really want to create 
268                 // multiple copies here...
269                 //Roo.log('render');
270                 //Roo.log(this[cntr]());
271                 cn.render(this[cntr](true));
272              }
273             // then add the element..
274         }
275         
276         
277         // handle the kids..
278         
279         var nitems = [];
280         /*
281         if (typeof (tree.menu) != 'undefined') {
282             tree.menu.parentType = cn.xtype;
283             tree.menu.triggerEl = cn.el;
284             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
285             
286         }
287         */
288         if (!tree.items || !tree.items.length) {
289             cn.items = nitems;
290             return cn;
291         }
292         var items = tree.items;
293         delete tree.items;
294         
295         //Roo.log(items.length);
296             // add the items..
297         if (!skip_children) {    
298             for(var i =0;i < items.length;i++) {
299                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
300             }
301         }
302         
303         cn.items = nitems;
304         
305         return cn;
306     }
307     
308     
309     
310     
311 });
312
313  /*
314  * - LGPL
315  *
316  * Body
317  * 
318  */
319
320 /**
321  * @class Roo.bootstrap.Body
322  * @extends Roo.bootstrap.Component
323  * Bootstrap Body class
324  * 
325  * @constructor
326  * Create a new body
327  * @param {Object} config The config object
328  */
329
330 Roo.bootstrap.Body = function(config){
331     Roo.bootstrap.Body.superclass.constructor.call(this, config);
332     this.el = Roo.get(document.body);
333     if (this.cls && this.cls.length) {
334         Roo.get(document.body).addClass(this.cls);
335     }
336 };
337
338 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
339       
340         autoCreate : {
341         cls: 'container'
342     },
343     onRender : function(ct, position)
344     {
345        /* Roo.log("Roo.bootstrap.Body - onRender");
346         if (this.cls && this.cls.length) {
347             Roo.get(document.body).addClass(this.cls);
348         }
349         // style??? xttr???
350         */
351     }
352     
353     
354  
355    
356 });
357
358  /*
359  * - LGPL
360  *
361  * button group
362  * 
363  */
364
365
366 /**
367  * @class Roo.bootstrap.ButtonGroup
368  * @extends Roo.bootstrap.Component
369  * Bootstrap ButtonGroup class
370  * @cfg {String} size lg | sm | xs (default empty normal)
371  * @cfg {String} align vertical | justified  (default none)
372  * @cfg {String} direction up | down (default down)
373  * @cfg {Boolean} toolbar false | true
374  * @cfg {Boolean} btn true | false
375  * 
376  * 
377  * @constructor
378  * Create a new Input
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.ButtonGroup = function(config){
383     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
384 };
385
386 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
387     
388     size: '',
389     align: '',
390     direction: '',
391     toolbar: false,
392     btn: true,
393
394     getAutoCreate : function(){
395         var cfg = {
396             cls: 'btn-group',
397             html : null
398         }
399         
400         cfg.html = this.html || cfg.html;
401         
402         if (this.toolbar) {
403             cfg = {
404                 cls: 'btn-toolbar',
405                 html: null
406             }
407             
408             return cfg;
409         }
410         
411         if (['vertical','justified'].indexOf(this.align)!==-1) {
412             cfg.cls = 'btn-group-' + this.align;
413             
414             if (this.align == 'justified') {
415                 console.log(this.items);
416             }
417         }
418         
419         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
420             cfg.cls += ' btn-group-' + this.size;
421         }
422         
423         if (this.direction == 'up') {
424             cfg.cls += ' dropup' ;
425         }
426         
427         return cfg;
428     }
429    
430 });
431
432  /*
433  * - LGPL
434  *
435  * button
436  * 
437  */
438
439 /**
440  * @class Roo.bootstrap.Button
441  * @extends Roo.bootstrap.Component
442  * Bootstrap Button class
443  * @cfg {String} html The button content
444  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
445  * @cfg {String} size empty | lg | sm | xs
446  * @cfg {String} tag empty | a | input | submit
447  * @cfg {String} href empty or href
448  * @cfg {Boolean} disabled false | true
449  * @cfg {Boolean} isClose false | true
450  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
451  * @cfg {String} badge text for badge
452  * @cfg {String} theme default (or empty) | glow
453  * @cfg {Boolean} inverse false | true
454  * @cfg {Boolean} toggle false | true
455  * @cfg {String} ontext text for on toggle state
456  * @cfg {String} offtext text for off toggle state
457  * @cfg {Boolean} defaulton true | false
458  * @cfg {Boolean} preventDefault (true | false) default true
459  * @cfg {Boolean} removeClass true | false remove the standard class..
460  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
461  * 
462  * @constructor
463  * Create a new button
464  * @param {Object} config The config object
465  */
466
467
468 Roo.bootstrap.Button = function(config){
469     Roo.bootstrap.Button.superclass.constructor.call(this, config);
470     this.addEvents({
471         // raw events
472         /**
473          * @event click
474          * When a butotn is pressed
475          * @param {Roo.EventObject} e
476          */
477         "click" : true,
478          /**
479          * @event toggle
480          * After the button has been toggles
481          * @param {Roo.EventObject} e
482          * @param {boolean} pressed (also available as button.pressed)
483          */
484         "toggle" : true
485     });
486 };
487
488 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
489     html: false,
490     active: false,
491     weight: '',
492     size: '',
493     tag: 'button',
494     href: '',
495     disabled: false,
496     isClose: false,
497     glyphicon: '',
498     badge: '',
499     theme: 'default',
500     inverse: false,
501     
502     toggle: false,
503     ontext: 'ON',
504     offtext: 'OFF',
505     defaulton: true,
506     preventDefault: true,
507     removeClass: false,
508     name: false,
509     target: false,
510     
511     
512     pressed : null,
513      
514     
515     getAutoCreate : function(){
516         
517         var cfg = {
518             tag : 'button',
519             cls : 'roo-button',
520             html: ''
521         };
522         
523         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
524             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
525             this.tag = 'button';
526         } else {
527             cfg.tag = this.tag;
528         }
529         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
530         
531         if (this.toggle == true) {
532             cfg={
533                 tag: 'div',
534                 cls: 'slider-frame roo-button',
535                 cn: [
536                     {
537                         tag: 'span',
538                         'data-on-text':'ON',
539                         'data-off-text':'OFF',
540                         cls: 'slider-button',
541                         html: this.offtext
542                     }
543                 ]
544             };
545             
546             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
547                 cfg.cls += ' '+this.weight;
548             }
549             
550             return cfg;
551         }
552         
553         if (this.isClose) {
554             cfg.cls += ' close';
555             
556             cfg["aria-hidden"] = true;
557             
558             cfg.html = "&times;";
559             
560             return cfg;
561         }
562         
563          
564         if (this.theme==='default') {
565             cfg.cls = 'btn roo-button';
566             
567             //if (this.parentType != 'Navbar') {
568             this.weight = this.weight.length ?  this.weight : 'default';
569             //}
570             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
571                 
572                 cfg.cls += ' btn-' + this.weight;
573             }
574         } else if (this.theme==='glow') {
575             
576             cfg.tag = 'a';
577             cfg.cls = 'btn-glow roo-button';
578             
579             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
580                 
581                 cfg.cls += ' ' + this.weight;
582             }
583         }
584    
585         
586         if (this.inverse) {
587             this.cls += ' inverse';
588         }
589         
590         
591         if (this.active) {
592             cfg.cls += ' active';
593         }
594         
595         if (this.disabled) {
596             cfg.disabled = 'disabled';
597         }
598         
599         if (this.items) {
600             Roo.log('changing to ul' );
601             cfg.tag = 'ul';
602             this.glyphicon = 'caret';
603         }
604         
605         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
606          
607         //gsRoo.log(this.parentType);
608         if (this.parentType === 'Navbar' && !this.parent().bar) {
609             Roo.log('changing to li?');
610             
611             cfg.tag = 'li';
612             
613             cfg.cls = '';
614             cfg.cn =  [{
615                 tag : 'a',
616                 cls : 'roo-button',
617                 html : this.html,
618                 href : this.href || '#'
619             }];
620             if (this.menu) {
621                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
622                 cfg.cls += ' dropdown';
623             }   
624             
625             delete cfg.html;
626             
627         }
628         
629        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
630         
631         if (this.glyphicon) {
632             cfg.html = ' ' + cfg.html;
633             
634             cfg.cn = [
635                 {
636                     tag: 'span',
637                     cls: 'glyphicon glyphicon-' + this.glyphicon
638                 }
639             ];
640         }
641         
642         if (this.badge) {
643             cfg.html += ' ';
644             
645             cfg.tag = 'a';
646             
647 //            cfg.cls='btn roo-button';
648             
649             cfg.href=this.href;
650             
651             var value = cfg.html;
652             
653             if(this.glyphicon){
654                 value = {
655                             tag: 'span',
656                             cls: 'glyphicon glyphicon-' + this.glyphicon,
657                             html: this.html
658                         };
659                 
660             }
661             
662             cfg.cn = [
663                 value,
664                 {
665                     tag: 'span',
666                     cls: 'badge',
667                     html: this.badge
668                 }
669             ];
670             
671             cfg.html='';
672         }
673         
674         if (this.menu) {
675             cfg.cls += ' dropdown';
676             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
677         }
678         
679         if (cfg.tag !== 'a' && this.href !== '') {
680             throw "Tag must be a to set href.";
681         } else if (this.href.length > 0) {
682             cfg.href = this.href;
683         }
684         
685         if(this.removeClass){
686             cfg.cls = '';
687         }
688         
689         if(this.target){
690             cfg.target = this.target;
691         }
692         
693         return cfg;
694     },
695     initEvents: function() {
696        // Roo.log('init events?');
697 //        Roo.log(this.el.dom);
698         // add the menu...
699         
700         if (typeof (this.menu) != 'undefined') {
701             this.menu.parentType = this.xtype;
702             this.menu.triggerEl = this.el;
703             this.addxtype(Roo.apply({}, this.menu));
704         }
705
706
707        if (this.el.hasClass('roo-button')) {
708             this.el.on('click', this.onClick, this);
709        } else {
710             this.el.select('.roo-button').on('click', this.onClick, this);
711        }
712        
713        if(this.removeClass){
714            this.el.on('click', this.onClick, this);
715        }
716        
717        this.el.enableDisplayMode();
718         
719     },
720     onClick : function(e)
721     {
722         if (this.disabled) {
723             return;
724         }
725         
726         Roo.log('button on click ');
727         if(this.preventDefault){
728             e.preventDefault();
729         }
730         if (this.pressed === true || this.pressed === false) {
731             this.pressed = !this.pressed;
732             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
733             this.fireEvent('toggle', this, e, this.pressed);
734         }
735         
736         
737         this.fireEvent('click', this, e);
738     },
739     
740     /**
741      * Enables this button
742      */
743     enable : function()
744     {
745         this.disabled = false;
746         this.el.removeClass('disabled');
747     },
748     
749     /**
750      * Disable this button
751      */
752     disable : function()
753     {
754         this.disabled = true;
755         this.el.addClass('disabled');
756     },
757      /**
758      * sets the active state on/off, 
759      * @param {Boolean} state (optional) Force a particular state
760      */
761     setActive : function(v) {
762         
763         this.el[v ? 'addClass' : 'removeClass']('active');
764     },
765      /**
766      * toggles the current active state 
767      */
768     toggleActive : function()
769     {
770        var active = this.el.hasClass('active');
771        this.setActive(!active);
772        
773         
774     },
775     setText : function(str)
776     {
777         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
778     },
779     getText : function()
780     {
781         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
782     },
783     hide: function() {
784        
785      
786         this.el.hide();   
787     },
788     show: function() {
789        
790         this.el.show();   
791     }
792     
793     
794 });
795
796  /*
797  * - LGPL
798  *
799  * column
800  * 
801  */
802
803 /**
804  * @class Roo.bootstrap.Column
805  * @extends Roo.bootstrap.Component
806  * Bootstrap Column class
807  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
808  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
809  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
810  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
811  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
812  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
813  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
814  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
815  *
816  * 
817  * @cfg {Boolean} hidden (true|false) hide the element
818  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
819  * @cfg {String} fa (ban|check|...) font awesome icon
820  * @cfg {Number} fasize (1|2|....) font awsome size
821
822  * @cfg {String} icon (info-sign|check|...) glyphicon name
823
824  * @cfg {String} html content of column.
825  * 
826  * @constructor
827  * Create a new Column
828  * @param {Object} config The config object
829  */
830
831 Roo.bootstrap.Column = function(config){
832     Roo.bootstrap.Column.superclass.constructor.call(this, config);
833 };
834
835 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
836     
837     xs: false,
838     sm: false,
839     md: false,
840     lg: false,
841     xsoff: false,
842     smoff: false,
843     mdoff: false,
844     lgoff: false,
845     html: '',
846     offset: 0,
847     alert: false,
848     fa: false,
849     icon : false,
850     hidden : false,
851     fasize : 1,
852     
853     getAutoCreate : function(){
854         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
855         
856         cfg = {
857             tag: 'div',
858             cls: 'column'
859         };
860         
861         var settings=this;
862         ['xs','sm','md','lg'].map(function(size){
863             //Roo.log( size + ':' + settings[size]);
864             
865             if (settings[size+'off'] !== false) {
866                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
867             }
868             
869             if (settings[size] === false) {
870                 return;
871             }
872             Roo.log(settings[size]);
873             if (!settings[size]) { // 0 = hidden
874                 cfg.cls += ' hidden-' + size;
875                 return;
876             }
877             cfg.cls += ' col-' + size + '-' + settings[size];
878             
879         });
880         
881         if (this.hidden) {
882             cfg.cls += ' hidden';
883         }
884         
885         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
886             cfg.cls +=' alert alert-' + this.alert;
887         }
888         
889         
890         if (this.html.length) {
891             cfg.html = this.html;
892         }
893         if (this.fa) {
894             var fasize = '';
895             if (this.fasize > 1) {
896                 fasize = ' fa-' + this.fasize + 'x';
897             }
898             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
899             
900             
901         }
902         if (this.icon) {
903             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
904         }
905         
906         return cfg;
907     }
908    
909 });
910
911  
912
913  /*
914  * - LGPL
915  *
916  * page container.
917  * 
918  */
919
920
921 /**
922  * @class Roo.bootstrap.Container
923  * @extends Roo.bootstrap.Component
924  * Bootstrap Container class
925  * @cfg {Boolean} jumbotron is it a jumbotron element
926  * @cfg {String} html content of element
927  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
928  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
929  * @cfg {String} header content of header (for panel)
930  * @cfg {String} footer content of footer (for panel)
931  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
932  * @cfg {String} tag (header|aside|section) type of HTML tag.
933  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
934  * @cfg {String} fa (ban|check|...) font awesome icon
935  * @cfg {String} icon (info-sign|check|...) glyphicon name
936  * @cfg {Boolean} hidden (true|false) hide the element
937
938  *     
939  * @constructor
940  * Create a new Container
941  * @param {Object} config The config object
942  */
943
944 Roo.bootstrap.Container = function(config){
945     Roo.bootstrap.Container.superclass.constructor.call(this, config);
946 };
947
948 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
949     
950     jumbotron : false,
951     well: '',
952     panel : '',
953     header: '',
954     footer : '',
955     sticky: '',
956     tag : false,
957     alert : false,
958     fa: false,
959     icon : false,
960   
961      
962     getChildContainer : function() {
963         
964         if(!this.el){
965             return false;
966         }
967         
968         if (this.panel.length) {
969             return this.el.select('.panel-body',true).first();
970         }
971         
972         return this.el;
973     },
974     
975     
976     getAutoCreate : function(){
977         
978         var cfg = {
979             tag : this.tag || 'div',
980             html : '',
981             cls : ''
982         };
983         if (this.jumbotron) {
984             cfg.cls = 'jumbotron';
985         }
986         
987         
988         
989         // - this is applied by the parent..
990         //if (this.cls) {
991         //    cfg.cls = this.cls + '';
992         //}
993         
994         if (this.sticky.length) {
995             
996             var bd = Roo.get(document.body);
997             if (!bd.hasClass('bootstrap-sticky')) {
998                 bd.addClass('bootstrap-sticky');
999                 Roo.select('html',true).setStyle('height', '100%');
1000             }
1001              
1002             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1003         }
1004         
1005         
1006         if (this.well.length) {
1007             switch (this.well) {
1008                 case 'lg':
1009                 case 'sm':
1010                     cfg.cls +=' well well-' +this.well;
1011                     break;
1012                 default:
1013                     cfg.cls +=' well';
1014                     break;
1015             }
1016         }
1017         
1018         if (this.hidden) {
1019             cfg.cls += ' hidden';
1020         }
1021         
1022         
1023         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024             cfg.cls +=' alert alert-' + this.alert;
1025         }
1026         
1027         var body = cfg;
1028         
1029         if (this.panel.length) {
1030             cfg.cls += ' panel panel-' + this.panel;
1031             cfg.cn = [];
1032             if (this.header.length) {
1033                 cfg.cn.push({
1034                     
1035                     cls : 'panel-heading',
1036                     cn : [{
1037                         tag: 'h3',
1038                         cls : 'panel-title',
1039                         html : this.header
1040                     }]
1041                     
1042                 });
1043             }
1044             body = false;
1045             cfg.cn.push({
1046                 cls : 'panel-body',
1047                 html : this.html
1048             });
1049             
1050             
1051             if (this.footer.length) {
1052                 cfg.cn.push({
1053                     cls : 'panel-footer',
1054                     html : this.footer
1055                     
1056                 });
1057             }
1058             
1059         }
1060         
1061         if (body) {
1062             body.html = this.html || cfg.html;
1063             // prefix with the icons..
1064             if (this.fa) {
1065                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1066             }
1067             if (this.icon) {
1068                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1069             }
1070             
1071             
1072         }
1073         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1074             cfg.cls =  'container';
1075         }
1076         
1077         return cfg;
1078     },
1079     
1080     titleEl : function()
1081     {
1082         if(!this.el || !this.panel.length || !this.header.length){
1083             return;
1084         }
1085         
1086         return this.el.select('.panel-title',true).first();
1087     },
1088     
1089     setTitle : function(v)
1090     {
1091         var titleEl = this.titleEl();
1092         
1093         if(!titleEl){
1094             return;
1095         }
1096         
1097         titleEl.dom.innerHTML = v;
1098     },
1099     
1100     getTitle : function()
1101     {
1102         
1103         var titleEl = this.titleEl();
1104         
1105         if(!titleEl){
1106             return '';
1107         }
1108         
1109         return titleEl.dom.innerHTML;
1110     }
1111    
1112 });
1113
1114  /*
1115  * - LGPL
1116  *
1117  * image
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Img
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Img class
1126  * @cfg {Boolean} imgResponsive false | true
1127  * @cfg {String} border rounded | circle | thumbnail
1128  * @cfg {String} src image source
1129  * @cfg {String} alt image alternative text
1130  * @cfg {String} href a tag href
1131  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1132  * 
1133  * @constructor
1134  * Create a new Input
1135  * @param {Object} config The config object
1136  */
1137
1138 Roo.bootstrap.Img = function(config){
1139     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1140     
1141     this.addEvents({
1142         // img events
1143         /**
1144          * @event click
1145          * The img click event for the img.
1146          * @param {Roo.EventObject} e
1147          */
1148         "click" : true
1149     });
1150 };
1151
1152 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1153     
1154     imgResponsive: true,
1155     border: '',
1156     src: '',
1157     href: false,
1158     target: false,
1159
1160     getAutoCreate : function(){
1161         
1162         var cfg = {
1163             tag: 'img',
1164             cls: (this.imgResponsive) ? 'img-responsive' : '',
1165             html : null
1166         }
1167         
1168         cfg.html = this.html || cfg.html;
1169         
1170         cfg.src = this.src || cfg.src;
1171         
1172         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1173             cfg.cls += ' img-' + this.border;
1174         }
1175         
1176         if(this.alt){
1177             cfg.alt = this.alt;
1178         }
1179         
1180         if(this.href){
1181             var a = {
1182                 tag: 'a',
1183                 href: this.href,
1184                 cn: [
1185                     cfg
1186                 ]
1187             }
1188             
1189             if(this.target){
1190                 a.target = this.target;
1191             }
1192             
1193         }
1194         
1195         
1196         return (this.href) ? a : cfg;
1197     },
1198     
1199     initEvents: function() {
1200         
1201         if(!this.href){
1202             this.el.on('click', this.onClick, this);
1203         }
1204     },
1205     
1206     onClick : function(e)
1207     {
1208         Roo.log('img onclick');
1209         this.fireEvent('click', this, e);
1210     }
1211    
1212 });
1213
1214  /*
1215  * - LGPL
1216  *
1217  * image
1218  * 
1219  */
1220
1221
1222 /**
1223  * @class Roo.bootstrap.Link
1224  * @extends Roo.bootstrap.Component
1225  * Bootstrap Link Class
1226  * @cfg {String} alt image alternative text
1227  * @cfg {String} href a tag href
1228  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1229  * @cfg {String} html the content of the link.
1230  * @cfg {String} anchor name for the anchor link
1231
1232  * @cfg {Boolean} preventDefault (true | false) default false
1233
1234  * 
1235  * @constructor
1236  * Create a new Input
1237  * @param {Object} config The config object
1238  */
1239
1240 Roo.bootstrap.Link = function(config){
1241     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1242     
1243     this.addEvents({
1244         // img events
1245         /**
1246          * @event click
1247          * The img click event for the img.
1248          * @param {Roo.EventObject} e
1249          */
1250         "click" : true
1251     });
1252 };
1253
1254 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1255     
1256     href: false,
1257     target: false,
1258     preventDefault: false,
1259     anchor : false,
1260     alt : false,
1261
1262     getAutoCreate : function()
1263     {
1264         
1265         var cfg = {
1266             tag: 'a'
1267         };
1268         // anchor's do not require html/href...
1269         if (this.anchor === false) {
1270             cfg.html = this.html || 'html-missing';
1271             cfg.href = this.href || '#';
1272         } else {
1273             cfg.name = this.anchor;
1274             if (this.html !== false) {
1275                 cfg.html = this.html;
1276             }
1277             if (this.href !== false) {
1278                 cfg.href = this.href;
1279             }
1280         }
1281         
1282         if(this.alt !== false){
1283             cfg.alt = this.alt;
1284         }
1285         
1286         
1287         if(this.target !== false) {
1288             cfg.target = this.target;
1289         }
1290         
1291         return cfg;
1292     },
1293     
1294     initEvents: function() {
1295         
1296         if(!this.href || this.preventDefault){
1297             this.el.on('click', this.onClick, this);
1298         }
1299     },
1300     
1301     onClick : function(e)
1302     {
1303         if(this.preventDefault){
1304             e.preventDefault();
1305         }
1306         //Roo.log('img onclick');
1307         this.fireEvent('click', this, e);
1308     }
1309    
1310 });
1311
1312  /*
1313  * - LGPL
1314  *
1315  * header
1316  * 
1317  */
1318
1319 /**
1320  * @class Roo.bootstrap.Header
1321  * @extends Roo.bootstrap.Component
1322  * Bootstrap Header class
1323  * @cfg {String} html content of header
1324  * @cfg {Number} level (1|2|3|4|5|6) default 1
1325  * 
1326  * @constructor
1327  * Create a new Header
1328  * @param {Object} config The config object
1329  */
1330
1331
1332 Roo.bootstrap.Header  = function(config){
1333     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1334 };
1335
1336 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1337     
1338     //href : false,
1339     html : false,
1340     level : 1,
1341     
1342     
1343     
1344     getAutoCreate : function(){
1345         
1346         var cfg = {
1347             tag: 'h' + (1 *this.level),
1348             html: this.html || 'fill in html'
1349         } ;
1350         
1351         return cfg;
1352     }
1353    
1354 });
1355
1356  
1357
1358  /*
1359  * Based on:
1360  * Ext JS Library 1.1.1
1361  * Copyright(c) 2006-2007, Ext JS, LLC.
1362  *
1363  * Originally Released Under LGPL - original licence link has changed is not relivant.
1364  *
1365  * Fork - LGPL
1366  * <script type="text/javascript">
1367  */
1368  
1369 /**
1370  * @class Roo.bootstrap.MenuMgr
1371  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1372  * @singleton
1373  */
1374 Roo.bootstrap.MenuMgr = function(){
1375    var menus, active, groups = {}, attached = false, lastShow = new Date();
1376
1377    // private - called when first menu is created
1378    function init(){
1379        menus = {};
1380        active = new Roo.util.MixedCollection();
1381        Roo.get(document).addKeyListener(27, function(){
1382            if(active.length > 0){
1383                hideAll();
1384            }
1385        });
1386    }
1387
1388    // private
1389    function hideAll(){
1390        if(active && active.length > 0){
1391            var c = active.clone();
1392            c.each(function(m){
1393                m.hide();
1394            });
1395        }
1396    }
1397
1398    // private
1399    function onHide(m){
1400        active.remove(m);
1401        if(active.length < 1){
1402            Roo.get(document).un("mouseup", onMouseDown);
1403             
1404            attached = false;
1405        }
1406    }
1407
1408    // private
1409    function onShow(m){
1410        var last = active.last();
1411        lastShow = new Date();
1412        active.add(m);
1413        if(!attached){
1414           Roo.get(document).on("mouseup", onMouseDown);
1415            
1416            attached = true;
1417        }
1418        if(m.parentMenu){
1419           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1420           m.parentMenu.activeChild = m;
1421        }else if(last && last.isVisible()){
1422           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1423        }
1424    }
1425
1426    // private
1427    function onBeforeHide(m){
1428        if(m.activeChild){
1429            m.activeChild.hide();
1430        }
1431        if(m.autoHideTimer){
1432            clearTimeout(m.autoHideTimer);
1433            delete m.autoHideTimer;
1434        }
1435    }
1436
1437    // private
1438    function onBeforeShow(m){
1439        var pm = m.parentMenu;
1440        if(!pm && !m.allowOtherMenus){
1441            hideAll();
1442        }else if(pm && pm.activeChild && active != m){
1443            pm.activeChild.hide();
1444        }
1445    }
1446
1447    // private
1448    function onMouseDown(e){
1449         Roo.log("on MouseDown");
1450         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1451            hideAll();
1452         }
1453         
1454         
1455    }
1456
1457    // private
1458    function onBeforeCheck(mi, state){
1459        if(state){
1460            var g = groups[mi.group];
1461            for(var i = 0, l = g.length; i < l; i++){
1462                if(g[i] != mi){
1463                    g[i].setChecked(false);
1464                }
1465            }
1466        }
1467    }
1468
1469    return {
1470
1471        /**
1472         * Hides all menus that are currently visible
1473         */
1474        hideAll : function(){
1475             hideAll();  
1476        },
1477
1478        // private
1479        register : function(menu){
1480            if(!menus){
1481                init();
1482            }
1483            menus[menu.id] = menu;
1484            menu.on("beforehide", onBeforeHide);
1485            menu.on("hide", onHide);
1486            menu.on("beforeshow", onBeforeShow);
1487            menu.on("show", onShow);
1488            var g = menu.group;
1489            if(g && menu.events["checkchange"]){
1490                if(!groups[g]){
1491                    groups[g] = [];
1492                }
1493                groups[g].push(menu);
1494                menu.on("checkchange", onCheck);
1495            }
1496        },
1497
1498         /**
1499          * Returns a {@link Roo.menu.Menu} object
1500          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1501          * be used to generate and return a new Menu instance.
1502          */
1503        get : function(menu){
1504            if(typeof menu == "string"){ // menu id
1505                return menus[menu];
1506            }else if(menu.events){  // menu instance
1507                return menu;
1508            }
1509            /*else if(typeof menu.length == 'number'){ // array of menu items?
1510                return new Roo.bootstrap.Menu({items:menu});
1511            }else{ // otherwise, must be a config
1512                return new Roo.bootstrap.Menu(menu);
1513            }
1514            */
1515            return false;
1516        },
1517
1518        // private
1519        unregister : function(menu){
1520            delete menus[menu.id];
1521            menu.un("beforehide", onBeforeHide);
1522            menu.un("hide", onHide);
1523            menu.un("beforeshow", onBeforeShow);
1524            menu.un("show", onShow);
1525            var g = menu.group;
1526            if(g && menu.events["checkchange"]){
1527                groups[g].remove(menu);
1528                menu.un("checkchange", onCheck);
1529            }
1530        },
1531
1532        // private
1533        registerCheckable : function(menuItem){
1534            var g = menuItem.group;
1535            if(g){
1536                if(!groups[g]){
1537                    groups[g] = [];
1538                }
1539                groups[g].push(menuItem);
1540                menuItem.on("beforecheckchange", onBeforeCheck);
1541            }
1542        },
1543
1544        // private
1545        unregisterCheckable : function(menuItem){
1546            var g = menuItem.group;
1547            if(g){
1548                groups[g].remove(menuItem);
1549                menuItem.un("beforecheckchange", onBeforeCheck);
1550            }
1551        }
1552    };
1553 }();/*
1554  * - LGPL
1555  *
1556  * menu
1557  * 
1558  */
1559
1560 /**
1561  * @class Roo.bootstrap.Menu
1562  * @extends Roo.bootstrap.Component
1563  * Bootstrap Menu class - container for MenuItems
1564  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1565  * 
1566  * @constructor
1567  * Create a new Menu
1568  * @param {Object} config The config object
1569  */
1570
1571
1572 Roo.bootstrap.Menu = function(config){
1573     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1574     if (this.registerMenu) {
1575         Roo.bootstrap.MenuMgr.register(this);
1576     }
1577     this.addEvents({
1578         /**
1579          * @event beforeshow
1580          * Fires before this menu is displayed
1581          * @param {Roo.menu.Menu} this
1582          */
1583         beforeshow : true,
1584         /**
1585          * @event beforehide
1586          * Fires before this menu is hidden
1587          * @param {Roo.menu.Menu} this
1588          */
1589         beforehide : true,
1590         /**
1591          * @event show
1592          * Fires after this menu is displayed
1593          * @param {Roo.menu.Menu} this
1594          */
1595         show : true,
1596         /**
1597          * @event hide
1598          * Fires after this menu is hidden
1599          * @param {Roo.menu.Menu} this
1600          */
1601         hide : true,
1602         /**
1603          * @event click
1604          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1605          * @param {Roo.menu.Menu} this
1606          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1607          * @param {Roo.EventObject} e
1608          */
1609         click : true,
1610         /**
1611          * @event mouseover
1612          * Fires when the mouse is hovering over this menu
1613          * @param {Roo.menu.Menu} this
1614          * @param {Roo.EventObject} e
1615          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1616          */
1617         mouseover : true,
1618         /**
1619          * @event mouseout
1620          * Fires when the mouse exits this menu
1621          * @param {Roo.menu.Menu} this
1622          * @param {Roo.EventObject} e
1623          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1624          */
1625         mouseout : true,
1626         /**
1627          * @event itemclick
1628          * Fires when a menu item contained in this menu is clicked
1629          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1630          * @param {Roo.EventObject} e
1631          */
1632         itemclick: true
1633     });
1634     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1635 };
1636
1637 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1638     
1639    /// html : false,
1640     //align : '',
1641     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1642     type: false,
1643     /**
1644      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1645      */
1646     registerMenu : true,
1647     
1648     menuItems :false, // stores the menu items..
1649     
1650     hidden:true,
1651     
1652     parentMenu : false,
1653     
1654     getChildContainer : function() {
1655         return this.el;  
1656     },
1657     
1658     getAutoCreate : function(){
1659          
1660         //if (['right'].indexOf(this.align)!==-1) {
1661         //    cfg.cn[1].cls += ' pull-right'
1662         //}
1663         
1664         
1665         var cfg = {
1666             tag : 'ul',
1667             cls : 'dropdown-menu' ,
1668             style : 'z-index:1000'
1669             
1670         }
1671         
1672         if (this.type === 'submenu') {
1673             cfg.cls = 'submenu active';
1674         }
1675         if (this.type === 'treeview') {
1676             cfg.cls = 'treeview-menu';
1677         }
1678         
1679         return cfg;
1680     },
1681     initEvents : function() {
1682         
1683        // Roo.log("ADD event");
1684        // Roo.log(this.triggerEl.dom);
1685         this.triggerEl.on('click', this.onTriggerPress, this);
1686         this.triggerEl.addClass('dropdown-toggle');
1687         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1688
1689         this.el.on("mouseover", this.onMouseOver, this);
1690         this.el.on("mouseout", this.onMouseOut, this);
1691         
1692         
1693     },
1694     findTargetItem : function(e){
1695         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1696         if(!t){
1697             return false;
1698         }
1699         //Roo.log(t);         Roo.log(t.id);
1700         if(t && t.id){
1701             //Roo.log(this.menuitems);
1702             return this.menuitems.get(t.id);
1703             
1704             //return this.items.get(t.menuItemId);
1705         }
1706         
1707         return false;
1708     },
1709     onClick : function(e){
1710         Roo.log("menu.onClick");
1711         var t = this.findTargetItem(e);
1712         if(!t || t.isContainer){
1713             return;
1714         }
1715         Roo.log(e);
1716         /*
1717         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1718             if(t == this.activeItem && t.shouldDeactivate(e)){
1719                 this.activeItem.deactivate();
1720                 delete this.activeItem;
1721                 return;
1722             }
1723             if(t.canActivate){
1724                 this.setActiveItem(t, true);
1725             }
1726             return;
1727             
1728             
1729         }
1730         */
1731        
1732         Roo.log('pass click event');
1733         
1734         t.onClick(e);
1735         
1736         this.fireEvent("click", this, t, e);
1737         
1738         this.hide();
1739     },
1740      onMouseOver : function(e){
1741         var t  = this.findTargetItem(e);
1742         //Roo.log(t);
1743         //if(t){
1744         //    if(t.canActivate && !t.disabled){
1745         //        this.setActiveItem(t, true);
1746         //    }
1747         //}
1748         
1749         this.fireEvent("mouseover", this, e, t);
1750     },
1751     isVisible : function(){
1752         return !this.hidden;
1753     },
1754      onMouseOut : function(e){
1755         var t  = this.findTargetItem(e);
1756         
1757         //if(t ){
1758         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1759         //        this.activeItem.deactivate();
1760         //        delete this.activeItem;
1761         //    }
1762         //}
1763         this.fireEvent("mouseout", this, e, t);
1764     },
1765     
1766     
1767     /**
1768      * Displays this menu relative to another element
1769      * @param {String/HTMLElement/Roo.Element} element The element to align to
1770      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1771      * the element (defaults to this.defaultAlign)
1772      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1773      */
1774     show : function(el, pos, parentMenu){
1775         this.parentMenu = parentMenu;
1776         if(!this.el){
1777             this.render();
1778         }
1779         this.fireEvent("beforeshow", this);
1780         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1781     },
1782      /**
1783      * Displays this menu at a specific xy position
1784      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1785      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1786      */
1787     showAt : function(xy, parentMenu, /* private: */_e){
1788         this.parentMenu = parentMenu;
1789         if(!this.el){
1790             this.render();
1791         }
1792         if(_e !== false){
1793             this.fireEvent("beforeshow", this);
1794             
1795             //xy = this.el.adjustForConstraints(xy);
1796         }
1797         //this.el.setXY(xy);
1798         //this.el.show();
1799         this.hideMenuItems();
1800         this.hidden = false;
1801         this.triggerEl.addClass('open');
1802         this.focus();
1803         this.fireEvent("show", this);
1804     },
1805     
1806     focus : function(){
1807         return;
1808         if(!this.hidden){
1809             this.doFocus.defer(50, this);
1810         }
1811     },
1812
1813     doFocus : function(){
1814         if(!this.hidden){
1815             this.focusEl.focus();
1816         }
1817     },
1818
1819     /**
1820      * Hides this menu and optionally all parent menus
1821      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1822      */
1823     hide : function(deep){
1824         
1825         this.hideMenuItems();
1826         if(this.el && this.isVisible()){
1827             this.fireEvent("beforehide", this);
1828             if(this.activeItem){
1829                 this.activeItem.deactivate();
1830                 this.activeItem = null;
1831             }
1832             this.triggerEl.removeClass('open');;
1833             this.hidden = true;
1834             this.fireEvent("hide", this);
1835         }
1836         if(deep === true && this.parentMenu){
1837             this.parentMenu.hide(true);
1838         }
1839     },
1840     
1841     onTriggerPress  : function(e)
1842     {
1843         
1844         Roo.log('trigger press');
1845         //Roo.log(e.getTarget());
1846        // Roo.log(this.triggerEl.dom);
1847         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1848             return;
1849         }
1850         if (this.isVisible()) {
1851             Roo.log('hide');
1852             this.hide();
1853         } else {
1854             this.show(this.triggerEl, false, false);
1855         }
1856         
1857         
1858     },
1859     
1860          
1861        
1862     
1863     hideMenuItems : function()
1864     {
1865         //$(backdrop).remove()
1866         Roo.select('.open',true).each(function(aa) {
1867             
1868             aa.removeClass('open');
1869           //var parent = getParent($(this))
1870           //var relatedTarget = { relatedTarget: this }
1871           
1872            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1873           //if (e.isDefaultPrevented()) return
1874            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1875         })
1876     },
1877     addxtypeChild : function (tree, cntr) {
1878         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1879           
1880         this.menuitems.add(comp);
1881         return comp;
1882
1883     },
1884     getEl : function()
1885     {
1886         Roo.log(this.el);
1887         return this.el;
1888     }
1889 });
1890
1891  
1892  /*
1893  * - LGPL
1894  *
1895  * menu item
1896  * 
1897  */
1898
1899
1900 /**
1901  * @class Roo.bootstrap.MenuItem
1902  * @extends Roo.bootstrap.Component
1903  * Bootstrap MenuItem class
1904  * @cfg {String} html the menu label
1905  * @cfg {String} href the link
1906  * @cfg {Boolean} preventDefault (true | false) default true
1907  * @cfg {Boolean} isContainer (true | false) default false
1908  * 
1909  * 
1910  * @constructor
1911  * Create a new MenuItem
1912  * @param {Object} config The config object
1913  */
1914
1915
1916 Roo.bootstrap.MenuItem = function(config){
1917     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1918     this.addEvents({
1919         // raw events
1920         /**
1921          * @event click
1922          * The raw click event for the entire grid.
1923          * @param {Roo.EventObject} e
1924          */
1925         "click" : true
1926     });
1927 };
1928
1929 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1930     
1931     href : false,
1932     html : false,
1933     preventDefault: true,
1934     isContainer : false,
1935     
1936     getAutoCreate : function(){
1937         
1938         if(this.isContainer){
1939             return {
1940                 tag: 'li',
1941                 cls: 'dropdown-menu-item'
1942             };
1943         }
1944         
1945         var cfg= {
1946             tag: 'li',
1947             cls: 'dropdown-menu-item',
1948             cn: [
1949                     {
1950                         tag : 'a',
1951                         href : '#',
1952                         html : 'Link'
1953                     }
1954                 ]
1955         };
1956         if (this.parent().type == 'treeview') {
1957             cfg.cls = 'treeview-menu';
1958         }
1959         
1960         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1961         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1962         return cfg;
1963     },
1964     
1965     initEvents: function() {
1966         
1967         //this.el.select('a').on('click', this.onClick, this);
1968         
1969     },
1970     onClick : function(e)
1971     {
1972         Roo.log('item on click ');
1973         //if(this.preventDefault){
1974         //    e.preventDefault();
1975         //}
1976         //this.parent().hideMenuItems();
1977         
1978         this.fireEvent('click', this, e);
1979     },
1980     getEl : function()
1981     {
1982         return this.el;
1983     }
1984 });
1985
1986  
1987
1988  /*
1989  * - LGPL
1990  *
1991  * menu separator
1992  * 
1993  */
1994
1995
1996 /**
1997  * @class Roo.bootstrap.MenuSeparator
1998  * @extends Roo.bootstrap.Component
1999  * Bootstrap MenuSeparator class
2000  * 
2001  * @constructor
2002  * Create a new MenuItem
2003  * @param {Object} config The config object
2004  */
2005
2006
2007 Roo.bootstrap.MenuSeparator = function(config){
2008     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2009 };
2010
2011 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2012     
2013     getAutoCreate : function(){
2014         var cfg = {
2015             cls: 'divider',
2016             tag : 'li'
2017         };
2018         
2019         return cfg;
2020     }
2021    
2022 });
2023
2024  
2025
2026  
2027 /*
2028 <div class="modal fade">
2029   <div class="modal-dialog">
2030     <div class="modal-content">
2031       <div class="modal-header">
2032         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2033         <h4 class="modal-title">Modal title</h4>
2034       </div>
2035       <div class="modal-body">
2036         <p>One fine body&hellip;</p>
2037       </div>
2038       <div class="modal-footer">
2039         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2040         <button type="button" class="btn btn-primary">Save changes</button>
2041       </div>
2042     </div><!-- /.modal-content -->
2043   </div><!-- /.modal-dialog -->
2044 </div><!-- /.modal -->
2045 */
2046 /*
2047  * - LGPL
2048  *
2049  * page contgainer.
2050  * 
2051  */
2052
2053 /**
2054  * @class Roo.bootstrap.Modal
2055  * @extends Roo.bootstrap.Component
2056  * Bootstrap Modal class
2057  * @cfg {String} title Title of dialog
2058  * @cfg {Boolean} specificTitle (true|false) default false
2059  * @cfg {Array} buttons Array of buttons or standard button set..
2060  * @cfg {String} buttonPosition (left|right|center) default right
2061  * @cfg {Boolean} animate (true | false) default true
2062  * 
2063  * @constructor
2064  * Create a new Modal Dialog
2065  * @param {Object} config The config object
2066  */
2067
2068 Roo.bootstrap.Modal = function(config){
2069     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2070     this.addEvents({
2071         // raw events
2072         /**
2073          * @event btnclick
2074          * The raw btnclick event for the button
2075          * @param {Roo.EventObject} e
2076          */
2077         "btnclick" : true
2078     });
2079     this.buttons = this.buttons || [];
2080 };
2081
2082 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2083     
2084     title : 'test dialog',
2085    
2086     buttons : false,
2087     
2088     // set on load...
2089     body:  false,
2090     
2091     specificTitle: false,
2092     
2093     buttonPosition: 'right',
2094     
2095     animate : true,
2096     
2097     onRender : function(ct, position)
2098     {
2099         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2100      
2101         if(!this.el){
2102             var cfg = Roo.apply({},  this.getAutoCreate());
2103             cfg.id = Roo.id();
2104             //if(!cfg.name){
2105             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2106             //}
2107             //if (!cfg.name.length) {
2108             //    delete cfg.name;
2109            // }
2110             if (this.cls) {
2111                 cfg.cls += ' ' + this.cls;
2112             }
2113             if (this.style) {
2114                 cfg.style = this.style;
2115             }
2116             this.el = Roo.get(document.body).createChild(cfg, position);
2117         }
2118         //var type = this.el.dom.type;
2119         
2120         if(this.tabIndex !== undefined){
2121             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2122         }
2123         
2124         
2125         
2126         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2127         this.maskEl.enableDisplayMode("block");
2128         this.maskEl.hide();
2129         //this.el.addClass("x-dlg-modal");
2130     
2131         if (this.buttons.length) {
2132             Roo.each(this.buttons, function(bb) {
2133                 b = Roo.apply({}, bb);
2134                 b.xns = b.xns || Roo.bootstrap;
2135                 b.xtype = b.xtype || 'Button';
2136                 if (typeof(b.listeners) == 'undefined') {
2137                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2138                 }
2139                 
2140                 var btn = Roo.factory(b);
2141                 
2142                 btn.onRender(this.el.select('.modal-footer div').first());
2143                 
2144             },this);
2145         }
2146         // render the children.
2147         var nitems = [];
2148         
2149         if(typeof(this.items) != 'undefined'){
2150             var items = this.items;
2151             delete this.items;
2152
2153             for(var i =0;i < items.length;i++) {
2154                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2155             }
2156         }
2157         
2158         this.items = nitems;
2159         
2160         this.body = this.el.select('.modal-body',true).first();
2161         this.close = this.el.select('.modal-header .close', true).first();
2162         this.footer = this.el.select('.modal-footer',true).first();
2163         this.initEvents();
2164         //this.el.addClass([this.fieldClass, this.cls]);
2165         
2166     },
2167     getAutoCreate : function(){
2168         
2169         
2170         var bdy = {
2171                 cls : 'modal-body',
2172                 html : this.html || ''
2173         };
2174         
2175         var title = {
2176             tag: 'h4',
2177             cls : 'modal-title',
2178             html : this.title
2179         };
2180         
2181         if(this.specificTitle){
2182             title = this.title;
2183         };
2184         
2185         var modal = {
2186             cls: "modal",
2187             style : 'display: none',
2188             cn : [
2189                 {
2190                     cls: "modal-dialog",
2191                     cn : [
2192                         {
2193                             cls : "modal-content",
2194                             cn : [
2195                                 {
2196                                     cls : 'modal-header',
2197                                     cn : [
2198                                         {
2199                                             tag: 'button',
2200                                             cls : 'close',
2201                                             html : '&times'
2202                                         },
2203                                         title
2204                                     ]
2205                                 },
2206                                 bdy,
2207                                 {
2208                                     cls : 'modal-footer',
2209                                     cn : [
2210                                         {
2211                                             tag: 'div',
2212                                             cls: 'btn-' + this.buttonPosition
2213                                         }
2214                                     ]
2215                                     
2216                                 }
2217                                 
2218                                 
2219                             ]
2220                             
2221                         }
2222                     ]
2223                         
2224                 }
2225             ]
2226         };
2227         
2228         if(this.animate){
2229             modal.cls += ' fade';
2230         }
2231         
2232         return modal;
2233           
2234     },
2235     getChildContainer : function() {
2236          
2237          return this.el.select('.modal-body',true).first();
2238         
2239     },
2240     getButtonContainer : function() {
2241          return this.el.select('.modal-footer div',true).first();
2242         
2243     },
2244     initEvents : function()
2245     {
2246         this.el.select('.modal-header .close').on('click', this.hide, this);
2247 //        
2248 //        this.addxtype(this);
2249     },
2250     show : function() {
2251         
2252         if (!this.rendered) {
2253             this.render();
2254         }
2255         
2256         this.el.setStyle('display', 'block');
2257         
2258         if(this.animate){
2259             var _this = this;
2260             (function(){ _this.el.addClass('in'); }).defer(50);
2261         }else{
2262             this.el.addClass('in');
2263         }
2264         
2265         Roo.get(document.body).addClass("x-body-masked");
2266         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2267         this.maskEl.show();
2268         this.el.setStyle('zIndex', '10001');
2269         this.fireEvent('show', this);
2270         
2271         
2272     },
2273     hide : function()
2274     {
2275         this.maskEl.hide();
2276         Roo.get(document.body).removeClass("x-body-masked");
2277         this.el.removeClass('in');
2278         
2279         if(this.animate){
2280             var _this = this;
2281             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2282         }else{
2283             this.el.setStyle('display', 'none');
2284         }
2285         
2286         this.fireEvent('hide', this);
2287     },
2288     
2289     addButton : function(str, cb)
2290     {
2291          
2292         
2293         var b = Roo.apply({}, { html : str } );
2294         b.xns = b.xns || Roo.bootstrap;
2295         b.xtype = b.xtype || 'Button';
2296         if (typeof(b.listeners) == 'undefined') {
2297             b.listeners = { click : cb.createDelegate(this)  };
2298         }
2299         
2300         var btn = Roo.factory(b);
2301            
2302         btn.onRender(this.el.select('.modal-footer div').first());
2303         
2304         return btn;   
2305        
2306     },
2307     
2308     setDefaultButton : function(btn)
2309     {
2310         //this.el.select('.modal-footer').()
2311     },
2312     resizeTo: function(w,h)
2313     {
2314         // skip..
2315     },
2316     setContentSize  : function(w, h)
2317     {
2318         
2319     },
2320     onButtonClick: function(btn,e)
2321     {
2322         //Roo.log([a,b,c]);
2323         this.fireEvent('btnclick', btn.name, e);
2324     },
2325     setTitle: function(str) {
2326         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2327         
2328     }
2329 });
2330
2331
2332 Roo.apply(Roo.bootstrap.Modal,  {
2333     /**
2334          * Button config that displays a single OK button
2335          * @type Object
2336          */
2337         OK :  [{
2338             name : 'ok',
2339             weight : 'primary',
2340             html : 'OK'
2341         }], 
2342         /**
2343          * Button config that displays Yes and No buttons
2344          * @type Object
2345          */
2346         YESNO : [
2347             {
2348                 name  : 'no',
2349                 html : 'No'
2350             },
2351             {
2352                 name  :'yes',
2353                 weight : 'primary',
2354                 html : 'Yes'
2355             }
2356         ],
2357         
2358         /**
2359          * Button config that displays OK and Cancel buttons
2360          * @type Object
2361          */
2362         OKCANCEL : [
2363             {
2364                name : 'cancel',
2365                 html : 'Cancel'
2366             },
2367             {
2368                 name : 'ok',
2369                 weight : 'primary',
2370                 html : 'OK'
2371             }
2372         ],
2373         /**
2374          * Button config that displays Yes, No and Cancel buttons
2375          * @type Object
2376          */
2377         YESNOCANCEL : [
2378             {
2379                 name : 'yes',
2380                 weight : 'primary',
2381                 html : 'Yes'
2382             },
2383             {
2384                 name : 'no',
2385                 html : 'No'
2386             },
2387             {
2388                 name : 'cancel',
2389                 html : 'Cancel'
2390             }
2391         ]
2392 });
2393  /*
2394  * - LGPL
2395  *
2396  * messagebox - can be used as a replace
2397  * 
2398  */
2399 /**
2400  * @class Roo.MessageBox
2401  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2402  * Example usage:
2403  *<pre><code>
2404 // Basic alert:
2405 Roo.Msg.alert('Status', 'Changes saved successfully.');
2406
2407 // Prompt for user data:
2408 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2409     if (btn == 'ok'){
2410         // process text value...
2411     }
2412 });
2413
2414 // Show a dialog using config options:
2415 Roo.Msg.show({
2416    title:'Save Changes?',
2417    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2418    buttons: Roo.Msg.YESNOCANCEL,
2419    fn: processResult,
2420    animEl: 'elId'
2421 });
2422 </code></pre>
2423  * @singleton
2424  */
2425 Roo.bootstrap.MessageBox = function(){
2426     var dlg, opt, mask, waitTimer;
2427     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2428     var buttons, activeTextEl, bwidth;
2429
2430     
2431     // private
2432     var handleButton = function(button){
2433         dlg.hide();
2434         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2435     };
2436
2437     // private
2438     var handleHide = function(){
2439         if(opt && opt.cls){
2440             dlg.el.removeClass(opt.cls);
2441         }
2442         //if(waitTimer){
2443         //    Roo.TaskMgr.stop(waitTimer);
2444         //    waitTimer = null;
2445         //}
2446     };
2447
2448     // private
2449     var updateButtons = function(b){
2450         var width = 0;
2451         if(!b){
2452             buttons["ok"].hide();
2453             buttons["cancel"].hide();
2454             buttons["yes"].hide();
2455             buttons["no"].hide();
2456             //dlg.footer.dom.style.display = 'none';
2457             return width;
2458         }
2459         dlg.footer.dom.style.display = '';
2460         for(var k in buttons){
2461             if(typeof buttons[k] != "function"){
2462                 if(b[k]){
2463                     buttons[k].show();
2464                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2465                     width += buttons[k].el.getWidth()+15;
2466                 }else{
2467                     buttons[k].hide();
2468                 }
2469             }
2470         }
2471         return width;
2472     };
2473
2474     // private
2475     var handleEsc = function(d, k, e){
2476         if(opt && opt.closable !== false){
2477             dlg.hide();
2478         }
2479         if(e){
2480             e.stopEvent();
2481         }
2482     };
2483
2484     return {
2485         /**
2486          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2487          * @return {Roo.BasicDialog} The BasicDialog element
2488          */
2489         getDialog : function(){
2490            if(!dlg){
2491                 dlg = new Roo.bootstrap.Modal( {
2492                     //draggable: true,
2493                     //resizable:false,
2494                     //constraintoviewport:false,
2495                     //fixedcenter:true,
2496                     //collapsible : false,
2497                     //shim:true,
2498                     //modal: true,
2499                   //  width:400,
2500                   //  height:100,
2501                     //buttonAlign:"center",
2502                     closeClick : function(){
2503                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2504                             handleButton("no");
2505                         }else{
2506                             handleButton("cancel");
2507                         }
2508                     }
2509                 });
2510                 dlg.render();
2511                 dlg.on("hide", handleHide);
2512                 mask = dlg.mask;
2513                 //dlg.addKeyListener(27, handleEsc);
2514                 buttons = {};
2515                 this.buttons = buttons;
2516                 var bt = this.buttonText;
2517                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2518                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2519                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2520                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2521                 Roo.log(buttons)
2522                 bodyEl = dlg.body.createChild({
2523
2524                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2525                         '<textarea class="roo-mb-textarea"></textarea>' +
2526                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2527                 });
2528                 msgEl = bodyEl.dom.firstChild;
2529                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2530                 textboxEl.enableDisplayMode();
2531                 textboxEl.addKeyListener([10,13], function(){
2532                     if(dlg.isVisible() && opt && opt.buttons){
2533                         if(opt.buttons.ok){
2534                             handleButton("ok");
2535                         }else if(opt.buttons.yes){
2536                             handleButton("yes");
2537                         }
2538                     }
2539                 });
2540                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2541                 textareaEl.enableDisplayMode();
2542                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2543                 progressEl.enableDisplayMode();
2544                 var pf = progressEl.dom.firstChild;
2545                 if (pf) {
2546                     pp = Roo.get(pf.firstChild);
2547                     pp.setHeight(pf.offsetHeight);
2548                 }
2549                 
2550             }
2551             return dlg;
2552         },
2553
2554         /**
2555          * Updates the message box body text
2556          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2557          * the XHTML-compliant non-breaking space character '&amp;#160;')
2558          * @return {Roo.MessageBox} This message box
2559          */
2560         updateText : function(text){
2561             if(!dlg.isVisible() && !opt.width){
2562                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2563             }
2564             msgEl.innerHTML = text || '&#160;';
2565       
2566             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2567             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2568             var w = Math.max(
2569                     Math.min(opt.width || cw , this.maxWidth), 
2570                     Math.max(opt.minWidth || this.minWidth, bwidth)
2571             );
2572             if(opt.prompt){
2573                 activeTextEl.setWidth(w);
2574             }
2575             if(dlg.isVisible()){
2576                 dlg.fixedcenter = false;
2577             }
2578             // to big, make it scroll. = But as usual stupid IE does not support
2579             // !important..
2580             
2581             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2582                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2583                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2584             } else {
2585                 bodyEl.dom.style.height = '';
2586                 bodyEl.dom.style.overflowY = '';
2587             }
2588             if (cw > w) {
2589                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2590             } else {
2591                 bodyEl.dom.style.overflowX = '';
2592             }
2593             
2594             dlg.setContentSize(w, bodyEl.getHeight());
2595             if(dlg.isVisible()){
2596                 dlg.fixedcenter = true;
2597             }
2598             return this;
2599         },
2600
2601         /**
2602          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2603          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2604          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2605          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2606          * @return {Roo.MessageBox} This message box
2607          */
2608         updateProgress : function(value, text){
2609             if(text){
2610                 this.updateText(text);
2611             }
2612             if (pp) { // weird bug on my firefox - for some reason this is not defined
2613                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2614             }
2615             return this;
2616         },        
2617
2618         /**
2619          * Returns true if the message box is currently displayed
2620          * @return {Boolean} True if the message box is visible, else false
2621          */
2622         isVisible : function(){
2623             return dlg && dlg.isVisible();  
2624         },
2625
2626         /**
2627          * Hides the message box if it is displayed
2628          */
2629         hide : function(){
2630             if(this.isVisible()){
2631                 dlg.hide();
2632             }  
2633         },
2634
2635         /**
2636          * Displays a new message box, or reinitializes an existing message box, based on the config options
2637          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2638          * The following config object properties are supported:
2639          * <pre>
2640 Property    Type             Description
2641 ----------  ---------------  ------------------------------------------------------------------------------------
2642 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2643                                    closes (defaults to undefined)
2644 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2645                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2646 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2647                                    progress and wait dialogs will ignore this property and always hide the
2648                                    close button as they can only be closed programmatically.
2649 cls               String           A custom CSS class to apply to the message box element
2650 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2651                                    displayed (defaults to 75)
2652 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2653                                    function will be btn (the name of the button that was clicked, if applicable,
2654                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2655                                    Progress and wait dialogs will ignore this option since they do not respond to
2656                                    user actions and can only be closed programmatically, so any required function
2657                                    should be called by the same code after it closes the dialog.
2658 icon              String           A CSS class that provides a background image to be used as an icon for
2659                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2660 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2661 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2662 modal             Boolean          False to allow user interaction with the page while the message box is
2663                                    displayed (defaults to true)
2664 msg               String           A string that will replace the existing message box body text (defaults
2665                                    to the XHTML-compliant non-breaking space character '&#160;')
2666 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2667 progress          Boolean          True to display a progress bar (defaults to false)
2668 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2669 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2670 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2671 title             String           The title text
2672 value             String           The string value to set into the active textbox element if displayed
2673 wait              Boolean          True to display a progress bar (defaults to false)
2674 width             Number           The width of the dialog in pixels
2675 </pre>
2676          *
2677          * Example usage:
2678          * <pre><code>
2679 Roo.Msg.show({
2680    title: 'Address',
2681    msg: 'Please enter your address:',
2682    width: 300,
2683    buttons: Roo.MessageBox.OKCANCEL,
2684    multiline: true,
2685    fn: saveAddress,
2686    animEl: 'addAddressBtn'
2687 });
2688 </code></pre>
2689          * @param {Object} config Configuration options
2690          * @return {Roo.MessageBox} This message box
2691          */
2692         show : function(options)
2693         {
2694             
2695             // this causes nightmares if you show one dialog after another
2696             // especially on callbacks..
2697              
2698             if(this.isVisible()){
2699                 
2700                 this.hide();
2701                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2702                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2703                 Roo.log("New Dialog Message:" +  options.msg )
2704                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2705                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2706                 
2707             }
2708             var d = this.getDialog();
2709             opt = options;
2710             d.setTitle(opt.title || "&#160;");
2711             d.close.setDisplayed(opt.closable !== false);
2712             activeTextEl = textboxEl;
2713             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2714             if(opt.prompt){
2715                 if(opt.multiline){
2716                     textboxEl.hide();
2717                     textareaEl.show();
2718                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2719                         opt.multiline : this.defaultTextHeight);
2720                     activeTextEl = textareaEl;
2721                 }else{
2722                     textboxEl.show();
2723                     textareaEl.hide();
2724                 }
2725             }else{
2726                 textboxEl.hide();
2727                 textareaEl.hide();
2728             }
2729             progressEl.setDisplayed(opt.progress === true);
2730             this.updateProgress(0);
2731             activeTextEl.dom.value = opt.value || "";
2732             if(opt.prompt){
2733                 dlg.setDefaultButton(activeTextEl);
2734             }else{
2735                 var bs = opt.buttons;
2736                 var db = null;
2737                 if(bs && bs.ok){
2738                     db = buttons["ok"];
2739                 }else if(bs && bs.yes){
2740                     db = buttons["yes"];
2741                 }
2742                 dlg.setDefaultButton(db);
2743             }
2744             bwidth = updateButtons(opt.buttons);
2745             this.updateText(opt.msg);
2746             if(opt.cls){
2747                 d.el.addClass(opt.cls);
2748             }
2749             d.proxyDrag = opt.proxyDrag === true;
2750             d.modal = opt.modal !== false;
2751             d.mask = opt.modal !== false ? mask : false;
2752             if(!d.isVisible()){
2753                 // force it to the end of the z-index stack so it gets a cursor in FF
2754                 document.body.appendChild(dlg.el.dom);
2755                 d.animateTarget = null;
2756                 d.show(options.animEl);
2757             }
2758             return this;
2759         },
2760
2761         /**
2762          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2763          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2764          * and closing the message box when the process is complete.
2765          * @param {String} title The title bar text
2766          * @param {String} msg The message box body text
2767          * @return {Roo.MessageBox} This message box
2768          */
2769         progress : function(title, msg){
2770             this.show({
2771                 title : title,
2772                 msg : msg,
2773                 buttons: false,
2774                 progress:true,
2775                 closable:false,
2776                 minWidth: this.minProgressWidth,
2777                 modal : true
2778             });
2779             return this;
2780         },
2781
2782         /**
2783          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2784          * If a callback function is passed it will be called after the user clicks the button, and the
2785          * id of the button that was clicked will be passed as the only parameter to the callback
2786          * (could also be the top-right close button).
2787          * @param {String} title The title bar text
2788          * @param {String} msg The message box body text
2789          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2790          * @param {Object} scope (optional) The scope of the callback function
2791          * @return {Roo.MessageBox} This message box
2792          */
2793         alert : function(title, msg, fn, scope){
2794             this.show({
2795                 title : title,
2796                 msg : msg,
2797                 buttons: this.OK,
2798                 fn: fn,
2799                 scope : scope,
2800                 modal : true
2801             });
2802             return this;
2803         },
2804
2805         /**
2806          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2807          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2808          * You are responsible for closing the message box when the process is complete.
2809          * @param {String} msg The message box body text
2810          * @param {String} title (optional) The title bar text
2811          * @return {Roo.MessageBox} This message box
2812          */
2813         wait : function(msg, title){
2814             this.show({
2815                 title : title,
2816                 msg : msg,
2817                 buttons: false,
2818                 closable:false,
2819                 progress:true,
2820                 modal:true,
2821                 width:300,
2822                 wait:true
2823             });
2824             waitTimer = Roo.TaskMgr.start({
2825                 run: function(i){
2826                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2827                 },
2828                 interval: 1000
2829             });
2830             return this;
2831         },
2832
2833         /**
2834          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2835          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2836          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2837          * @param {String} title The title bar text
2838          * @param {String} msg The message box body text
2839          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2840          * @param {Object} scope (optional) The scope of the callback function
2841          * @return {Roo.MessageBox} This message box
2842          */
2843         confirm : function(title, msg, fn, scope){
2844             this.show({
2845                 title : title,
2846                 msg : msg,
2847                 buttons: this.YESNO,
2848                 fn: fn,
2849                 scope : scope,
2850                 modal : true
2851             });
2852             return this;
2853         },
2854
2855         /**
2856          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2857          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2858          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2859          * (could also be the top-right close button) and the text that was entered will be passed as the two
2860          * parameters to the callback.
2861          * @param {String} title The title bar text
2862          * @param {String} msg The message box body text
2863          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2864          * @param {Object} scope (optional) The scope of the callback function
2865          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2866          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2867          * @return {Roo.MessageBox} This message box
2868          */
2869         prompt : function(title, msg, fn, scope, multiline){
2870             this.show({
2871                 title : title,
2872                 msg : msg,
2873                 buttons: this.OKCANCEL,
2874                 fn: fn,
2875                 minWidth:250,
2876                 scope : scope,
2877                 prompt:true,
2878                 multiline: multiline,
2879                 modal : true
2880             });
2881             return this;
2882         },
2883
2884         /**
2885          * Button config that displays a single OK button
2886          * @type Object
2887          */
2888         OK : {ok:true},
2889         /**
2890          * Button config that displays Yes and No buttons
2891          * @type Object
2892          */
2893         YESNO : {yes:true, no:true},
2894         /**
2895          * Button config that displays OK and Cancel buttons
2896          * @type Object
2897          */
2898         OKCANCEL : {ok:true, cancel:true},
2899         /**
2900          * Button config that displays Yes, No and Cancel buttons
2901          * @type Object
2902          */
2903         YESNOCANCEL : {yes:true, no:true, cancel:true},
2904
2905         /**
2906          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2907          * @type Number
2908          */
2909         defaultTextHeight : 75,
2910         /**
2911          * The maximum width in pixels of the message box (defaults to 600)
2912          * @type Number
2913          */
2914         maxWidth : 600,
2915         /**
2916          * The minimum width in pixels of the message box (defaults to 100)
2917          * @type Number
2918          */
2919         minWidth : 100,
2920         /**
2921          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2922          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2923          * @type Number
2924          */
2925         minProgressWidth : 250,
2926         /**
2927          * An object containing the default button text strings that can be overriden for localized language support.
2928          * Supported properties are: ok, cancel, yes and no.
2929          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2930          * @type Object
2931          */
2932         buttonText : {
2933             ok : "OK",
2934             cancel : "Cancel",
2935             yes : "Yes",
2936             no : "No"
2937         }
2938     };
2939 }();
2940
2941 /**
2942  * Shorthand for {@link Roo.MessageBox}
2943  */
2944 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2945 Roo.Msg = Roo.Msg || Roo.MessageBox;
2946 /*
2947  * - LGPL
2948  *
2949  * navbar
2950  * 
2951  */
2952
2953 /**
2954  * @class Roo.bootstrap.Navbar
2955  * @extends Roo.bootstrap.Component
2956  * Bootstrap Navbar class
2957
2958  * @constructor
2959  * Create a new Navbar
2960  * @param {Object} config The config object
2961  */
2962
2963
2964 Roo.bootstrap.Navbar = function(config){
2965     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2966     
2967 };
2968
2969 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2970     
2971     
2972    
2973     // private
2974     navItems : false,
2975     loadMask : false,
2976     
2977     
2978     getAutoCreate : function(){
2979         
2980         
2981         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2982         
2983     },
2984     
2985     initEvents :function ()
2986     {
2987         //Roo.log(this.el.select('.navbar-toggle',true));
2988         this.el.select('.navbar-toggle',true).on('click', function() {
2989            // Roo.log('click');
2990             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2991         }, this);
2992         
2993         var mark = {
2994             tag: "div",
2995             cls:"x-dlg-mask"
2996         }
2997         
2998         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2999         
3000         var size = this.el.getSize();
3001         this.maskEl.setSize(size.width, size.height);
3002         this.maskEl.enableDisplayMode("block");
3003         this.maskEl.hide();
3004         
3005         if(this.loadMask){
3006             this.maskEl.show();
3007         }
3008     },
3009     
3010     
3011     getChildContainer : function()
3012     {
3013         if (this.el.select('.collapse').getCount()) {
3014             return this.el.select('.collapse',true).first();
3015         }
3016         
3017         return this.el;
3018     },
3019     
3020     mask : function()
3021     {
3022         this.maskEl.show();
3023     },
3024     
3025     unmask : function()
3026     {
3027         this.maskEl.hide();
3028     } 
3029     
3030     
3031     
3032     
3033 });
3034
3035
3036
3037  
3038
3039  /*
3040  * - LGPL
3041  *
3042  * navbar
3043  * 
3044  */
3045
3046 /**
3047  * @class Roo.bootstrap.NavSimplebar
3048  * @extends Roo.bootstrap.Navbar
3049  * Bootstrap Sidebar class
3050  *
3051  * @cfg {Boolean} inverse is inverted color
3052  * 
3053  * @cfg {String} type (nav | pills | tabs)
3054  * @cfg {Boolean} arrangement stacked | justified
3055  * @cfg {String} align (left | right) alignment
3056  * 
3057  * @cfg {Boolean} main (true|false) main nav bar? default false
3058  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3059  * 
3060  * @cfg {String} tag (header|footer|nav|div) default is nav 
3061
3062  * 
3063  * 
3064  * 
3065  * @constructor
3066  * Create a new Sidebar
3067  * @param {Object} config The config object
3068  */
3069
3070
3071 Roo.bootstrap.NavSimplebar = function(config){
3072     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3073 };
3074
3075 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3076     
3077     inverse: false,
3078     
3079     type: false,
3080     arrangement: '',
3081     align : false,
3082     
3083     
3084     
3085     main : false,
3086     
3087     
3088     tag : false,
3089     
3090     
3091     getAutoCreate : function(){
3092         
3093         
3094         var cfg = {
3095             tag : this.tag || 'div',
3096             cls : 'navbar'
3097         };
3098           
3099         
3100         cfg.cn = [
3101             {
3102                 cls: 'nav',
3103                 tag : 'ul'
3104             }
3105         ];
3106         
3107          
3108         this.type = this.type || 'nav';
3109         if (['tabs','pills'].indexOf(this.type)!==-1) {
3110             cfg.cn[0].cls += ' nav-' + this.type
3111         
3112         
3113         } else {
3114             if (this.type!=='nav') {
3115                 Roo.log('nav type must be nav/tabs/pills')
3116             }
3117             cfg.cn[0].cls += ' navbar-nav'
3118         }
3119         
3120         
3121         
3122         
3123         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3124             cfg.cn[0].cls += ' nav-' + this.arrangement;
3125         }
3126         
3127         
3128         if (this.align === 'right') {
3129             cfg.cn[0].cls += ' navbar-right';
3130         }
3131         
3132         if (this.inverse) {
3133             cfg.cls += ' navbar-inverse';
3134             
3135         }
3136         
3137         
3138         return cfg;
3139     
3140         
3141     }
3142     
3143     
3144     
3145 });
3146
3147
3148
3149  
3150
3151  
3152        /*
3153  * - LGPL
3154  *
3155  * navbar
3156  * 
3157  */
3158
3159 /**
3160  * @class Roo.bootstrap.NavHeaderbar
3161  * @extends Roo.bootstrap.NavSimplebar
3162  * Bootstrap Sidebar class
3163  *
3164  * @cfg {String} brand what is brand
3165  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3166  * @cfg {String} brand_href href of the brand
3167  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3168  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3169  * 
3170  * @constructor
3171  * Create a new Sidebar
3172  * @param {Object} config The config object
3173  */
3174
3175
3176 Roo.bootstrap.NavHeaderbar = function(config){
3177     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3178 };
3179
3180 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3181     
3182     position: '',
3183     brand: '',
3184     brand_href: false,
3185     srButton : true,
3186     autohide : false,
3187     
3188     getAutoCreate : function(){
3189         
3190         var   cfg = {
3191             tag: this.nav || 'nav',
3192             cls: 'navbar',
3193             role: 'navigation',
3194             cn: []
3195         };
3196         
3197         if(this.srButton){
3198             cfg.cn.push({
3199                 tag: 'div',
3200                 cls: 'navbar-header',
3201                 cn: [
3202                     {
3203                         tag: 'button',
3204                         type: 'button',
3205                         cls: 'navbar-toggle',
3206                         'data-toggle': 'collapse',
3207                         cn: [
3208                             {
3209                                 tag: 'span',
3210                                 cls: 'sr-only',
3211                                 html: 'Toggle navigation'
3212                             },
3213                             {
3214                                 tag: 'span',
3215                                 cls: 'icon-bar'
3216                             },
3217                             {
3218                                 tag: 'span',
3219                                 cls: 'icon-bar'
3220                             },
3221                             {
3222                                 tag: 'span',
3223                                 cls: 'icon-bar'
3224                             }
3225                         ]
3226                     }
3227                 ]
3228             });
3229         }
3230         
3231         cfg.cn.push({
3232             tag: 'div',
3233             cls: 'collapse navbar-collapse',
3234             cn : []
3235         });
3236         
3237         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3238         
3239         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3240             cfg.cls += ' navbar-' + this.position;
3241             
3242             // tag can override this..
3243             
3244             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3245         }
3246         
3247         if (this.brand !== '') {
3248             cfg.cn[0].cn.push({
3249                 tag: 'a',
3250                 href: this.brand_href ? this.brand_href : '#',
3251                 cls: 'navbar-brand',
3252                 cn: [
3253                 this.brand
3254                 ]
3255             });
3256         }
3257         
3258         if(this.main){
3259             cfg.cls += ' main-nav';
3260         }
3261         
3262         
3263         return cfg;
3264
3265         
3266     },
3267     initEvents : function()
3268     {
3269         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3270         
3271         if (this.autohide) {
3272             
3273             var prevScroll = 0;
3274             var ft = this.el;
3275             
3276             Roo.get(document).on('scroll',function(e) {
3277                 var ns = Roo.get(document).getScroll().top;
3278                 var os = prevScroll;
3279                 prevScroll = ns;
3280                 
3281                 if(ns > os){
3282                     ft.removeClass('slideDown');
3283                     ft.addClass('slideUp');
3284                     return;
3285                 }
3286                 ft.removeClass('slideUp');
3287                 ft.addClass('slideDown');
3288                  
3289               
3290           },this);
3291         }
3292     }    
3293           
3294       
3295     
3296     
3297 });
3298
3299
3300
3301  
3302
3303  /*
3304  * - LGPL
3305  *
3306  * navbar
3307  * 
3308  */
3309
3310 /**
3311  * @class Roo.bootstrap.NavSidebar
3312  * @extends Roo.bootstrap.Navbar
3313  * Bootstrap Sidebar class
3314  * 
3315  * @constructor
3316  * Create a new Sidebar
3317  * @param {Object} config The config object
3318  */
3319
3320
3321 Roo.bootstrap.NavSidebar = function(config){
3322     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3323 };
3324
3325 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3326     
3327     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3328     
3329     getAutoCreate : function(){
3330         
3331         
3332         return  {
3333             tag: 'div',
3334             cls: 'sidebar sidebar-nav'
3335         };
3336     
3337         
3338     }
3339     
3340     
3341     
3342 });
3343
3344
3345
3346  
3347
3348  /*
3349  * - LGPL
3350  *
3351  * nav group
3352  * 
3353  */
3354
3355 /**
3356  * @class Roo.bootstrap.NavGroup
3357  * @extends Roo.bootstrap.Component
3358  * Bootstrap NavGroup class
3359  * @cfg {String} align left | right
3360  * @cfg {Boolean} inverse false | true
3361  * @cfg {String} type (nav|pills|tab) default nav
3362  * @cfg {String} navId - reference Id for navbar.
3363
3364  * 
3365  * @constructor
3366  * Create a new nav group
3367  * @param {Object} config The config object
3368  */
3369
3370 Roo.bootstrap.NavGroup = function(config){
3371     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3372     this.navItems = [];
3373    
3374     Roo.bootstrap.NavGroup.register(this);
3375      this.addEvents({
3376         /**
3377              * @event changed
3378              * Fires when the active item changes
3379              * @param {Roo.bootstrap.NavGroup} this
3380              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3381              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3382          */
3383         'changed': true
3384      });
3385     
3386 };
3387
3388 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3389     
3390     align: '',
3391     inverse: false,
3392     form: false,
3393     type: 'nav',
3394     navId : '',
3395     // private
3396     
3397     navItems : false, 
3398     
3399     getAutoCreate : function()
3400     {
3401         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3402         
3403         cfg = {
3404             tag : 'ul',
3405             cls: 'nav' 
3406         }
3407         
3408         if (['tabs','pills'].indexOf(this.type)!==-1) {
3409             cfg.cls += ' nav-' + this.type
3410         } else {
3411             if (this.type!=='nav') {
3412                 Roo.log('nav type must be nav/tabs/pills')
3413             }
3414             cfg.cls += ' navbar-nav'
3415         }
3416         
3417         if (this.parent().sidebar) {
3418             cfg = {
3419                 tag: 'ul',
3420                 cls: 'dashboard-menu sidebar-menu'
3421             }
3422             
3423             return cfg;
3424         }
3425         
3426         if (this.form === true) {
3427             cfg = {
3428                 tag: 'form',
3429                 cls: 'navbar-form'
3430             }
3431             
3432             if (this.align === 'right') {
3433                 cfg.cls += ' navbar-right';
3434             } else {
3435                 cfg.cls += ' navbar-left';
3436             }
3437         }
3438         
3439         if (this.align === 'right') {
3440             cfg.cls += ' navbar-right';
3441         }
3442         
3443         if (this.inverse) {
3444             cfg.cls += ' navbar-inverse';
3445             
3446         }
3447         
3448         
3449         return cfg;
3450     },
3451     /**
3452     * sets the active Navigation item
3453     * @param {Roo.bootstrap.NavItem} the new current navitem
3454     */
3455     setActiveItem : function(item)
3456     {
3457         var prev = false;
3458         Roo.each(this.navItems, function(v){
3459             if (v == item) {
3460                 return ;
3461             }
3462             if (v.isActive()) {
3463                 v.setActive(false, true);
3464                 prev = v;
3465                 
3466             }
3467             
3468         });
3469
3470         item.setActive(true, true);
3471         this.fireEvent('changed', this, item, prev);
3472         
3473         
3474     },
3475     /**
3476     * gets the active Navigation item
3477     * @return {Roo.bootstrap.NavItem} the current navitem
3478     */
3479     getActive : function()
3480     {
3481         
3482         var prev = false;
3483         Roo.each(this.navItems, function(v){
3484             
3485             if (v.isActive()) {
3486                 prev = v;
3487                 
3488             }
3489             
3490         });
3491         return prev;
3492     },
3493     
3494     indexOfNav : function()
3495     {
3496         
3497         var prev = false;
3498         Roo.each(this.navItems, function(v,i){
3499             
3500             if (v.isActive()) {
3501                 prev = i;
3502                 
3503             }
3504             
3505         });
3506         return prev;
3507     },
3508     /**
3509     * adds a Navigation item
3510     * @param {Roo.bootstrap.NavItem} the navitem to add
3511     */
3512     addItem : function(cfg)
3513     {
3514         var cn = new Roo.bootstrap.NavItem(cfg);
3515         this.register(cn);
3516         cn.parentId = this.id;
3517         cn.onRender(this.el, null);
3518         return cn;
3519     },
3520     /**
3521     * register a Navigation item
3522     * @param {Roo.bootstrap.NavItem} the navitem to add
3523     */
3524     register : function(item)
3525     {
3526         this.navItems.push( item);
3527         item.navId = this.navId;
3528     
3529     },
3530     
3531     /**
3532     * clear all the Navigation item
3533     */
3534    
3535     clearAll : function()
3536     {
3537         this.navItems = [];
3538         this.el.dom.innerHTML = '';
3539     },
3540     
3541     getNavItem: function(tabId)
3542     {
3543         var ret = false;
3544         Roo.each(this.navItems, function(e) {
3545             if (e.tabId == tabId) {
3546                ret =  e;
3547                return false;
3548             }
3549             return true;
3550             
3551         });
3552         return ret;
3553     },
3554     
3555     setActiveNext : function()
3556     {
3557         var i = this.indexOfNav(this.getActive());
3558         if (i > this.navItems.length) {
3559             return;
3560         }
3561         this.setActiveItem(this.navItems[i+1]);
3562     },
3563     setActivePrev : function()
3564     {
3565         var i = this.indexOfNav(this.getActive());
3566         if (i  < 1) {
3567             return;
3568         }
3569         this.setActiveItem(this.navItems[i-1]);
3570     },
3571     clearWasActive : function(except) {
3572         Roo.each(this.navItems, function(e) {
3573             if (e.tabId != except.tabId && e.was_active) {
3574                e.was_active = false;
3575                return false;
3576             }
3577             return true;
3578             
3579         });
3580     },
3581     getWasActive : function ()
3582     {
3583         var r = false;
3584         Roo.each(this.navItems, function(e) {
3585             if (e.was_active) {
3586                r = e;
3587                return false;
3588             }
3589             return true;
3590             
3591         });
3592         return r;
3593     }
3594     
3595     
3596 });
3597
3598  
3599 Roo.apply(Roo.bootstrap.NavGroup, {
3600     
3601     groups: {},
3602      /**
3603     * register a Navigation Group
3604     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3605     */
3606     register : function(navgrp)
3607     {
3608         this.groups[navgrp.navId] = navgrp;
3609         
3610     },
3611     /**
3612     * fetch a Navigation Group based on the navigation ID
3613     * @param {string} the navgroup to add
3614     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3615     */
3616     get: function(navId) {
3617         if (typeof(this.groups[navId]) == 'undefined') {
3618             return false;
3619             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3620         }
3621         return this.groups[navId] ;
3622     }
3623     
3624     
3625     
3626 });
3627
3628  /*
3629  * - LGPL
3630  *
3631  * row
3632  * 
3633  */
3634
3635 /**
3636  * @class Roo.bootstrap.NavItem
3637  * @extends Roo.bootstrap.Component
3638  * Bootstrap Navbar.NavItem class
3639  * @cfg {String} href  link to
3640  * @cfg {String} html content of button
3641  * @cfg {String} badge text inside badge
3642  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3643  * @cfg {String} glyphicon name of glyphicon
3644  * @cfg {String} icon name of font awesome icon
3645  * @cfg {Boolean} active Is item active
3646  * @cfg {Boolean} disabled Is item disabled
3647  
3648  * @cfg {Boolean} preventDefault (true | false) default false
3649  * @cfg {String} tabId the tab that this item activates.
3650  * @cfg {String} tagtype (a|span) render as a href or span?
3651   
3652  * @constructor
3653  * Create a new Navbar Item
3654  * @param {Object} config The config object
3655  */
3656 Roo.bootstrap.NavItem = function(config){
3657     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3658     this.addEvents({
3659         // raw events
3660         /**
3661          * @event click
3662          * The raw click event for the entire grid.
3663          * @param {Roo.EventObject} e
3664          */
3665         "click" : true,
3666          /**
3667             * @event changed
3668             * Fires when the active item active state changes
3669             * @param {Roo.bootstrap.NavItem} this
3670             * @param {boolean} state the new state
3671              
3672          */
3673         'changed': true
3674     });
3675    
3676 };
3677
3678 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3679     
3680     href: false,
3681     html: '',
3682     badge: '',
3683     icon: false,
3684     glyphicon: false,
3685     active: false,
3686     preventDefault : false,
3687     tabId : false,
3688     tagtype : 'a',
3689     disabled : false,
3690     
3691     was_active : false,
3692     
3693     getAutoCreate : function(){
3694          
3695         var cfg = {
3696             tag: 'li',
3697             cls: 'nav-item'
3698             
3699         }
3700         if (this.active) {
3701             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3702         }
3703         if (this.disabled) {
3704             cfg.cls += ' disabled';
3705         }
3706         
3707         if (this.href || this.html || this.glyphicon || this.icon) {
3708             cfg.cn = [
3709                 {
3710                     tag: this.tagtype,
3711                     href : this.href || "#",
3712                     html: this.html || ''
3713                 }
3714             ];
3715             
3716             if (this.icon) {
3717                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3718             }
3719
3720             if(this.glyphicon) {
3721                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3722             }
3723             
3724             if (this.menu) {
3725                 
3726                 cfg.cn[0].html += " <span class='caret'></span>";
3727              
3728             }
3729             
3730             if (this.badge !== '') {
3731                  
3732                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3733             }
3734         }
3735         
3736         
3737         
3738         return cfg;
3739     },
3740     initEvents: function() 
3741     {
3742         if (typeof (this.menu) != 'undefined') {
3743             this.menu.parentType = this.xtype;
3744             this.menu.triggerEl = this.el;
3745             this.addxtype(Roo.apply({}, this.menu));
3746         }
3747         
3748         this.el.select('a',true).on('click', this.onClick, this);
3749         
3750         if(this.tagtype == 'span'){
3751             this.el.select('span',true).on('click', this.onClick, this);
3752         }
3753        
3754         // at this point parent should be available..
3755         this.parent().register(this);
3756     },
3757     
3758     onClick : function(e)
3759     {
3760          
3761         if(this.preventDefault){
3762             e.preventDefault();
3763         }
3764         if (this.disabled) {
3765             return;
3766         }
3767         
3768         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3769         if (tg && tg.transition) {
3770             Roo.log("waiting for the transitionend");
3771             return;
3772         }
3773         
3774         Roo.log("fire event clicked");
3775         if(this.fireEvent('click', this, e) === false){
3776             return;
3777         };
3778         
3779         if(this.tagtype == 'span'){
3780             return;
3781         }
3782         
3783         var p = this.parent();
3784         if (['tabs','pills'].indexOf(p.type)!==-1) {
3785             if (typeof(p.setActiveItem) !== 'undefined') {
3786                 p.setActiveItem(this);
3787             }
3788         }
3789         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3790         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3791             // remove the collapsed menu expand...
3792             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3793         }
3794         
3795     },
3796     
3797     isActive: function () {
3798         return this.active
3799     },
3800     setActive : function(state, fire, is_was_active)
3801     {
3802         if (this.active && !state & this.navId) {
3803             this.was_active = true;
3804             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3805             if (nv) {
3806                 nv.clearWasActive(this);
3807             }
3808             
3809         }
3810         this.active = state;
3811         
3812         if (!state ) {
3813             this.el.removeClass('active');
3814         } else if (!this.el.hasClass('active')) {
3815             this.el.addClass('active');
3816         }
3817         if (fire) {
3818             this.fireEvent('changed', this, state);
3819         }
3820         
3821         // show a panel if it's registered and related..
3822         
3823         if (!this.navId || !this.tabId || !state || is_was_active) {
3824             return;
3825         }
3826         
3827         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3828         if (!tg) {
3829             return;
3830         }
3831         var pan = tg.getPanelByName(this.tabId);
3832         if (!pan) {
3833             return;
3834         }
3835         // if we can not flip to new panel - go back to old nav highlight..
3836         if (false == tg.showPanel(pan)) {
3837             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3838             if (nv) {
3839                 var onav = nv.getWasActive();
3840                 if (onav) {
3841                     onav.setActive(true, false, true);
3842                 }
3843             }
3844             
3845         }
3846         
3847         
3848         
3849     },
3850      // this should not be here...
3851     setDisabled : function(state)
3852     {
3853         this.disabled = state;
3854         if (!state ) {
3855             this.el.removeClass('disabled');
3856         } else if (!this.el.hasClass('disabled')) {
3857             this.el.addClass('disabled');
3858         }
3859         
3860     }
3861 });
3862  
3863
3864  /*
3865  * - LGPL
3866  *
3867  * sidebar item
3868  *
3869  *  li
3870  *    <span> icon </span>
3871  *    <span> text </span>
3872  *    <span>badge </span>
3873  */
3874
3875 /**
3876  * @class Roo.bootstrap.NavSidebarItem
3877  * @extends Roo.bootstrap.NavItem
3878  * Bootstrap Navbar.NavSidebarItem class
3879  * @constructor
3880  * Create a new Navbar Button
3881  * @param {Object} config The config object
3882  */
3883 Roo.bootstrap.NavSidebarItem = function(config){
3884     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3885     this.addEvents({
3886         // raw events
3887         /**
3888          * @event click
3889          * The raw click event for the entire grid.
3890          * @param {Roo.EventObject} e
3891          */
3892         "click" : true,
3893          /**
3894             * @event changed
3895             * Fires when the active item active state changes
3896             * @param {Roo.bootstrap.NavSidebarItem} this
3897             * @param {boolean} state the new state
3898              
3899          */
3900         'changed': true
3901     });
3902    
3903 };
3904
3905 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3906     
3907     
3908     getAutoCreate : function(){
3909         
3910         
3911         var a = {
3912                 tag: 'a',
3913                 href : this.href || '#',
3914                 cls: '',
3915                 html : '',
3916                 cn : []
3917         };
3918         var cfg = {
3919             tag: 'li',
3920             cls: '',
3921             cn: [ a ]
3922         }
3923         var span = {
3924             tag: 'span',
3925             html : this.html || ''
3926         }
3927         
3928         
3929         if (this.active) {
3930             cfg.cls += ' active';
3931         }
3932         
3933         // left icon..
3934         if (this.glyphicon || this.icon) {
3935             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3936             a.cn.push({ tag : 'i', cls : c }) ;
3937         }
3938         // html..
3939         a.cn.push(span);
3940         // then badge..
3941         if (this.badge !== '') {
3942             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3943         }
3944         // fi
3945         if (this.menu) {
3946             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3947             a.cls += 'dropdown-toggle treeview' ;
3948             
3949         }
3950         
3951         
3952         
3953         return cfg;
3954          
3955            
3956     }
3957    
3958      
3959  
3960 });
3961  
3962
3963  /*
3964  * - LGPL
3965  *
3966  * row
3967  * 
3968  */
3969
3970 /**
3971  * @class Roo.bootstrap.Row
3972  * @extends Roo.bootstrap.Component
3973  * Bootstrap Row class (contains columns...)
3974  * 
3975  * @constructor
3976  * Create a new Row
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.Row = function(config){
3981     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3982 };
3983
3984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3985     
3986     getAutoCreate : function(){
3987        return {
3988             cls: 'row clearfix'
3989        };
3990     }
3991     
3992     
3993 });
3994
3995  
3996
3997  /*
3998  * - LGPL
3999  *
4000  * element
4001  * 
4002  */
4003
4004 /**
4005  * @class Roo.bootstrap.Element
4006  * @extends Roo.bootstrap.Component
4007  * Bootstrap Element class
4008  * @cfg {String} html contents of the element
4009  * @cfg {String} tag tag of the element
4010  * @cfg {String} cls class of the element
4011  * 
4012  * @constructor
4013  * Create a new Element
4014  * @param {Object} config The config object
4015  */
4016
4017 Roo.bootstrap.Element = function(config){
4018     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4019 };
4020
4021 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4022     
4023     tag: 'div',
4024     cls: '',
4025     html: '',
4026      
4027     
4028     getAutoCreate : function(){
4029         
4030         var cfg = {
4031             tag: this.tag,
4032             cls: this.cls,
4033             html: this.html
4034         }
4035         
4036         
4037         
4038         return cfg;
4039     }
4040    
4041 });
4042
4043  
4044
4045  /*
4046  * - LGPL
4047  *
4048  * pagination
4049  * 
4050  */
4051
4052 /**
4053  * @class Roo.bootstrap.Pagination
4054  * @extends Roo.bootstrap.Component
4055  * Bootstrap Pagination class
4056  * @cfg {String} size xs | sm | md | lg
4057  * @cfg {Boolean} inverse false | true
4058  * 
4059  * @constructor
4060  * Create a new Pagination
4061  * @param {Object} config The config object
4062  */
4063
4064 Roo.bootstrap.Pagination = function(config){
4065     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4066 };
4067
4068 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4069     
4070     cls: false,
4071     size: false,
4072     inverse: false,
4073     
4074     getAutoCreate : function(){
4075         var cfg = {
4076             tag: 'ul',
4077                 cls: 'pagination'
4078         };
4079         if (this.inverse) {
4080             cfg.cls += ' inverse';
4081         }
4082         if (this.html) {
4083             cfg.html=this.html;
4084         }
4085         if (this.cls) {
4086             cfg.cls += " " + this.cls;
4087         }
4088         return cfg;
4089     }
4090    
4091 });
4092
4093  
4094
4095  /*
4096  * - LGPL
4097  *
4098  * Pagination item
4099  * 
4100  */
4101
4102
4103 /**
4104  * @class Roo.bootstrap.PaginationItem
4105  * @extends Roo.bootstrap.Component
4106  * Bootstrap PaginationItem class
4107  * @cfg {String} html text
4108  * @cfg {String} href the link
4109  * @cfg {Boolean} preventDefault (true | false) default true
4110  * @cfg {Boolean} active (true | false) default false
4111  * 
4112  * 
4113  * @constructor
4114  * Create a new PaginationItem
4115  * @param {Object} config The config object
4116  */
4117
4118
4119 Roo.bootstrap.PaginationItem = function(config){
4120     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4121     this.addEvents({
4122         // raw events
4123         /**
4124          * @event click
4125          * The raw click event for the entire grid.
4126          * @param {Roo.EventObject} e
4127          */
4128         "click" : true
4129     });
4130 };
4131
4132 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4133     
4134     href : false,
4135     html : false,
4136     preventDefault: true,
4137     active : false,
4138     cls : false,
4139     
4140     getAutoCreate : function(){
4141         var cfg= {
4142             tag: 'li',
4143             cn: [
4144                 {
4145                     tag : 'a',
4146                     href : this.href ? this.href : '#',
4147                     html : this.html ? this.html : ''
4148                 }
4149             ]
4150         };
4151         
4152         if(this.cls){
4153             cfg.cls = this.cls;
4154         }
4155         
4156         if(this.active){
4157             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4158         }
4159         
4160         return cfg;
4161     },
4162     
4163     initEvents: function() {
4164         
4165         this.el.on('click', this.onClick, this);
4166         
4167     },
4168     onClick : function(e)
4169     {
4170         Roo.log('PaginationItem on click ');
4171         if(this.preventDefault){
4172             e.preventDefault();
4173         }
4174         
4175         this.fireEvent('click', this, e);
4176     }
4177    
4178 });
4179
4180  
4181
4182  /*
4183  * - LGPL
4184  *
4185  * slider
4186  * 
4187  */
4188
4189
4190 /**
4191  * @class Roo.bootstrap.Slider
4192  * @extends Roo.bootstrap.Component
4193  * Bootstrap Slider class
4194  *    
4195  * @constructor
4196  * Create a new Slider
4197  * @param {Object} config The config object
4198  */
4199
4200 Roo.bootstrap.Slider = function(config){
4201     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4202 };
4203
4204 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4205     
4206     getAutoCreate : function(){
4207         
4208         var cfg = {
4209             tag: 'div',
4210             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4211             cn: [
4212                 {
4213                     tag: 'a',
4214                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4215                 }
4216             ]
4217         }
4218         
4219         return cfg;
4220     }
4221    
4222 });
4223
4224  /*
4225  * Based on:
4226  * Ext JS Library 1.1.1
4227  * Copyright(c) 2006-2007, Ext JS, LLC.
4228  *
4229  * Originally Released Under LGPL - original licence link has changed is not relivant.
4230  *
4231  * Fork - LGPL
4232  * <script type="text/javascript">
4233  */
4234  
4235
4236 /**
4237  * @class Roo.grid.ColumnModel
4238  * @extends Roo.util.Observable
4239  * This is the default implementation of a ColumnModel used by the Grid. It defines
4240  * the columns in the grid.
4241  * <br>Usage:<br>
4242  <pre><code>
4243  var colModel = new Roo.grid.ColumnModel([
4244         {header: "Ticker", width: 60, sortable: true, locked: true},
4245         {header: "Company Name", width: 150, sortable: true},
4246         {header: "Market Cap.", width: 100, sortable: true},
4247         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4248         {header: "Employees", width: 100, sortable: true, resizable: false}
4249  ]);
4250  </code></pre>
4251  * <p>
4252  
4253  * The config options listed for this class are options which may appear in each
4254  * individual column definition.
4255  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4256  * @constructor
4257  * @param {Object} config An Array of column config objects. See this class's
4258  * config objects for details.
4259 */
4260 Roo.grid.ColumnModel = function(config){
4261         /**
4262      * The config passed into the constructor
4263      */
4264     this.config = config;
4265     this.lookup = {};
4266
4267     // if no id, create one
4268     // if the column does not have a dataIndex mapping,
4269     // map it to the order it is in the config
4270     for(var i = 0, len = config.length; i < len; i++){
4271         var c = config[i];
4272         if(typeof c.dataIndex == "undefined"){
4273             c.dataIndex = i;
4274         }
4275         if(typeof c.renderer == "string"){
4276             c.renderer = Roo.util.Format[c.renderer];
4277         }
4278         if(typeof c.id == "undefined"){
4279             c.id = Roo.id();
4280         }
4281         if(c.editor && c.editor.xtype){
4282             c.editor  = Roo.factory(c.editor, Roo.grid);
4283         }
4284         if(c.editor && c.editor.isFormField){
4285             c.editor = new Roo.grid.GridEditor(c.editor);
4286         }
4287         this.lookup[c.id] = c;
4288     }
4289
4290     /**
4291      * The width of columns which have no width specified (defaults to 100)
4292      * @type Number
4293      */
4294     this.defaultWidth = 100;
4295
4296     /**
4297      * Default sortable of columns which have no sortable specified (defaults to false)
4298      * @type Boolean
4299      */
4300     this.defaultSortable = false;
4301
4302     this.addEvents({
4303         /**
4304              * @event widthchange
4305              * Fires when the width of a column changes.
4306              * @param {ColumnModel} this
4307              * @param {Number} columnIndex The column index
4308              * @param {Number} newWidth The new width
4309              */
4310             "widthchange": true,
4311         /**
4312              * @event headerchange
4313              * Fires when the text of a header changes.
4314              * @param {ColumnModel} this
4315              * @param {Number} columnIndex The column index
4316              * @param {Number} newText The new header text
4317              */
4318             "headerchange": true,
4319         /**
4320              * @event hiddenchange
4321              * Fires when a column is hidden or "unhidden".
4322              * @param {ColumnModel} this
4323              * @param {Number} columnIndex The column index
4324              * @param {Boolean} hidden true if hidden, false otherwise
4325              */
4326             "hiddenchange": true,
4327             /**
4328          * @event columnmoved
4329          * Fires when a column is moved.
4330          * @param {ColumnModel} this
4331          * @param {Number} oldIndex
4332          * @param {Number} newIndex
4333          */
4334         "columnmoved" : true,
4335         /**
4336          * @event columlockchange
4337          * Fires when a column's locked state is changed
4338          * @param {ColumnModel} this
4339          * @param {Number} colIndex
4340          * @param {Boolean} locked true if locked
4341          */
4342         "columnlockchange" : true
4343     });
4344     Roo.grid.ColumnModel.superclass.constructor.call(this);
4345 };
4346 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4347     /**
4348      * @cfg {String} header The header text to display in the Grid view.
4349      */
4350     /**
4351      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4352      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4353      * specified, the column's index is used as an index into the Record's data Array.
4354      */
4355     /**
4356      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4357      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4358      */
4359     /**
4360      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4361      * Defaults to the value of the {@link #defaultSortable} property.
4362      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4363      */
4364     /**
4365      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4366      */
4367     /**
4368      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4369      */
4370     /**
4371      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4372      */
4373     /**
4374      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4375      */
4376     /**
4377      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4378      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4379      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4380      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4381      */
4382        /**
4383      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4384      */
4385     /**
4386      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4387      */
4388
4389     /**
4390      * Returns the id of the column at the specified index.
4391      * @param {Number} index The column index
4392      * @return {String} the id
4393      */
4394     getColumnId : function(index){
4395         return this.config[index].id;
4396     },
4397
4398     /**
4399      * Returns the column for a specified id.
4400      * @param {String} id The column id
4401      * @return {Object} the column
4402      */
4403     getColumnById : function(id){
4404         return this.lookup[id];
4405     },
4406
4407     
4408     /**
4409      * Returns the column for a specified dataIndex.
4410      * @param {String} dataIndex The column dataIndex
4411      * @return {Object|Boolean} the column or false if not found
4412      */
4413     getColumnByDataIndex: function(dataIndex){
4414         var index = this.findColumnIndex(dataIndex);
4415         return index > -1 ? this.config[index] : false;
4416     },
4417     
4418     /**
4419      * Returns the index for a specified column id.
4420      * @param {String} id The column id
4421      * @return {Number} the index, or -1 if not found
4422      */
4423     getIndexById : function(id){
4424         for(var i = 0, len = this.config.length; i < len; i++){
4425             if(this.config[i].id == id){
4426                 return i;
4427             }
4428         }
4429         return -1;
4430     },
4431     
4432     /**
4433      * Returns the index for a specified column dataIndex.
4434      * @param {String} dataIndex The column dataIndex
4435      * @return {Number} the index, or -1 if not found
4436      */
4437     
4438     findColumnIndex : function(dataIndex){
4439         for(var i = 0, len = this.config.length; i < len; i++){
4440             if(this.config[i].dataIndex == dataIndex){
4441                 return i;
4442             }
4443         }
4444         return -1;
4445     },
4446     
4447     
4448     moveColumn : function(oldIndex, newIndex){
4449         var c = this.config[oldIndex];
4450         this.config.splice(oldIndex, 1);
4451         this.config.splice(newIndex, 0, c);
4452         this.dataMap = null;
4453         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4454     },
4455
4456     isLocked : function(colIndex){
4457         return this.config[colIndex].locked === true;
4458     },
4459
4460     setLocked : function(colIndex, value, suppressEvent){
4461         if(this.isLocked(colIndex) == value){
4462             return;
4463         }
4464         this.config[colIndex].locked = value;
4465         if(!suppressEvent){
4466             this.fireEvent("columnlockchange", this, colIndex, value);
4467         }
4468     },
4469
4470     getTotalLockedWidth : function(){
4471         var totalWidth = 0;
4472         for(var i = 0; i < this.config.length; i++){
4473             if(this.isLocked(i) && !this.isHidden(i)){
4474                 this.totalWidth += this.getColumnWidth(i);
4475             }
4476         }
4477         return totalWidth;
4478     },
4479
4480     getLockedCount : function(){
4481         for(var i = 0, len = this.config.length; i < len; i++){
4482             if(!this.isLocked(i)){
4483                 return i;
4484             }
4485         }
4486     },
4487
4488     /**
4489      * Returns the number of columns.
4490      * @return {Number}
4491      */
4492     getColumnCount : function(visibleOnly){
4493         if(visibleOnly === true){
4494             var c = 0;
4495             for(var i = 0, len = this.config.length; i < len; i++){
4496                 if(!this.isHidden(i)){
4497                     c++;
4498                 }
4499             }
4500             return c;
4501         }
4502         return this.config.length;
4503     },
4504
4505     /**
4506      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4507      * @param {Function} fn
4508      * @param {Object} scope (optional)
4509      * @return {Array} result
4510      */
4511     getColumnsBy : function(fn, scope){
4512         var r = [];
4513         for(var i = 0, len = this.config.length; i < len; i++){
4514             var c = this.config[i];
4515             if(fn.call(scope||this, c, i) === true){
4516                 r[r.length] = c;
4517             }
4518         }
4519         return r;
4520     },
4521
4522     /**
4523      * Returns true if the specified column is sortable.
4524      * @param {Number} col The column index
4525      * @return {Boolean}
4526      */
4527     isSortable : function(col){
4528         if(typeof this.config[col].sortable == "undefined"){
4529             return this.defaultSortable;
4530         }
4531         return this.config[col].sortable;
4532     },
4533
4534     /**
4535      * Returns the rendering (formatting) function defined for the column.
4536      * @param {Number} col The column index.
4537      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4538      */
4539     getRenderer : function(col){
4540         if(!this.config[col].renderer){
4541             return Roo.grid.ColumnModel.defaultRenderer;
4542         }
4543         return this.config[col].renderer;
4544     },
4545
4546     /**
4547      * Sets the rendering (formatting) function for a column.
4548      * @param {Number} col The column index
4549      * @param {Function} fn The function to use to process the cell's raw data
4550      * to return HTML markup for the grid view. The render function is called with
4551      * the following parameters:<ul>
4552      * <li>Data value.</li>
4553      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4554      * <li>css A CSS style string to apply to the table cell.</li>
4555      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4556      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4557      * <li>Row index</li>
4558      * <li>Column index</li>
4559      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4560      */
4561     setRenderer : function(col, fn){
4562         this.config[col].renderer = fn;
4563     },
4564
4565     /**
4566      * Returns the width for the specified column.
4567      * @param {Number} col The column index
4568      * @return {Number}
4569      */
4570     getColumnWidth : function(col){
4571         return this.config[col].width * 1 || this.defaultWidth;
4572     },
4573
4574     /**
4575      * Sets the width for a column.
4576      * @param {Number} col The column index
4577      * @param {Number} width The new width
4578      */
4579     setColumnWidth : function(col, width, suppressEvent){
4580         this.config[col].width = width;
4581         this.totalWidth = null;
4582         if(!suppressEvent){
4583              this.fireEvent("widthchange", this, col, width);
4584         }
4585     },
4586
4587     /**
4588      * Returns the total width of all columns.
4589      * @param {Boolean} includeHidden True to include hidden column widths
4590      * @return {Number}
4591      */
4592     getTotalWidth : function(includeHidden){
4593         if(!this.totalWidth){
4594             this.totalWidth = 0;
4595             for(var i = 0, len = this.config.length; i < len; i++){
4596                 if(includeHidden || !this.isHidden(i)){
4597                     this.totalWidth += this.getColumnWidth(i);
4598                 }
4599             }
4600         }
4601         return this.totalWidth;
4602     },
4603
4604     /**
4605      * Returns the header for the specified column.
4606      * @param {Number} col The column index
4607      * @return {String}
4608      */
4609     getColumnHeader : function(col){
4610         return this.config[col].header;
4611     },
4612
4613     /**
4614      * Sets the header for a column.
4615      * @param {Number} col The column index
4616      * @param {String} header The new header
4617      */
4618     setColumnHeader : function(col, header){
4619         this.config[col].header = header;
4620         this.fireEvent("headerchange", this, col, header);
4621     },
4622
4623     /**
4624      * Returns the tooltip for the specified column.
4625      * @param {Number} col The column index
4626      * @return {String}
4627      */
4628     getColumnTooltip : function(col){
4629             return this.config[col].tooltip;
4630     },
4631     /**
4632      * Sets the tooltip for a column.
4633      * @param {Number} col The column index
4634      * @param {String} tooltip The new tooltip
4635      */
4636     setColumnTooltip : function(col, tooltip){
4637             this.config[col].tooltip = tooltip;
4638     },
4639
4640     /**
4641      * Returns the dataIndex for the specified column.
4642      * @param {Number} col The column index
4643      * @return {Number}
4644      */
4645     getDataIndex : function(col){
4646         return this.config[col].dataIndex;
4647     },
4648
4649     /**
4650      * Sets the dataIndex for a column.
4651      * @param {Number} col The column index
4652      * @param {Number} dataIndex The new dataIndex
4653      */
4654     setDataIndex : function(col, dataIndex){
4655         this.config[col].dataIndex = dataIndex;
4656     },
4657
4658     
4659     
4660     /**
4661      * Returns true if the cell is editable.
4662      * @param {Number} colIndex The column index
4663      * @param {Number} rowIndex The row index
4664      * @return {Boolean}
4665      */
4666     isCellEditable : function(colIndex, rowIndex){
4667         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4668     },
4669
4670     /**
4671      * Returns the editor defined for the cell/column.
4672      * return false or null to disable editing.
4673      * @param {Number} colIndex The column index
4674      * @param {Number} rowIndex The row index
4675      * @return {Object}
4676      */
4677     getCellEditor : function(colIndex, rowIndex){
4678         return this.config[colIndex].editor;
4679     },
4680
4681     /**
4682      * Sets if a column is editable.
4683      * @param {Number} col The column index
4684      * @param {Boolean} editable True if the column is editable
4685      */
4686     setEditable : function(col, editable){
4687         this.config[col].editable = editable;
4688     },
4689
4690
4691     /**
4692      * Returns true if the column is hidden.
4693      * @param {Number} colIndex The column index
4694      * @return {Boolean}
4695      */
4696     isHidden : function(colIndex){
4697         return this.config[colIndex].hidden;
4698     },
4699
4700
4701     /**
4702      * Returns true if the column width cannot be changed
4703      */
4704     isFixed : function(colIndex){
4705         return this.config[colIndex].fixed;
4706     },
4707
4708     /**
4709      * Returns true if the column can be resized
4710      * @return {Boolean}
4711      */
4712     isResizable : function(colIndex){
4713         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4714     },
4715     /**
4716      * Sets if a column is hidden.
4717      * @param {Number} colIndex The column index
4718      * @param {Boolean} hidden True if the column is hidden
4719      */
4720     setHidden : function(colIndex, hidden){
4721         this.config[colIndex].hidden = hidden;
4722         this.totalWidth = null;
4723         this.fireEvent("hiddenchange", this, colIndex, hidden);
4724     },
4725
4726     /**
4727      * Sets the editor for a column.
4728      * @param {Number} col The column index
4729      * @param {Object} editor The editor object
4730      */
4731     setEditor : function(col, editor){
4732         this.config[col].editor = editor;
4733     }
4734 });
4735
4736 Roo.grid.ColumnModel.defaultRenderer = function(value){
4737         if(typeof value == "string" && value.length < 1){
4738             return "&#160;";
4739         }
4740         return value;
4741 };
4742
4743 // Alias for backwards compatibility
4744 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4745 /*
4746  * Based on:
4747  * Ext JS Library 1.1.1
4748  * Copyright(c) 2006-2007, Ext JS, LLC.
4749  *
4750  * Originally Released Under LGPL - original licence link has changed is not relivant.
4751  *
4752  * Fork - LGPL
4753  * <script type="text/javascript">
4754  */
4755  
4756 /**
4757  * @class Roo.LoadMask
4758  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4759  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4760  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4761  * element's UpdateManager load indicator and will be destroyed after the initial load.
4762  * @constructor
4763  * Create a new LoadMask
4764  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4765  * @param {Object} config The config object
4766  */
4767 Roo.LoadMask = function(el, config){
4768     this.el = Roo.get(el);
4769     Roo.apply(this, config);
4770     if(this.store){
4771         this.store.on('beforeload', this.onBeforeLoad, this);
4772         this.store.on('load', this.onLoad, this);
4773         this.store.on('loadexception', this.onLoadException, this);
4774         this.removeMask = false;
4775     }else{
4776         var um = this.el.getUpdateManager();
4777         um.showLoadIndicator = false; // disable the default indicator
4778         um.on('beforeupdate', this.onBeforeLoad, this);
4779         um.on('update', this.onLoad, this);
4780         um.on('failure', this.onLoad, this);
4781         this.removeMask = true;
4782     }
4783 };
4784
4785 Roo.LoadMask.prototype = {
4786     /**
4787      * @cfg {Boolean} removeMask
4788      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4789      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4790      */
4791     /**
4792      * @cfg {String} msg
4793      * The text to display in a centered loading message box (defaults to 'Loading...')
4794      */
4795     msg : 'Loading...',
4796     /**
4797      * @cfg {String} msgCls
4798      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4799      */
4800     msgCls : 'x-mask-loading',
4801
4802     /**
4803      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4804      * @type Boolean
4805      */
4806     disabled: false,
4807
4808     /**
4809      * Disables the mask to prevent it from being displayed
4810      */
4811     disable : function(){
4812        this.disabled = true;
4813     },
4814
4815     /**
4816      * Enables the mask so that it can be displayed
4817      */
4818     enable : function(){
4819         this.disabled = false;
4820     },
4821     
4822     onLoadException : function()
4823     {
4824         Roo.log(arguments);
4825         
4826         if (typeof(arguments[3]) != 'undefined') {
4827             Roo.MessageBox.alert("Error loading",arguments[3]);
4828         } 
4829         /*
4830         try {
4831             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4832                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4833             }   
4834         } catch(e) {
4835             
4836         }
4837         */
4838     
4839         
4840         
4841         this.el.unmask(this.removeMask);
4842     },
4843     // private
4844     onLoad : function()
4845     {
4846         this.el.unmask(this.removeMask);
4847     },
4848
4849     // private
4850     onBeforeLoad : function(){
4851         if(!this.disabled){
4852             this.el.mask(this.msg, this.msgCls);
4853         }
4854     },
4855
4856     // private
4857     destroy : function(){
4858         if(this.store){
4859             this.store.un('beforeload', this.onBeforeLoad, this);
4860             this.store.un('load', this.onLoad, this);
4861             this.store.un('loadexception', this.onLoadException, this);
4862         }else{
4863             var um = this.el.getUpdateManager();
4864             um.un('beforeupdate', this.onBeforeLoad, this);
4865             um.un('update', this.onLoad, this);
4866             um.un('failure', this.onLoad, this);
4867         }
4868     }
4869 };/*
4870  * - LGPL
4871  *
4872  * table
4873  * 
4874  */
4875
4876 /**
4877  * @class Roo.bootstrap.Table
4878  * @extends Roo.bootstrap.Component
4879  * Bootstrap Table class
4880  * @cfg {String} cls table class
4881  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4882  * @cfg {String} bgcolor Specifies the background color for a table
4883  * @cfg {Number} border Specifies whether the table cells should have borders or not
4884  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4885  * @cfg {Number} cellspacing Specifies the space between cells
4886  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4887  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4888  * @cfg {String} sortable Specifies that the table should be sortable
4889  * @cfg {String} summary Specifies a summary of the content of a table
4890  * @cfg {Number} width Specifies the width of a table
4891  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4892  * 
4893  * @cfg {boolean} striped Should the rows be alternative striped
4894  * @cfg {boolean} bordered Add borders to the table
4895  * @cfg {boolean} hover Add hover highlighting
4896  * @cfg {boolean} condensed Format condensed
4897  * @cfg {boolean} responsive Format condensed
4898  * @cfg {Boolean} loadMask (true|false) default false
4899  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4900  * @cfg {Boolean} thead (true|false) generate thead, default true
4901  * @cfg {Boolean} RowSelection (true|false) default false
4902  * @cfg {Boolean} CellSelection (true|false) default false
4903  *
4904  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4905  
4906  * 
4907  * @constructor
4908  * Create a new Table
4909  * @param {Object} config The config object
4910  */
4911
4912 Roo.bootstrap.Table = function(config){
4913     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4914     
4915     if (this.sm) {
4916         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4917         this.sm = this.selModel;
4918         this.sm.xmodule = this.xmodule || false;
4919     }
4920     if (this.cm && typeof(this.cm.config) == 'undefined') {
4921         this.colModel = new Roo.grid.ColumnModel(this.cm);
4922         this.cm = this.colModel;
4923         this.cm.xmodule = this.xmodule || false;
4924     }
4925     if (this.store) {
4926         this.store= Roo.factory(this.store, Roo.data);
4927         this.ds = this.store;
4928         this.ds.xmodule = this.xmodule || false;
4929          
4930     }
4931     if (this.footer && this.store) {
4932         this.footer.dataSource = this.ds;
4933         this.footer = Roo.factory(this.footer);
4934     }
4935     
4936     /** @private */
4937     this.addEvents({
4938         /**
4939          * @event cellclick
4940          * Fires when a cell is clicked
4941          * @param {Roo.bootstrap.Table} this
4942          * @param {Roo.Element} el
4943          * @param {Number} rowIndex
4944          * @param {Number} columnIndex
4945          * @param {Roo.EventObject} e
4946          */
4947         "cellclick" : true,
4948         /**
4949          * @event celldblclick
4950          * Fires when a cell is double clicked
4951          * @param {Roo.bootstrap.Table} this
4952          * @param {Roo.Element} el
4953          * @param {Number} rowIndex
4954          * @param {Number} columnIndex
4955          * @param {Roo.EventObject} e
4956          */
4957         "celldblclick" : true,
4958         /**
4959          * @event rowclick
4960          * Fires when a row is clicked
4961          * @param {Roo.bootstrap.Table} this
4962          * @param {Roo.Element} el
4963          * @param {Number} rowIndex
4964          * @param {Roo.EventObject} e
4965          */
4966         "rowclick" : true,
4967         /**
4968          * @event rowdblclick
4969          * Fires when a row is double clicked
4970          * @param {Roo.bootstrap.Table} this
4971          * @param {Roo.Element} el
4972          * @param {Number} rowIndex
4973          * @param {Roo.EventObject} e
4974          */
4975         "rowdblclick" : true,
4976         /**
4977          * @event mouseover
4978          * Fires when a mouseover occur
4979          * @param {Roo.bootstrap.Table} this
4980          * @param {Roo.Element} el
4981          * @param {Number} rowIndex
4982          * @param {Number} columnIndex
4983          * @param {Roo.EventObject} e
4984          */
4985         "mouseover" : true,
4986         /**
4987          * @event mouseout
4988          * Fires when a mouseout occur
4989          * @param {Roo.bootstrap.Table} this
4990          * @param {Roo.Element} el
4991          * @param {Number} rowIndex
4992          * @param {Number} columnIndex
4993          * @param {Roo.EventObject} e
4994          */
4995         "mouseout" : true,
4996         /**
4997          * @event rowclass
4998          * Fires when a row is rendered, so you can change add a style to it.
4999          * @param {Roo.bootstrap.Table} this
5000          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5001          */
5002         'rowclass' : true
5003         
5004     });
5005 };
5006
5007 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5008     
5009     cls: false,
5010     align: false,
5011     bgcolor: false,
5012     border: false,
5013     cellpadding: false,
5014     cellspacing: false,
5015     frame: false,
5016     rules: false,
5017     sortable: false,
5018     summary: false,
5019     width: false,
5020     striped : false,
5021     bordered: false,
5022     hover:  false,
5023     condensed : false,
5024     responsive : false,
5025     sm : false,
5026     cm : false,
5027     store : false,
5028     loadMask : false,
5029     tfoot : true,
5030     thead : true,
5031     RowSelection : false,
5032     CellSelection : false,
5033     layout : false,
5034     
5035     // Roo.Element - the tbody
5036     mainBody: false, 
5037     
5038     getAutoCreate : function(){
5039         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5040         
5041         cfg = {
5042             tag: 'table',
5043             cls : 'table',
5044             cn : []
5045         }
5046             
5047         if (this.striped) {
5048             cfg.cls += ' table-striped';
5049         }
5050         
5051         if (this.hover) {
5052             cfg.cls += ' table-hover';
5053         }
5054         if (this.bordered) {
5055             cfg.cls += ' table-bordered';
5056         }
5057         if (this.condensed) {
5058             cfg.cls += ' table-condensed';
5059         }
5060         if (this.responsive) {
5061             cfg.cls += ' table-responsive';
5062         }
5063         
5064         if (this.cls) {
5065             cfg.cls+=  ' ' +this.cls;
5066         }
5067         
5068         // this lot should be simplifed...
5069         
5070         if (this.align) {
5071             cfg.align=this.align;
5072         }
5073         if (this.bgcolor) {
5074             cfg.bgcolor=this.bgcolor;
5075         }
5076         if (this.border) {
5077             cfg.border=this.border;
5078         }
5079         if (this.cellpadding) {
5080             cfg.cellpadding=this.cellpadding;
5081         }
5082         if (this.cellspacing) {
5083             cfg.cellspacing=this.cellspacing;
5084         }
5085         if (this.frame) {
5086             cfg.frame=this.frame;
5087         }
5088         if (this.rules) {
5089             cfg.rules=this.rules;
5090         }
5091         if (this.sortable) {
5092             cfg.sortable=this.sortable;
5093         }
5094         if (this.summary) {
5095             cfg.summary=this.summary;
5096         }
5097         if (this.width) {
5098             cfg.width=this.width;
5099         }
5100         if (this.layout) {
5101             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5102         }
5103         
5104         if(this.store || this.cm){
5105             if(this.thead){
5106                 cfg.cn.push(this.renderHeader());
5107             }
5108             
5109             cfg.cn.push(this.renderBody());
5110             
5111             if(this.tfoot){
5112                 cfg.cn.push(this.renderFooter());
5113             }
5114             
5115             cfg.cls+=  ' TableGrid';
5116         }
5117         
5118         return { cn : [ cfg ] };
5119     },
5120     
5121     initEvents : function()
5122     {   
5123         if(!this.store || !this.cm){
5124             return;
5125         }
5126         
5127         //Roo.log('initEvents with ds!!!!');
5128         
5129         this.mainBody = this.el.select('tbody', true).first();
5130         
5131         
5132         var _this = this;
5133         
5134         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5135             e.on('click', _this.sort, _this);
5136         });
5137         
5138         this.el.on("click", this.onClick, this);
5139         this.el.on("dblclick", this.onDblClick, this);
5140         
5141         this.parent().el.setStyle('position', 'relative');
5142         if (this.footer) {
5143             this.footer.parentId = this.id;
5144             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5145         }
5146         
5147         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5148         
5149         this.store.on('load', this.onLoad, this);
5150         this.store.on('beforeload', this.onBeforeLoad, this);
5151         this.store.on('update', this.onUpdate, this);
5152         
5153     },
5154     
5155     onMouseover : function(e, el)
5156     {
5157         var cell = Roo.get(el);
5158         
5159         if(!cell){
5160             return;
5161         }
5162         
5163         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5164             cell = cell.findParent('td', false, true);
5165         }
5166         
5167         var row = cell.findParent('tr', false, true);
5168         var cellIndex = cell.dom.cellIndex;
5169         var rowIndex = row.dom.rowIndex - 1; // start from 0
5170         
5171         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5172         
5173     },
5174     
5175     onMouseout : function(e, el)
5176     {
5177         var cell = Roo.get(el);
5178         
5179         if(!cell){
5180             return;
5181         }
5182         
5183         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5184             cell = cell.findParent('td', false, true);
5185         }
5186         
5187         var row = cell.findParent('tr', false, true);
5188         var cellIndex = cell.dom.cellIndex;
5189         var rowIndex = row.dom.rowIndex - 1; // start from 0
5190         
5191         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5192         
5193     },
5194     
5195     onClick : function(e, el)
5196     {
5197         var cell = Roo.get(el);
5198         
5199         if(!cell || (!this.CellSelection && !this.RowSelection)){
5200             return;
5201         }
5202         
5203         
5204         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5205             cell = cell.findParent('td', false, true);
5206         }
5207         
5208         var row = cell.findParent('tr', false, true);
5209         var cellIndex = cell.dom.cellIndex;
5210         var rowIndex = row.dom.rowIndex - 1;
5211         
5212         if(this.CellSelection){
5213             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5214         }
5215         
5216         if(this.RowSelection){
5217             this.fireEvent('rowclick', this, row, rowIndex, e);
5218         }
5219         
5220         
5221     },
5222     
5223     onDblClick : function(e,el)
5224     {
5225         var cell = Roo.get(el);
5226         
5227         if(!cell || (!this.CellSelection && !this.RowSelection)){
5228             return;
5229         }
5230         
5231         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5232             cell = cell.findParent('td', false, true);
5233         }
5234         
5235         var row = cell.findParent('tr', false, true);
5236         var cellIndex = cell.dom.cellIndex;
5237         var rowIndex = row.dom.rowIndex - 1;
5238         
5239         if(this.CellSelection){
5240             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5241         }
5242         
5243         if(this.RowSelection){
5244             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5245         }
5246     },
5247     
5248     sort : function(e,el)
5249     {
5250         var col = Roo.get(el)
5251         
5252         if(!col.hasClass('sortable')){
5253             return;
5254         }
5255         
5256         var sort = col.attr('sort');
5257         var dir = 'ASC';
5258         
5259         if(col.hasClass('glyphicon-arrow-up')){
5260             dir = 'DESC';
5261         }
5262         
5263         this.store.sortInfo = {field : sort, direction : dir};
5264         
5265         if (this.footer) {
5266             Roo.log("calling footer first");
5267             this.footer.onClick('first');
5268         } else {
5269         
5270             this.store.load({ params : { start : 0 } });
5271         }
5272     },
5273     
5274     renderHeader : function()
5275     {
5276         var header = {
5277             tag: 'thead',
5278             cn : []
5279         };
5280         
5281         var cm = this.cm;
5282         
5283         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5284             
5285             var config = cm.config[i];
5286                     
5287             var c = {
5288                 tag: 'th',
5289                 style : '',
5290                 html: cm.getColumnHeader(i)
5291             };
5292             
5293             if(typeof(config.hidden) != 'undefined' && config.hidden){
5294                 c.style += ' display:none;';
5295             }
5296             
5297             if(typeof(config.dataIndex) != 'undefined'){
5298                 c.sort = config.dataIndex;
5299             }
5300             
5301             if(typeof(config.sortable) != 'undefined' && config.sortable){
5302                 c.cls = 'sortable';
5303             }
5304             
5305             if(typeof(config.align) != 'undefined' && config.align.length){
5306                 c.style += ' text-align:' + config.align + ';';
5307             }
5308             
5309             if(typeof(config.width) != 'undefined'){
5310                 c.style += ' width:' + config.width + 'px;';
5311             }
5312             
5313             header.cn.push(c)
5314         }
5315         
5316         return header;
5317     },
5318     
5319     renderBody : function()
5320     {
5321         var body = {
5322             tag: 'tbody',
5323             cn : [
5324                 {
5325                     tag: 'tr',
5326                     cn : [
5327                         {
5328                             tag : 'td',
5329                             colspan :  this.cm.getColumnCount()
5330                         }
5331                     ]
5332                 }
5333             ]
5334         };
5335         
5336         return body;
5337     },
5338     
5339     renderFooter : function()
5340     {
5341         var footer = {
5342             tag: 'tfoot',
5343             cn : [
5344                 {
5345                     tag: 'tr',
5346                     cn : [
5347                         {
5348                             tag : 'td',
5349                             colspan :  this.cm.getColumnCount()
5350                         }
5351                     ]
5352                 }
5353             ]
5354         };
5355         
5356         return footer;
5357     },
5358     
5359     
5360     
5361     onLoad : function()
5362     {
5363         Roo.log('ds onload');
5364         this.clear();
5365         
5366         var _this = this;
5367         var cm = this.cm;
5368         var ds = this.store;
5369         
5370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5371             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5372             
5373             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5374                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5375             }
5376             
5377             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5378                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5379             }
5380         });
5381         
5382         var tbody =  this.mainBody;
5383               
5384         if(ds.getCount() > 0){
5385             ds.data.each(function(d,rowIndex){
5386                 var row =  this.renderRow(cm, ds, rowIndex);
5387                 
5388                 tbody.createChild(row);
5389                 
5390                 var _this = this;
5391                 
5392                 if(row.cellObjects.length){
5393                     Roo.each(row.cellObjects, function(r){
5394                         _this.renderCellObject(r);
5395                     })
5396                 }
5397                 
5398             }, this);
5399         }
5400         
5401         Roo.each(this.el.select('tbody td', true).elements, function(e){
5402             e.on('mouseover', _this.onMouseover, _this);
5403         });
5404         
5405         Roo.each(this.el.select('tbody td', true).elements, function(e){
5406             e.on('mouseout', _this.onMouseout, _this);
5407         });
5408
5409         //if(this.loadMask){
5410         //    this.maskEl.hide();
5411         //}
5412     },
5413     
5414     
5415     onUpdate : function(ds,record)
5416     {
5417         this.refreshRow(record);
5418     },
5419     onRemove : function(ds, record, index, isUpdate){
5420         if(isUpdate !== true){
5421             this.fireEvent("beforerowremoved", this, index, record);
5422         }
5423         var bt = this.mainBody.dom;
5424         if(bt.rows[index]){
5425             bt.removeChild(bt.rows[index]);
5426         }
5427         
5428         if(isUpdate !== true){
5429             //this.stripeRows(index);
5430             //this.syncRowHeights(index, index);
5431             //this.layout();
5432             this.fireEvent("rowremoved", this, index, record);
5433         }
5434     },
5435     
5436     
5437     refreshRow : function(record){
5438         var ds = this.store, index;
5439         if(typeof record == 'number'){
5440             index = record;
5441             record = ds.getAt(index);
5442         }else{
5443             index = ds.indexOf(record);
5444         }
5445         this.insertRow(ds, index, true);
5446         this.onRemove(ds, record, index+1, true);
5447         //this.syncRowHeights(index, index);
5448         //this.layout();
5449         this.fireEvent("rowupdated", this, index, record);
5450     },
5451     
5452     insertRow : function(dm, rowIndex, isUpdate){
5453         
5454         if(!isUpdate){
5455             this.fireEvent("beforerowsinserted", this, rowIndex);
5456         }
5457             //var s = this.getScrollState();
5458         var row = this.renderRow(this.cm, this.store, rowIndex);
5459         // insert before rowIndex..
5460         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5461         
5462         var _this = this;
5463                 
5464         if(row.cellObjects.length){
5465             Roo.each(row.cellObjects, function(r){
5466                 _this.renderCellObject(r);
5467             })
5468         }
5469             
5470         if(!isUpdate){
5471             this.fireEvent("rowsinserted", this, rowIndex);
5472             //this.syncRowHeights(firstRow, lastRow);
5473             //this.stripeRows(firstRow);
5474             //this.layout();
5475         }
5476         
5477     },
5478     
5479     
5480     getRowDom : function(rowIndex)
5481     {
5482         // not sure if I need to check this.. but let's do it anyway..
5483         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5484                 this.mainBody.dom.rows[rowIndex] : false
5485     },
5486     // returns the object tree for a tr..
5487   
5488     
5489     renderRow : function(cm, ds, rowIndex) {
5490         
5491         var d = ds.getAt(rowIndex);
5492         
5493         var row = {
5494             tag : 'tr',
5495             cn : []
5496         };
5497             
5498         var cellObjects = [];
5499         
5500         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5501             var config = cm.config[i];
5502             
5503             var renderer = cm.getRenderer(i);
5504             var value = '';
5505             var id = false;
5506             
5507             if(typeof(renderer) !== 'undefined'){
5508                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5509             }
5510             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5511             // and are rendered into the cells after the row is rendered - using the id for the element.
5512             
5513             if(typeof(value) === 'object'){
5514                 id = Roo.id();
5515                 cellObjects.push({
5516                     container : id,
5517                     cfg : value 
5518                 })
5519             }
5520             
5521             var rowcfg = {
5522                 record: d,
5523                 rowIndex : rowIndex,
5524                 colIndex : i,
5525                 rowClass : ''
5526             }
5527
5528             this.fireEvent('rowclass', this, rowcfg);
5529             
5530             var td = {
5531                 tag: 'td',
5532                 cls : rowcfg.rowClass,
5533                 style: '',
5534                 html: (typeof(value) === 'object') ? '' : value
5535             };
5536             
5537             if (id) {
5538                 td.id = id;
5539             }
5540             
5541             if(typeof(config.hidden) != 'undefined' && config.hidden){
5542                 td.style += ' display:none;';
5543             }
5544             
5545             if(typeof(config.align) != 'undefined' && config.align.length){
5546                 td.style += ' text-align:' + config.align + ';';
5547             }
5548             
5549             if(typeof(config.width) != 'undefined'){
5550                 td.style += ' width:' +  config.width + 'px;';
5551             }
5552              
5553             row.cn.push(td);
5554            
5555         }
5556         
5557         row.cellObjects = cellObjects;
5558         
5559         return row;
5560           
5561     },
5562     
5563     
5564     
5565     onBeforeLoad : function()
5566     {
5567         //Roo.log('ds onBeforeLoad');
5568         
5569         //this.clear();
5570         
5571         //if(this.loadMask){
5572         //    this.maskEl.show();
5573         //}
5574     },
5575     
5576     clear : function()
5577     {
5578         this.el.select('tbody', true).first().dom.innerHTML = '';
5579     },
5580     
5581     getSelectionModel : function(){
5582         if(!this.selModel){
5583             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5584         }
5585         return this.selModel;
5586     },
5587     /*
5588      * Render the Roo.bootstrap object from renderder
5589      */
5590     renderCellObject : function(r)
5591     {
5592         var _this = this;
5593         
5594         var t = r.cfg.render(r.container);
5595         
5596         if(r.cfg.cn){
5597             Roo.each(r.cfg.cn, function(c){
5598                 var child = {
5599                     container: t.getChildContainer(),
5600                     cfg: c
5601                 }
5602                 _this.renderCellObject(child);
5603             })
5604         }
5605     }
5606    
5607 });
5608
5609  
5610
5611  /*
5612  * - LGPL
5613  *
5614  * table cell
5615  * 
5616  */
5617
5618 /**
5619  * @class Roo.bootstrap.TableCell
5620  * @extends Roo.bootstrap.Component
5621  * Bootstrap TableCell class
5622  * @cfg {String} html cell contain text
5623  * @cfg {String} cls cell class
5624  * @cfg {String} tag cell tag (td|th) default td
5625  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5626  * @cfg {String} align Aligns the content in a cell
5627  * @cfg {String} axis Categorizes cells
5628  * @cfg {String} bgcolor Specifies the background color of a cell
5629  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5630  * @cfg {Number} colspan Specifies the number of columns a cell should span
5631  * @cfg {String} headers Specifies one or more header cells a cell is related to
5632  * @cfg {Number} height Sets the height of a cell
5633  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5634  * @cfg {Number} rowspan Sets the number of rows a cell should span
5635  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5636  * @cfg {String} valign Vertical aligns the content in a cell
5637  * @cfg {Number} width Specifies the width of a cell
5638  * 
5639  * @constructor
5640  * Create a new TableCell
5641  * @param {Object} config The config object
5642  */
5643
5644 Roo.bootstrap.TableCell = function(config){
5645     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5646 };
5647
5648 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5649     
5650     html: false,
5651     cls: false,
5652     tag: false,
5653     abbr: false,
5654     align: false,
5655     axis: false,
5656     bgcolor: false,
5657     charoff: false,
5658     colspan: false,
5659     headers: false,
5660     height: false,
5661     nowrap: false,
5662     rowspan: false,
5663     scope: false,
5664     valign: false,
5665     width: false,
5666     
5667     
5668     getAutoCreate : function(){
5669         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5670         
5671         cfg = {
5672             tag: 'td'
5673         }
5674         
5675         if(this.tag){
5676             cfg.tag = this.tag;
5677         }
5678         
5679         if (this.html) {
5680             cfg.html=this.html
5681         }
5682         if (this.cls) {
5683             cfg.cls=this.cls
5684         }
5685         if (this.abbr) {
5686             cfg.abbr=this.abbr
5687         }
5688         if (this.align) {
5689             cfg.align=this.align
5690         }
5691         if (this.axis) {
5692             cfg.axis=this.axis
5693         }
5694         if (this.bgcolor) {
5695             cfg.bgcolor=this.bgcolor
5696         }
5697         if (this.charoff) {
5698             cfg.charoff=this.charoff
5699         }
5700         if (this.colspan) {
5701             cfg.colspan=this.colspan
5702         }
5703         if (this.headers) {
5704             cfg.headers=this.headers
5705         }
5706         if (this.height) {
5707             cfg.height=this.height
5708         }
5709         if (this.nowrap) {
5710             cfg.nowrap=this.nowrap
5711         }
5712         if (this.rowspan) {
5713             cfg.rowspan=this.rowspan
5714         }
5715         if (this.scope) {
5716             cfg.scope=this.scope
5717         }
5718         if (this.valign) {
5719             cfg.valign=this.valign
5720         }
5721         if (this.width) {
5722             cfg.width=this.width
5723         }
5724         
5725         
5726         return cfg;
5727     }
5728    
5729 });
5730
5731  
5732
5733  /*
5734  * - LGPL
5735  *
5736  * table row
5737  * 
5738  */
5739
5740 /**
5741  * @class Roo.bootstrap.TableRow
5742  * @extends Roo.bootstrap.Component
5743  * Bootstrap TableRow class
5744  * @cfg {String} cls row class
5745  * @cfg {String} align Aligns the content in a table row
5746  * @cfg {String} bgcolor Specifies a background color for a table row
5747  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5748  * @cfg {String} valign Vertical aligns the content in a table row
5749  * 
5750  * @constructor
5751  * Create a new TableRow
5752  * @param {Object} config The config object
5753  */
5754
5755 Roo.bootstrap.TableRow = function(config){
5756     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5757 };
5758
5759 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5760     
5761     cls: false,
5762     align: false,
5763     bgcolor: false,
5764     charoff: false,
5765     valign: false,
5766     
5767     getAutoCreate : function(){
5768         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5769         
5770         cfg = {
5771             tag: 'tr'
5772         }
5773             
5774         if(this.cls){
5775             cfg.cls = this.cls;
5776         }
5777         if(this.align){
5778             cfg.align = this.align;
5779         }
5780         if(this.bgcolor){
5781             cfg.bgcolor = this.bgcolor;
5782         }
5783         if(this.charoff){
5784             cfg.charoff = this.charoff;
5785         }
5786         if(this.valign){
5787             cfg.valign = this.valign;
5788         }
5789         
5790         return cfg;
5791     }
5792    
5793 });
5794
5795  
5796
5797  /*
5798  * - LGPL
5799  *
5800  * table body
5801  * 
5802  */
5803
5804 /**
5805  * @class Roo.bootstrap.TableBody
5806  * @extends Roo.bootstrap.Component
5807  * Bootstrap TableBody class
5808  * @cfg {String} cls element class
5809  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5810  * @cfg {String} align Aligns the content inside the element
5811  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5812  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5813  * 
5814  * @constructor
5815  * Create a new TableBody
5816  * @param {Object} config The config object
5817  */
5818
5819 Roo.bootstrap.TableBody = function(config){
5820     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5821 };
5822
5823 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5824     
5825     cls: false,
5826     tag: false,
5827     align: false,
5828     charoff: false,
5829     valign: false,
5830     
5831     getAutoCreate : function(){
5832         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5833         
5834         cfg = {
5835             tag: 'tbody'
5836         }
5837             
5838         if (this.cls) {
5839             cfg.cls=this.cls
5840         }
5841         if(this.tag){
5842             cfg.tag = this.tag;
5843         }
5844         
5845         if(this.align){
5846             cfg.align = this.align;
5847         }
5848         if(this.charoff){
5849             cfg.charoff = this.charoff;
5850         }
5851         if(this.valign){
5852             cfg.valign = this.valign;
5853         }
5854         
5855         return cfg;
5856     }
5857     
5858     
5859 //    initEvents : function()
5860 //    {
5861 //        
5862 //        if(!this.store){
5863 //            return;
5864 //        }
5865 //        
5866 //        this.store = Roo.factory(this.store, Roo.data);
5867 //        this.store.on('load', this.onLoad, this);
5868 //        
5869 //        this.store.load();
5870 //        
5871 //    },
5872 //    
5873 //    onLoad: function () 
5874 //    {   
5875 //        this.fireEvent('load', this);
5876 //    }
5877 //    
5878 //   
5879 });
5880
5881  
5882
5883  /*
5884  * Based on:
5885  * Ext JS Library 1.1.1
5886  * Copyright(c) 2006-2007, Ext JS, LLC.
5887  *
5888  * Originally Released Under LGPL - original licence link has changed is not relivant.
5889  *
5890  * Fork - LGPL
5891  * <script type="text/javascript">
5892  */
5893
5894 // as we use this in bootstrap.
5895 Roo.namespace('Roo.form');
5896  /**
5897  * @class Roo.form.Action
5898  * Internal Class used to handle form actions
5899  * @constructor
5900  * @param {Roo.form.BasicForm} el The form element or its id
5901  * @param {Object} config Configuration options
5902  */
5903
5904  
5905  
5906 // define the action interface
5907 Roo.form.Action = function(form, options){
5908     this.form = form;
5909     this.options = options || {};
5910 };
5911 /**
5912  * Client Validation Failed
5913  * @const 
5914  */
5915 Roo.form.Action.CLIENT_INVALID = 'client';
5916 /**
5917  * Server Validation Failed
5918  * @const 
5919  */
5920 Roo.form.Action.SERVER_INVALID = 'server';
5921  /**
5922  * Connect to Server Failed
5923  * @const 
5924  */
5925 Roo.form.Action.CONNECT_FAILURE = 'connect';
5926 /**
5927  * Reading Data from Server Failed
5928  * @const 
5929  */
5930 Roo.form.Action.LOAD_FAILURE = 'load';
5931
5932 Roo.form.Action.prototype = {
5933     type : 'default',
5934     failureType : undefined,
5935     response : undefined,
5936     result : undefined,
5937
5938     // interface method
5939     run : function(options){
5940
5941     },
5942
5943     // interface method
5944     success : function(response){
5945
5946     },
5947
5948     // interface method
5949     handleResponse : function(response){
5950
5951     },
5952
5953     // default connection failure
5954     failure : function(response){
5955         
5956         this.response = response;
5957         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5958         this.form.afterAction(this, false);
5959     },
5960
5961     processResponse : function(response){
5962         this.response = response;
5963         if(!response.responseText){
5964             return true;
5965         }
5966         this.result = this.handleResponse(response);
5967         return this.result;
5968     },
5969
5970     // utility functions used internally
5971     getUrl : function(appendParams){
5972         var url = this.options.url || this.form.url || this.form.el.dom.action;
5973         if(appendParams){
5974             var p = this.getParams();
5975             if(p){
5976                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5977             }
5978         }
5979         return url;
5980     },
5981
5982     getMethod : function(){
5983         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5984     },
5985
5986     getParams : function(){
5987         var bp = this.form.baseParams;
5988         var p = this.options.params;
5989         if(p){
5990             if(typeof p == "object"){
5991                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5992             }else if(typeof p == 'string' && bp){
5993                 p += '&' + Roo.urlEncode(bp);
5994             }
5995         }else if(bp){
5996             p = Roo.urlEncode(bp);
5997         }
5998         return p;
5999     },
6000
6001     createCallback : function(){
6002         return {
6003             success: this.success,
6004             failure: this.failure,
6005             scope: this,
6006             timeout: (this.form.timeout*1000),
6007             upload: this.form.fileUpload ? this.success : undefined
6008         };
6009     }
6010 };
6011
6012 Roo.form.Action.Submit = function(form, options){
6013     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6014 };
6015
6016 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6017     type : 'submit',
6018
6019     haveProgress : false,
6020     uploadComplete : false,
6021     
6022     // uploadProgress indicator.
6023     uploadProgress : function()
6024     {
6025         if (!this.form.progressUrl) {
6026             return;
6027         }
6028         
6029         if (!this.haveProgress) {
6030             Roo.MessageBox.progress("Uploading", "Uploading");
6031         }
6032         if (this.uploadComplete) {
6033            Roo.MessageBox.hide();
6034            return;
6035         }
6036         
6037         this.haveProgress = true;
6038    
6039         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6040         
6041         var c = new Roo.data.Connection();
6042         c.request({
6043             url : this.form.progressUrl,
6044             params: {
6045                 id : uid
6046             },
6047             method: 'GET',
6048             success : function(req){
6049                //console.log(data);
6050                 var rdata = false;
6051                 var edata;
6052                 try  {
6053                    rdata = Roo.decode(req.responseText)
6054                 } catch (e) {
6055                     Roo.log("Invalid data from server..");
6056                     Roo.log(edata);
6057                     return;
6058                 }
6059                 if (!rdata || !rdata.success) {
6060                     Roo.log(rdata);
6061                     Roo.MessageBox.alert(Roo.encode(rdata));
6062                     return;
6063                 }
6064                 var data = rdata.data;
6065                 
6066                 if (this.uploadComplete) {
6067                    Roo.MessageBox.hide();
6068                    return;
6069                 }
6070                    
6071                 if (data){
6072                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6073                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6074                     );
6075                 }
6076                 this.uploadProgress.defer(2000,this);
6077             },
6078        
6079             failure: function(data) {
6080                 Roo.log('progress url failed ');
6081                 Roo.log(data);
6082             },
6083             scope : this
6084         });
6085            
6086     },
6087     
6088     
6089     run : function()
6090     {
6091         // run get Values on the form, so it syncs any secondary forms.
6092         this.form.getValues();
6093         
6094         var o = this.options;
6095         var method = this.getMethod();
6096         var isPost = method == 'POST';
6097         if(o.clientValidation === false || this.form.isValid()){
6098             
6099             if (this.form.progressUrl) {
6100                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6101                     (new Date() * 1) + '' + Math.random());
6102                     
6103             } 
6104             
6105             
6106             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6107                 form:this.form.el.dom,
6108                 url:this.getUrl(!isPost),
6109                 method: method,
6110                 params:isPost ? this.getParams() : null,
6111                 isUpload: this.form.fileUpload
6112             }));
6113             
6114             this.uploadProgress();
6115
6116         }else if (o.clientValidation !== false){ // client validation failed
6117             this.failureType = Roo.form.Action.CLIENT_INVALID;
6118             this.form.afterAction(this, false);
6119         }
6120     },
6121
6122     success : function(response)
6123     {
6124         this.uploadComplete= true;
6125         if (this.haveProgress) {
6126             Roo.MessageBox.hide();
6127         }
6128         
6129         
6130         var result = this.processResponse(response);
6131         if(result === true || result.success){
6132             this.form.afterAction(this, true);
6133             return;
6134         }
6135         if(result.errors){
6136             this.form.markInvalid(result.errors);
6137             this.failureType = Roo.form.Action.SERVER_INVALID;
6138         }
6139         this.form.afterAction(this, false);
6140     },
6141     failure : function(response)
6142     {
6143         this.uploadComplete= true;
6144         if (this.haveProgress) {
6145             Roo.MessageBox.hide();
6146         }
6147         
6148         this.response = response;
6149         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6150         this.form.afterAction(this, false);
6151     },
6152     
6153     handleResponse : function(response){
6154         if(this.form.errorReader){
6155             var rs = this.form.errorReader.read(response);
6156             var errors = [];
6157             if(rs.records){
6158                 for(var i = 0, len = rs.records.length; i < len; i++) {
6159                     var r = rs.records[i];
6160                     errors[i] = r.data;
6161                 }
6162             }
6163             if(errors.length < 1){
6164                 errors = null;
6165             }
6166             return {
6167                 success : rs.success,
6168                 errors : errors
6169             };
6170         }
6171         var ret = false;
6172         try {
6173             ret = Roo.decode(response.responseText);
6174         } catch (e) {
6175             ret = {
6176                 success: false,
6177                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6178                 errors : []
6179             };
6180         }
6181         return ret;
6182         
6183     }
6184 });
6185
6186
6187 Roo.form.Action.Load = function(form, options){
6188     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6189     this.reader = this.form.reader;
6190 };
6191
6192 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6193     type : 'load',
6194
6195     run : function(){
6196         
6197         Roo.Ajax.request(Roo.apply(
6198                 this.createCallback(), {
6199                     method:this.getMethod(),
6200                     url:this.getUrl(false),
6201                     params:this.getParams()
6202         }));
6203     },
6204
6205     success : function(response){
6206         
6207         var result = this.processResponse(response);
6208         if(result === true || !result.success || !result.data){
6209             this.failureType = Roo.form.Action.LOAD_FAILURE;
6210             this.form.afterAction(this, false);
6211             return;
6212         }
6213         this.form.clearInvalid();
6214         this.form.setValues(result.data);
6215         this.form.afterAction(this, true);
6216     },
6217
6218     handleResponse : function(response){
6219         if(this.form.reader){
6220             var rs = this.form.reader.read(response);
6221             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6222             return {
6223                 success : rs.success,
6224                 data : data
6225             };
6226         }
6227         return Roo.decode(response.responseText);
6228     }
6229 });
6230
6231 Roo.form.Action.ACTION_TYPES = {
6232     'load' : Roo.form.Action.Load,
6233     'submit' : Roo.form.Action.Submit
6234 };/*
6235  * - LGPL
6236  *
6237  * form
6238  * 
6239  */
6240
6241 /**
6242  * @class Roo.bootstrap.Form
6243  * @extends Roo.bootstrap.Component
6244  * Bootstrap Form class
6245  * @cfg {String} method  GET | POST (default POST)
6246  * @cfg {String} labelAlign top | left (default top)
6247  * @cfg {String} align left  | right - for navbars
6248  * @cfg {Boolean} loadMask load mask when submit (default true)
6249
6250  * 
6251  * @constructor
6252  * Create a new Form
6253  * @param {Object} config The config object
6254  */
6255
6256
6257 Roo.bootstrap.Form = function(config){
6258     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6259     this.addEvents({
6260         /**
6261          * @event clientvalidation
6262          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6263          * @param {Form} this
6264          * @param {Boolean} valid true if the form has passed client-side validation
6265          */
6266         clientvalidation: true,
6267         /**
6268          * @event beforeaction
6269          * Fires before any action is performed. Return false to cancel the action.
6270          * @param {Form} this
6271          * @param {Action} action The action to be performed
6272          */
6273         beforeaction: true,
6274         /**
6275          * @event actionfailed
6276          * Fires when an action fails.
6277          * @param {Form} this
6278          * @param {Action} action The action that failed
6279          */
6280         actionfailed : true,
6281         /**
6282          * @event actioncomplete
6283          * Fires when an action is completed.
6284          * @param {Form} this
6285          * @param {Action} action The action that completed
6286          */
6287         actioncomplete : true
6288     });
6289     
6290 };
6291
6292 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6293       
6294      /**
6295      * @cfg {String} method
6296      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6297      */
6298     method : 'POST',
6299     /**
6300      * @cfg {String} url
6301      * The URL to use for form actions if one isn't supplied in the action options.
6302      */
6303     /**
6304      * @cfg {Boolean} fileUpload
6305      * Set to true if this form is a file upload.
6306      */
6307      
6308     /**
6309      * @cfg {Object} baseParams
6310      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6311      */
6312       
6313     /**
6314      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6315      */
6316     timeout: 30,
6317     /**
6318      * @cfg {Sting} align (left|right) for navbar forms
6319      */
6320     align : 'left',
6321
6322     // private
6323     activeAction : null,
6324  
6325     /**
6326      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6327      * element by passing it or its id or mask the form itself by passing in true.
6328      * @type Mixed
6329      */
6330     waitMsgTarget : false,
6331     
6332     loadMask : true,
6333     
6334     getAutoCreate : function(){
6335         
6336         var cfg = {
6337             tag: 'form',
6338             method : this.method || 'POST',
6339             id : this.id || Roo.id(),
6340             cls : ''
6341         }
6342         if (this.parent().xtype.match(/^Nav/)) {
6343             cfg.cls = 'navbar-form navbar-' + this.align;
6344             
6345         }
6346         
6347         if (this.labelAlign == 'left' ) {
6348             cfg.cls += ' form-horizontal';
6349         }
6350         
6351         
6352         return cfg;
6353     },
6354     initEvents : function()
6355     {
6356         this.el.on('submit', this.onSubmit, this);
6357         // this was added as random key presses on the form where triggering form submit.
6358         this.el.on('keypress', function(e) {
6359             if (e.getCharCode() != 13) {
6360                 return true;
6361             }
6362             // we might need to allow it for textareas.. and some other items.
6363             // check e.getTarget().
6364             
6365             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6366                 return true;
6367             }
6368         
6369             Roo.log("keypress blocked");
6370             
6371             e.preventDefault();
6372             return false;
6373         });
6374         
6375     },
6376     // private
6377     onSubmit : function(e){
6378         e.stopEvent();
6379     },
6380     
6381      /**
6382      * Returns true if client-side validation on the form is successful.
6383      * @return Boolean
6384      */
6385     isValid : function(){
6386         var items = this.getItems();
6387         var valid = true;
6388         items.each(function(f){
6389            if(!f.validate()){
6390                valid = false;
6391                
6392            }
6393         });
6394         return valid;
6395     },
6396     /**
6397      * Returns true if any fields in this form have changed since their original load.
6398      * @return Boolean
6399      */
6400     isDirty : function(){
6401         var dirty = false;
6402         var items = this.getItems();
6403         items.each(function(f){
6404            if(f.isDirty()){
6405                dirty = true;
6406                return false;
6407            }
6408            return true;
6409         });
6410         return dirty;
6411     },
6412      /**
6413      * Performs a predefined action (submit or load) or custom actions you define on this form.
6414      * @param {String} actionName The name of the action type
6415      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6416      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6417      * accept other config options):
6418      * <pre>
6419 Property          Type             Description
6420 ----------------  ---------------  ----------------------------------------------------------------------------------
6421 url               String           The url for the action (defaults to the form's url)
6422 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6423 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6424 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6425                                    validate the form on the client (defaults to false)
6426      * </pre>
6427      * @return {BasicForm} this
6428      */
6429     doAction : function(action, options){
6430         if(typeof action == 'string'){
6431             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6432         }
6433         if(this.fireEvent('beforeaction', this, action) !== false){
6434             this.beforeAction(action);
6435             action.run.defer(100, action);
6436         }
6437         return this;
6438     },
6439     
6440     // private
6441     beforeAction : function(action){
6442         var o = action.options;
6443         
6444         if(this.loadMask){
6445             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6446         }
6447         // not really supported yet.. ??
6448         
6449         //if(this.waitMsgTarget === true){
6450         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6451         //}else if(this.waitMsgTarget){
6452         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6453         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6454         //}else {
6455         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6456        // }
6457          
6458     },
6459
6460     // private
6461     afterAction : function(action, success){
6462         this.activeAction = null;
6463         var o = action.options;
6464         
6465         //if(this.waitMsgTarget === true){
6466             this.el.unmask();
6467         //}else if(this.waitMsgTarget){
6468         //    this.waitMsgTarget.unmask();
6469         //}else{
6470         //    Roo.MessageBox.updateProgress(1);
6471         //    Roo.MessageBox.hide();
6472        // }
6473         // 
6474         if(success){
6475             if(o.reset){
6476                 this.reset();
6477             }
6478             Roo.callback(o.success, o.scope, [this, action]);
6479             this.fireEvent('actioncomplete', this, action);
6480             
6481         }else{
6482             
6483             // failure condition..
6484             // we have a scenario where updates need confirming.
6485             // eg. if a locking scenario exists..
6486             // we look for { errors : { needs_confirm : true }} in the response.
6487             if (
6488                 (typeof(action.result) != 'undefined')  &&
6489                 (typeof(action.result.errors) != 'undefined')  &&
6490                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6491            ){
6492                 var _t = this;
6493                 Roo.log("not supported yet");
6494                  /*
6495                 
6496                 Roo.MessageBox.confirm(
6497                     "Change requires confirmation",
6498                     action.result.errorMsg,
6499                     function(r) {
6500                         if (r != 'yes') {
6501                             return;
6502                         }
6503                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6504                     }
6505                     
6506                 );
6507                 */
6508                 
6509                 
6510                 return;
6511             }
6512             
6513             Roo.callback(o.failure, o.scope, [this, action]);
6514             // show an error message if no failed handler is set..
6515             if (!this.hasListener('actionfailed')) {
6516                 Roo.log("need to add dialog support");
6517                 /*
6518                 Roo.MessageBox.alert("Error",
6519                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6520                         action.result.errorMsg :
6521                         "Saving Failed, please check your entries or try again"
6522                 );
6523                 */
6524             }
6525             
6526             this.fireEvent('actionfailed', this, action);
6527         }
6528         
6529     },
6530     /**
6531      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6532      * @param {String} id The value to search for
6533      * @return Field
6534      */
6535     findField : function(id){
6536         var items = this.getItems();
6537         var field = items.get(id);
6538         if(!field){
6539              items.each(function(f){
6540                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6541                     field = f;
6542                     return false;
6543                 }
6544                 return true;
6545             });
6546         }
6547         return field || null;
6548     },
6549      /**
6550      * Mark fields in this form invalid in bulk.
6551      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6552      * @return {BasicForm} this
6553      */
6554     markInvalid : function(errors){
6555         if(errors instanceof Array){
6556             for(var i = 0, len = errors.length; i < len; i++){
6557                 var fieldError = errors[i];
6558                 var f = this.findField(fieldError.id);
6559                 if(f){
6560                     f.markInvalid(fieldError.msg);
6561                 }
6562             }
6563         }else{
6564             var field, id;
6565             for(id in errors){
6566                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6567                     field.markInvalid(errors[id]);
6568                 }
6569             }
6570         }
6571         //Roo.each(this.childForms || [], function (f) {
6572         //    f.markInvalid(errors);
6573         //});
6574         
6575         return this;
6576     },
6577
6578     /**
6579      * Set values for fields in this form in bulk.
6580      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6581      * @return {BasicForm} this
6582      */
6583     setValues : function(values){
6584         if(values instanceof Array){ // array of objects
6585             for(var i = 0, len = values.length; i < len; i++){
6586                 var v = values[i];
6587                 var f = this.findField(v.id);
6588                 if(f){
6589                     f.setValue(v.value);
6590                     if(this.trackResetOnLoad){
6591                         f.originalValue = f.getValue();
6592                     }
6593                 }
6594             }
6595         }else{ // object hash
6596             var field, id;
6597             for(id in values){
6598                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6599                     
6600                     if (field.setFromData && 
6601                         field.valueField && 
6602                         field.displayField &&
6603                         // combos' with local stores can 
6604                         // be queried via setValue()
6605                         // to set their value..
6606                         (field.store && !field.store.isLocal)
6607                         ) {
6608                         // it's a combo
6609                         var sd = { };
6610                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6611                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6612                         field.setFromData(sd);
6613                         
6614                     } else {
6615                         field.setValue(values[id]);
6616                     }
6617                     
6618                     
6619                     if(this.trackResetOnLoad){
6620                         field.originalValue = field.getValue();
6621                     }
6622                 }
6623             }
6624         }
6625          
6626         //Roo.each(this.childForms || [], function (f) {
6627         //    f.setValues(values);
6628         //});
6629                 
6630         return this;
6631     },
6632
6633     /**
6634      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6635      * they are returned as an array.
6636      * @param {Boolean} asString
6637      * @return {Object}
6638      */
6639     getValues : function(asString){
6640         //if (this.childForms) {
6641             // copy values from the child forms
6642         //    Roo.each(this.childForms, function (f) {
6643         //        this.setValues(f.getValues());
6644         //    }, this);
6645         //}
6646         
6647         
6648         
6649         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6650         if(asString === true){
6651             return fs;
6652         }
6653         return Roo.urlDecode(fs);
6654     },
6655     
6656     /**
6657      * Returns the fields in this form as an object with key/value pairs. 
6658      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6659      * @return {Object}
6660      */
6661     getFieldValues : function(with_hidden)
6662     {
6663         var items = this.getItems();
6664         var ret = {};
6665         items.each(function(f){
6666             if (!f.getName()) {
6667                 return;
6668             }
6669             var v = f.getValue();
6670             if (f.inputType =='radio') {
6671                 if (typeof(ret[f.getName()]) == 'undefined') {
6672                     ret[f.getName()] = ''; // empty..
6673                 }
6674                 
6675                 if (!f.el.dom.checked) {
6676                     return;
6677                     
6678                 }
6679                 v = f.el.dom.value;
6680                 
6681             }
6682             
6683             // not sure if this supported any more..
6684             if ((typeof(v) == 'object') && f.getRawValue) {
6685                 v = f.getRawValue() ; // dates..
6686             }
6687             // combo boxes where name != hiddenName...
6688             if (f.name != f.getName()) {
6689                 ret[f.name] = f.getRawValue();
6690             }
6691             ret[f.getName()] = v;
6692         });
6693         
6694         return ret;
6695     },
6696
6697     /**
6698      * Clears all invalid messages in this form.
6699      * @return {BasicForm} this
6700      */
6701     clearInvalid : function(){
6702         var items = this.getItems();
6703         
6704         items.each(function(f){
6705            f.clearInvalid();
6706         });
6707         
6708         
6709         
6710         return this;
6711     },
6712
6713     /**
6714      * Resets this form.
6715      * @return {BasicForm} this
6716      */
6717     reset : function(){
6718         var items = this.getItems();
6719         items.each(function(f){
6720             f.reset();
6721         });
6722         
6723         Roo.each(this.childForms || [], function (f) {
6724             f.reset();
6725         });
6726        
6727         
6728         return this;
6729     },
6730     getItems : function()
6731     {
6732         var r=new Roo.util.MixedCollection(false, function(o){
6733             return o.id || (o.id = Roo.id());
6734         });
6735         var iter = function(el) {
6736             if (el.inputEl) {
6737                 r.add(el);
6738             }
6739             if (!el.items) {
6740                 return;
6741             }
6742             Roo.each(el.items,function(e) {
6743                 iter(e);
6744             });
6745             
6746             
6747         };
6748         iter(this);
6749         return r;
6750         
6751         
6752         
6753         
6754     }
6755     
6756 });
6757
6758  
6759 /*
6760  * Based on:
6761  * Ext JS Library 1.1.1
6762  * Copyright(c) 2006-2007, Ext JS, LLC.
6763  *
6764  * Originally Released Under LGPL - original licence link has changed is not relivant.
6765  *
6766  * Fork - LGPL
6767  * <script type="text/javascript">
6768  */
6769 /**
6770  * @class Roo.form.VTypes
6771  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6772  * @singleton
6773  */
6774 Roo.form.VTypes = function(){
6775     // closure these in so they are only created once.
6776     var alpha = /^[a-zA-Z_]+$/;
6777     var alphanum = /^[a-zA-Z0-9_]+$/;
6778     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6779     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6780
6781     // All these messages and functions are configurable
6782     return {
6783         /**
6784          * The function used to validate email addresses
6785          * @param {String} value The email address
6786          */
6787         'email' : function(v){
6788             return email.test(v);
6789         },
6790         /**
6791          * The error text to display when the email validation function returns false
6792          * @type String
6793          */
6794         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6795         /**
6796          * The keystroke filter mask to be applied on email input
6797          * @type RegExp
6798          */
6799         'emailMask' : /[a-z0-9_\.\-@]/i,
6800
6801         /**
6802          * The function used to validate URLs
6803          * @param {String} value The URL
6804          */
6805         'url' : function(v){
6806             return url.test(v);
6807         },
6808         /**
6809          * The error text to display when the url validation function returns false
6810          * @type String
6811          */
6812         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6813         
6814         /**
6815          * The function used to validate alpha values
6816          * @param {String} value The value
6817          */
6818         'alpha' : function(v){
6819             return alpha.test(v);
6820         },
6821         /**
6822          * The error text to display when the alpha validation function returns false
6823          * @type String
6824          */
6825         'alphaText' : 'This field should only contain letters and _',
6826         /**
6827          * The keystroke filter mask to be applied on alpha input
6828          * @type RegExp
6829          */
6830         'alphaMask' : /[a-z_]/i,
6831
6832         /**
6833          * The function used to validate alphanumeric values
6834          * @param {String} value The value
6835          */
6836         'alphanum' : function(v){
6837             return alphanum.test(v);
6838         },
6839         /**
6840          * The error text to display when the alphanumeric validation function returns false
6841          * @type String
6842          */
6843         'alphanumText' : 'This field should only contain letters, numbers and _',
6844         /**
6845          * The keystroke filter mask to be applied on alphanumeric input
6846          * @type RegExp
6847          */
6848         'alphanumMask' : /[a-z0-9_]/i
6849     };
6850 }();/*
6851  * - LGPL
6852  *
6853  * Input
6854  * 
6855  */
6856
6857 /**
6858  * @class Roo.bootstrap.Input
6859  * @extends Roo.bootstrap.Component
6860  * Bootstrap Input class
6861  * @cfg {Boolean} disabled is it disabled
6862  * @cfg {String} fieldLabel - the label associated
6863  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6864  * @cfg {String} name name of the input
6865  * @cfg {string} fieldLabel - the label associated
6866  * @cfg {string}  inputType - input / file submit ...
6867  * @cfg {string} placeholder - placeholder to put in text.
6868  * @cfg {string}  before - input group add on before
6869  * @cfg {string} after - input group add on after
6870  * @cfg {string} size - (lg|sm) or leave empty..
6871  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6872  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6873  * @cfg {Number} md colspan out of 12 for computer-sized screens
6874  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6875  * @cfg {string} value default value of the input
6876  * @cfg {Number} labelWidth set the width of label (0-12)
6877  * @cfg {String} labelAlign (top|left)
6878  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6879  * @cfg {String} align (left|center|right) Default left
6880  * 
6881  * 
6882  * @constructor
6883  * Create a new Input
6884  * @param {Object} config The config object
6885  */
6886
6887 Roo.bootstrap.Input = function(config){
6888     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6889    
6890         this.addEvents({
6891             /**
6892              * @event focus
6893              * Fires when this field receives input focus.
6894              * @param {Roo.form.Field} this
6895              */
6896             focus : true,
6897             /**
6898              * @event blur
6899              * Fires when this field loses input focus.
6900              * @param {Roo.form.Field} this
6901              */
6902             blur : true,
6903             /**
6904              * @event specialkey
6905              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6906              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6907              * @param {Roo.form.Field} this
6908              * @param {Roo.EventObject} e The event object
6909              */
6910             specialkey : true,
6911             /**
6912              * @event change
6913              * Fires just before the field blurs if the field value has changed.
6914              * @param {Roo.form.Field} this
6915              * @param {Mixed} newValue The new value
6916              * @param {Mixed} oldValue The original value
6917              */
6918             change : true,
6919             /**
6920              * @event invalid
6921              * Fires after the field has been marked as invalid.
6922              * @param {Roo.form.Field} this
6923              * @param {String} msg The validation message
6924              */
6925             invalid : true,
6926             /**
6927              * @event valid
6928              * Fires after the field has been validated with no errors.
6929              * @param {Roo.form.Field} this
6930              */
6931             valid : true,
6932              /**
6933              * @event keyup
6934              * Fires after the key up
6935              * @param {Roo.form.Field} this
6936              * @param {Roo.EventObject}  e The event Object
6937              */
6938             keyup : true
6939         });
6940 };
6941
6942 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6943      /**
6944      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6945       automatic validation (defaults to "keyup").
6946      */
6947     validationEvent : "keyup",
6948      /**
6949      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6950      */
6951     validateOnBlur : true,
6952     /**
6953      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6954      */
6955     validationDelay : 250,
6956      /**
6957      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6958      */
6959     focusClass : "x-form-focus",  // not needed???
6960     
6961        
6962     /**
6963      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6964      */
6965     invalidClass : "has-error",
6966     
6967     /**
6968      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6969      */
6970     selectOnFocus : false,
6971     
6972      /**
6973      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6974      */
6975     maskRe : null,
6976        /**
6977      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6978      */
6979     vtype : null,
6980     
6981       /**
6982      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6983      */
6984     disableKeyFilter : false,
6985     
6986        /**
6987      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6988      */
6989     disabled : false,
6990      /**
6991      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6992      */
6993     allowBlank : true,
6994     /**
6995      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6996      */
6997     blankText : "This field is required",
6998     
6999      /**
7000      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7001      */
7002     minLength : 0,
7003     /**
7004      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7005      */
7006     maxLength : Number.MAX_VALUE,
7007     /**
7008      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7009      */
7010     minLengthText : "The minimum length for this field is {0}",
7011     /**
7012      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7013      */
7014     maxLengthText : "The maximum length for this field is {0}",
7015   
7016     
7017     /**
7018      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7019      * If available, this function will be called only after the basic validators all return true, and will be passed the
7020      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7021      */
7022     validator : null,
7023     /**
7024      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7025      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7026      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7027      */
7028     regex : null,
7029     /**
7030      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7031      */
7032     regexText : "",
7033     
7034     
7035     
7036     fieldLabel : '',
7037     inputType : 'text',
7038     
7039     name : false,
7040     placeholder: false,
7041     before : false,
7042     after : false,
7043     size : false,
7044     // private
7045     hasFocus : false,
7046     preventMark: false,
7047     isFormField : true,
7048     value : '',
7049     labelWidth : 2,
7050     labelAlign : false,
7051     readOnly : false,
7052     align : false,
7053     formatedValue : false,
7054     
7055     parentLabelAlign : function()
7056     {
7057         var parent = this;
7058         while (parent.parent()) {
7059             parent = parent.parent();
7060             if (typeof(parent.labelAlign) !='undefined') {
7061                 return parent.labelAlign;
7062             }
7063         }
7064         return 'left';
7065         
7066     },
7067     
7068     getAutoCreate : function(){
7069         
7070         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7071         
7072         var id = Roo.id();
7073         
7074         var cfg = {};
7075         
7076         if(this.inputType != 'hidden'){
7077             cfg.cls = 'form-group' //input-group
7078         }
7079         
7080         var input =  {
7081             tag: 'input',
7082             id : id,
7083             type : this.inputType,
7084             value : this.value,
7085             cls : 'form-control',
7086             placeholder : this.placeholder || ''
7087             
7088         };
7089         
7090         if(this.align){
7091             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7092         }
7093         
7094         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7095             input.maxLength = this.maxLength;
7096         }
7097         
7098         if (this.disabled) {
7099             input.disabled=true;
7100         }
7101         
7102         if (this.readOnly) {
7103             input.readonly=true;
7104         }
7105         
7106         if (this.name) {
7107             input.name = this.name;
7108         }
7109         if (this.size) {
7110             input.cls += ' input-' + this.size;
7111         }
7112         var settings=this;
7113         ['xs','sm','md','lg'].map(function(size){
7114             if (settings[size]) {
7115                 cfg.cls += ' col-' + size + '-' + settings[size];
7116             }
7117         });
7118         
7119         var inputblock = input;
7120         
7121         if (this.before || this.after) {
7122             
7123             inputblock = {
7124                 cls : 'input-group',
7125                 cn :  [] 
7126             };
7127             if (this.before && typeof(this.before) == 'string') {
7128                 
7129                 inputblock.cn.push({
7130                     tag :'span',
7131                     cls : 'roo-input-before input-group-addon',
7132                     html : this.before
7133                 });
7134             }
7135             if (this.before && typeof(this.before) == 'object') {
7136                 this.before = Roo.factory(this.before);
7137                 Roo.log(this.before);
7138                 inputblock.cn.push({
7139                     tag :'span',
7140                     cls : 'roo-input-before input-group-' +
7141                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7142                 });
7143             }
7144             
7145             inputblock.cn.push(input);
7146             
7147             if (this.after && typeof(this.after) == 'string') {
7148                 inputblock.cn.push({
7149                     tag :'span',
7150                     cls : 'roo-input-after input-group-addon',
7151                     html : this.after
7152                 });
7153             }
7154             if (this.after && typeof(this.after) == 'object') {
7155                 this.after = Roo.factory(this.after);
7156                 Roo.log(this.after);
7157                 inputblock.cn.push({
7158                     tag :'span',
7159                     cls : 'roo-input-after input-group-' +
7160                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7161                 });
7162             }
7163         };
7164         
7165         if (align ==='left' && this.fieldLabel.length) {
7166                 Roo.log("left and has label");
7167                 cfg.cn = [
7168                     
7169                     {
7170                         tag: 'label',
7171                         'for' :  id,
7172                         cls : 'control-label col-sm-' + this.labelWidth,
7173                         html : this.fieldLabel
7174                         
7175                     },
7176                     {
7177                         cls : "col-sm-" + (12 - this.labelWidth), 
7178                         cn: [
7179                             inputblock
7180                         ]
7181                     }
7182                     
7183                 ];
7184         } else if ( this.fieldLabel.length) {
7185                 Roo.log(" label");
7186                  cfg.cn = [
7187                    
7188                     {
7189                         tag: 'label',
7190                         //cls : 'input-group-addon',
7191                         html : this.fieldLabel
7192                         
7193                     },
7194                     
7195                     inputblock
7196                     
7197                 ];
7198
7199         } else {
7200             
7201                 Roo.log(" no label && no align");
7202                 cfg.cn = [
7203                     
7204                         inputblock
7205                     
7206                 ];
7207                 
7208                 
7209         };
7210         Roo.log('input-parentType: ' + this.parentType);
7211         
7212         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7213            cfg.cls += ' navbar-form';
7214            Roo.log(cfg);
7215         }
7216         
7217         return cfg;
7218         
7219     },
7220     /**
7221      * return the real input element.
7222      */
7223     inputEl: function ()
7224     {
7225         return this.el.select('input.form-control',true).first();
7226     },
7227     
7228     tooltipEl : function()
7229     {
7230         return this.inputEl();
7231     },
7232     
7233     setDisabled : function(v)
7234     {
7235         var i  = this.inputEl().dom;
7236         if (!v) {
7237             i.removeAttribute('disabled');
7238             return;
7239             
7240         }
7241         i.setAttribute('disabled','true');
7242     },
7243     initEvents : function()
7244     {
7245           
7246         this.inputEl().on("keydown" , this.fireKey,  this);
7247         this.inputEl().on("focus", this.onFocus,  this);
7248         this.inputEl().on("blur", this.onBlur,  this);
7249         
7250         this.inputEl().relayEvent('keyup', this);
7251
7252         // reference to original value for reset
7253         this.originalValue = this.getValue();
7254         //Roo.form.TextField.superclass.initEvents.call(this);
7255         if(this.validationEvent == 'keyup'){
7256             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7257             this.inputEl().on('keyup', this.filterValidation, this);
7258         }
7259         else if(this.validationEvent !== false){
7260             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7261         }
7262         
7263         if(this.selectOnFocus){
7264             this.on("focus", this.preFocus, this);
7265             
7266         }
7267         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7268             this.inputEl().on("keypress", this.filterKeys, this);
7269         }
7270        /* if(this.grow){
7271             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7272             this.el.on("click", this.autoSize,  this);
7273         }
7274         */
7275         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7276             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7277         }
7278         
7279         if (typeof(this.before) == 'object') {
7280             this.before.render(this.el.select('.roo-input-before',true).first());
7281         }
7282         if (typeof(this.after) == 'object') {
7283             this.after.render(this.el.select('.roo-input-after',true).first());
7284         }
7285         
7286         
7287     },
7288     filterValidation : function(e){
7289         if(!e.isNavKeyPress()){
7290             this.validationTask.delay(this.validationDelay);
7291         }
7292     },
7293      /**
7294      * Validates the field value
7295      * @return {Boolean} True if the value is valid, else false
7296      */
7297     validate : function(){
7298         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7299         if(this.disabled || this.validateValue(this.getRawValue())){
7300             this.clearInvalid();
7301             return true;
7302         }
7303         return false;
7304     },
7305     
7306     
7307     /**
7308      * Validates a value according to the field's validation rules and marks the field as invalid
7309      * if the validation fails
7310      * @param {Mixed} value The value to validate
7311      * @return {Boolean} True if the value is valid, else false
7312      */
7313     validateValue : function(value){
7314         if(value.length < 1)  { // if it's blank
7315              if(this.allowBlank){
7316                 this.clearInvalid();
7317                 return true;
7318              }else{
7319                 this.markInvalid(this.blankText);
7320                 return false;
7321              }
7322         }
7323         if(value.length < this.minLength){
7324             this.markInvalid(String.format(this.minLengthText, this.minLength));
7325             return false;
7326         }
7327         if(value.length > this.maxLength){
7328             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7329             return false;
7330         }
7331         if(this.vtype){
7332             var vt = Roo.form.VTypes;
7333             if(!vt[this.vtype](value, this)){
7334                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7335                 return false;
7336             }
7337         }
7338         if(typeof this.validator == "function"){
7339             var msg = this.validator(value);
7340             if(msg !== true){
7341                 this.markInvalid(msg);
7342                 return false;
7343             }
7344         }
7345         if(this.regex && !this.regex.test(value)){
7346             this.markInvalid(this.regexText);
7347             return false;
7348         }
7349         return true;
7350     },
7351
7352     
7353     
7354      // private
7355     fireKey : function(e){
7356         //Roo.log('field ' + e.getKey());
7357         if(e.isNavKeyPress()){
7358             this.fireEvent("specialkey", this, e);
7359         }
7360     },
7361     focus : function (selectText){
7362         if(this.rendered){
7363             this.inputEl().focus();
7364             if(selectText === true){
7365                 this.inputEl().dom.select();
7366             }
7367         }
7368         return this;
7369     } ,
7370     
7371     onFocus : function(){
7372         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7373            // this.el.addClass(this.focusClass);
7374         }
7375         if(!this.hasFocus){
7376             this.hasFocus = true;
7377             this.startValue = this.getValue();
7378             this.fireEvent("focus", this);
7379         }
7380     },
7381     
7382     beforeBlur : Roo.emptyFn,
7383
7384     
7385     // private
7386     onBlur : function(){
7387         this.beforeBlur();
7388         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7389             //this.el.removeClass(this.focusClass);
7390         }
7391         this.hasFocus = false;
7392         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7393             this.validate();
7394         }
7395         var v = this.getValue();
7396         if(String(v) !== String(this.startValue)){
7397             this.fireEvent('change', this, v, this.startValue);
7398         }
7399         this.fireEvent("blur", this);
7400     },
7401     
7402     /**
7403      * Resets the current field value to the originally loaded value and clears any validation messages
7404      */
7405     reset : function(){
7406         this.setValue(this.originalValue);
7407         this.clearInvalid();
7408     },
7409      /**
7410      * Returns the name of the field
7411      * @return {Mixed} name The name field
7412      */
7413     getName: function(){
7414         return this.name;
7415     },
7416      /**
7417      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7418      * @return {Mixed} value The field value
7419      */
7420     getValue : function(){
7421         
7422         var v = this.inputEl().getValue();
7423         
7424         return v;
7425     },
7426     /**
7427      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7428      * @return {Mixed} value The field value
7429      */
7430     getRawValue : function(){
7431         var v = this.inputEl().getValue();
7432         
7433         return v;
7434     },
7435     
7436     /**
7437      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7438      * @param {Mixed} value The value to set
7439      */
7440     setRawValue : function(v){
7441         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7442     },
7443     
7444     selectText : function(start, end){
7445         var v = this.getRawValue();
7446         if(v.length > 0){
7447             start = start === undefined ? 0 : start;
7448             end = end === undefined ? v.length : end;
7449             var d = this.inputEl().dom;
7450             if(d.setSelectionRange){
7451                 d.setSelectionRange(start, end);
7452             }else if(d.createTextRange){
7453                 var range = d.createTextRange();
7454                 range.moveStart("character", start);
7455                 range.moveEnd("character", v.length-end);
7456                 range.select();
7457             }
7458         }
7459     },
7460     
7461     /**
7462      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7463      * @param {Mixed} value The value to set
7464      */
7465     setValue : function(v){
7466         this.value = v;
7467         if(this.rendered){
7468             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7469             this.validate();
7470         }
7471     },
7472     
7473     /*
7474     processValue : function(value){
7475         if(this.stripCharsRe){
7476             var newValue = value.replace(this.stripCharsRe, '');
7477             if(newValue !== value){
7478                 this.setRawValue(newValue);
7479                 return newValue;
7480             }
7481         }
7482         return value;
7483     },
7484   */
7485     preFocus : function(){
7486         
7487         if(this.selectOnFocus){
7488             this.inputEl().dom.select();
7489         }
7490     },
7491     filterKeys : function(e){
7492         var k = e.getKey();
7493         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7494             return;
7495         }
7496         var c = e.getCharCode(), cc = String.fromCharCode(c);
7497         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7498             return;
7499         }
7500         if(!this.maskRe.test(cc)){
7501             e.stopEvent();
7502         }
7503     },
7504      /**
7505      * Clear any invalid styles/messages for this field
7506      */
7507     clearInvalid : function(){
7508         
7509         if(!this.el || this.preventMark){ // not rendered
7510             return;
7511         }
7512         this.el.removeClass(this.invalidClass);
7513         /*
7514         switch(this.msgTarget){
7515             case 'qtip':
7516                 this.el.dom.qtip = '';
7517                 break;
7518             case 'title':
7519                 this.el.dom.title = '';
7520                 break;
7521             case 'under':
7522                 if(this.errorEl){
7523                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7524                 }
7525                 break;
7526             case 'side':
7527                 if(this.errorIcon){
7528                     this.errorIcon.dom.qtip = '';
7529                     this.errorIcon.hide();
7530                     this.un('resize', this.alignErrorIcon, this);
7531                 }
7532                 break;
7533             default:
7534                 var t = Roo.getDom(this.msgTarget);
7535                 t.innerHTML = '';
7536                 t.style.display = 'none';
7537                 break;
7538         }
7539         */
7540         this.fireEvent('valid', this);
7541     },
7542      /**
7543      * Mark this field as invalid
7544      * @param {String} msg The validation message
7545      */
7546     markInvalid : function(msg){
7547         if(!this.el  || this.preventMark){ // not rendered
7548             return;
7549         }
7550         this.el.addClass(this.invalidClass);
7551         /*
7552         msg = msg || this.invalidText;
7553         switch(this.msgTarget){
7554             case 'qtip':
7555                 this.el.dom.qtip = msg;
7556                 this.el.dom.qclass = 'x-form-invalid-tip';
7557                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7558                     Roo.QuickTips.enable();
7559                 }
7560                 break;
7561             case 'title':
7562                 this.el.dom.title = msg;
7563                 break;
7564             case 'under':
7565                 if(!this.errorEl){
7566                     var elp = this.el.findParent('.x-form-element', 5, true);
7567                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7568                     this.errorEl.setWidth(elp.getWidth(true)-20);
7569                 }
7570                 this.errorEl.update(msg);
7571                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7572                 break;
7573             case 'side':
7574                 if(!this.errorIcon){
7575                     var elp = this.el.findParent('.x-form-element', 5, true);
7576                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7577                 }
7578                 this.alignErrorIcon();
7579                 this.errorIcon.dom.qtip = msg;
7580                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7581                 this.errorIcon.show();
7582                 this.on('resize', this.alignErrorIcon, this);
7583                 break;
7584             default:
7585                 var t = Roo.getDom(this.msgTarget);
7586                 t.innerHTML = msg;
7587                 t.style.display = this.msgDisplay;
7588                 break;
7589         }
7590         */
7591         this.fireEvent('invalid', this, msg);
7592     },
7593     // private
7594     SafariOnKeyDown : function(event)
7595     {
7596         // this is a workaround for a password hang bug on chrome/ webkit.
7597         
7598         var isSelectAll = false;
7599         
7600         if(this.inputEl().dom.selectionEnd > 0){
7601             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7602         }
7603         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7604             event.preventDefault();
7605             this.setValue('');
7606             return;
7607         }
7608         
7609         if(isSelectAll){ // backspace and delete key
7610             
7611             event.preventDefault();
7612             // this is very hacky as keydown always get's upper case.
7613             //
7614             var cc = String.fromCharCode(event.getCharCode());
7615             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7616             
7617         }
7618     },
7619     adjustWidth : function(tag, w){
7620         tag = tag.toLowerCase();
7621         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7622             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7623                 if(tag == 'input'){
7624                     return w + 2;
7625                 }
7626                 if(tag == 'textarea'){
7627                     return w-2;
7628                 }
7629             }else if(Roo.isOpera){
7630                 if(tag == 'input'){
7631                     return w + 2;
7632                 }
7633                 if(tag == 'textarea'){
7634                     return w-2;
7635                 }
7636             }
7637         }
7638         return w;
7639     }
7640     
7641 });
7642
7643  
7644 /*
7645  * - LGPL
7646  *
7647  * Input
7648  * 
7649  */
7650
7651 /**
7652  * @class Roo.bootstrap.TextArea
7653  * @extends Roo.bootstrap.Input
7654  * Bootstrap TextArea class
7655  * @cfg {Number} cols Specifies the visible width of a text area
7656  * @cfg {Number} rows Specifies the visible number of lines in a text area
7657  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7658  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7659  * @cfg {string} html text
7660  * 
7661  * @constructor
7662  * Create a new TextArea
7663  * @param {Object} config The config object
7664  */
7665
7666 Roo.bootstrap.TextArea = function(config){
7667     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7668    
7669 };
7670
7671 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7672      
7673     cols : false,
7674     rows : 5,
7675     readOnly : false,
7676     warp : 'soft',
7677     resize : false,
7678     value: false,
7679     html: false,
7680     
7681     getAutoCreate : function(){
7682         
7683         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7684         
7685         var id = Roo.id();
7686         
7687         var cfg = {};
7688         
7689         var input =  {
7690             tag: 'textarea',
7691             id : id,
7692             warp : this.warp,
7693             rows : this.rows,
7694             value : this.value || '',
7695             html: this.html || '',
7696             cls : 'form-control',
7697             placeholder : this.placeholder || '' 
7698             
7699         };
7700         
7701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7702             input.maxLength = this.maxLength;
7703         }
7704         
7705         if(this.resize){
7706             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7707         }
7708         
7709         if(this.cols){
7710             input.cols = this.cols;
7711         }
7712         
7713         if (this.readOnly) {
7714             input.readonly = true;
7715         }
7716         
7717         if (this.name) {
7718             input.name = this.name;
7719         }
7720         
7721         if (this.size) {
7722             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7723         }
7724         
7725         var settings=this;
7726         ['xs','sm','md','lg'].map(function(size){
7727             if (settings[size]) {
7728                 cfg.cls += ' col-' + size + '-' + settings[size];
7729             }
7730         });
7731         
7732         var inputblock = input;
7733         
7734         if (this.before || this.after) {
7735             
7736             inputblock = {
7737                 cls : 'input-group',
7738                 cn :  [] 
7739             };
7740             if (this.before) {
7741                 inputblock.cn.push({
7742                     tag :'span',
7743                     cls : 'input-group-addon',
7744                     html : this.before
7745                 });
7746             }
7747             inputblock.cn.push(input);
7748             if (this.after) {
7749                 inputblock.cn.push({
7750                     tag :'span',
7751                     cls : 'input-group-addon',
7752                     html : this.after
7753                 });
7754             }
7755             
7756         }
7757         
7758         if (align ==='left' && this.fieldLabel.length) {
7759                 Roo.log("left and has label");
7760                 cfg.cn = [
7761                     
7762                     {
7763                         tag: 'label',
7764                         'for' :  id,
7765                         cls : 'control-label col-sm-' + this.labelWidth,
7766                         html : this.fieldLabel
7767                         
7768                     },
7769                     {
7770                         cls : "col-sm-" + (12 - this.labelWidth), 
7771                         cn: [
7772                             inputblock
7773                         ]
7774                     }
7775                     
7776                 ];
7777         } else if ( this.fieldLabel.length) {
7778                 Roo.log(" label");
7779                  cfg.cn = [
7780                    
7781                     {
7782                         tag: 'label',
7783                         //cls : 'input-group-addon',
7784                         html : this.fieldLabel
7785                         
7786                     },
7787                     
7788                     inputblock
7789                     
7790                 ];
7791
7792         } else {
7793             
7794                    Roo.log(" no label && no align");
7795                 cfg.cn = [
7796                     
7797                         inputblock
7798                     
7799                 ];
7800                 
7801                 
7802         }
7803         
7804         if (this.disabled) {
7805             input.disabled=true;
7806         }
7807         
7808         return cfg;
7809         
7810     },
7811     /**
7812      * return the real textarea element.
7813      */
7814     inputEl: function ()
7815     {
7816         return this.el.select('textarea.form-control',true).first();
7817     }
7818 });
7819
7820  
7821 /*
7822  * - LGPL
7823  *
7824  * trigger field - base class for combo..
7825  * 
7826  */
7827  
7828 /**
7829  * @class Roo.bootstrap.TriggerField
7830  * @extends Roo.bootstrap.Input
7831  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7832  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7833  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7834  * for which you can provide a custom implementation.  For example:
7835  * <pre><code>
7836 var trigger = new Roo.bootstrap.TriggerField();
7837 trigger.onTriggerClick = myTriggerFn;
7838 trigger.applyTo('my-field');
7839 </code></pre>
7840  *
7841  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7842  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7843  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7844  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7845  * @constructor
7846  * Create a new TriggerField.
7847  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7848  * to the base TextField)
7849  */
7850 Roo.bootstrap.TriggerField = function(config){
7851     this.mimicing = false;
7852     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7853 };
7854
7855 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7856     /**
7857      * @cfg {String} triggerClass A CSS class to apply to the trigger
7858      */
7859      /**
7860      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7861      */
7862     hideTrigger:false,
7863
7864     /** @cfg {Boolean} grow @hide */
7865     /** @cfg {Number} growMin @hide */
7866     /** @cfg {Number} growMax @hide */
7867
7868     /**
7869      * @hide 
7870      * @method
7871      */
7872     autoSize: Roo.emptyFn,
7873     // private
7874     monitorTab : true,
7875     // private
7876     deferHeight : true,
7877
7878     
7879     actionMode : 'wrap',
7880     
7881     
7882     
7883     getAutoCreate : function(){
7884        
7885         var align = this.labelAlign || this.parentLabelAlign();
7886         
7887         var id = Roo.id();
7888         
7889         var cfg = {
7890             cls: 'form-group' //input-group
7891         };
7892         
7893         
7894         var input =  {
7895             tag: 'input',
7896             id : id,
7897             type : this.inputType,
7898             cls : 'form-control',
7899             autocomplete: 'off',
7900             placeholder : this.placeholder || '' 
7901             
7902         };
7903         if (this.name) {
7904             input.name = this.name;
7905         }
7906         if (this.size) {
7907             input.cls += ' input-' + this.size;
7908         }
7909         
7910         if (this.disabled) {
7911             input.disabled=true;
7912         }
7913         
7914         var inputblock = input;
7915         
7916         if (this.before || this.after) {
7917             
7918             inputblock = {
7919                 cls : 'input-group',
7920                 cn :  [] 
7921             };
7922             if (this.before) {
7923                 inputblock.cn.push({
7924                     tag :'span',
7925                     cls : 'input-group-addon',
7926                     html : this.before
7927                 });
7928             }
7929             inputblock.cn.push(input);
7930             if (this.after) {
7931                 inputblock.cn.push({
7932                     tag :'span',
7933                     cls : 'input-group-addon',
7934                     html : this.after
7935                 });
7936             }
7937             
7938         };
7939         
7940         var box = {
7941             tag: 'div',
7942             cn: [
7943                 {
7944                     tag: 'input',
7945                     type : 'hidden',
7946                     cls: 'form-hidden-field'
7947                 },
7948                 inputblock
7949             ]
7950             
7951         };
7952         
7953         if(this.multiple){
7954             Roo.log('multiple');
7955             
7956             box = {
7957                 tag: 'div',
7958                 cn: [
7959                     {
7960                         tag: 'input',
7961                         type : 'hidden',
7962                         cls: 'form-hidden-field'
7963                     },
7964                     {
7965                         tag: 'ul',
7966                         cls: 'select2-choices',
7967                         cn:[
7968                             {
7969                                 tag: 'li',
7970                                 cls: 'select2-search-field',
7971                                 cn: [
7972
7973                                     inputblock
7974                                 ]
7975                             }
7976                         ]
7977                     }
7978                 ]
7979             }
7980         };
7981         
7982         var combobox = {
7983             cls: 'select2-container input-group',
7984             cn: [
7985                 box
7986 //                {
7987 //                    tag: 'ul',
7988 //                    cls: 'typeahead typeahead-long dropdown-menu',
7989 //                    style: 'display:none'
7990 //                }
7991             ]
7992         };
7993         
7994         if(!this.multiple && this.showToggleBtn){
7995             combobox.cn.push({
7996                 tag :'span',
7997                 cls : 'input-group-addon btn dropdown-toggle',
7998                 cn : [
7999                     {
8000                         tag: 'span',
8001                         cls: 'caret'
8002                     },
8003                     {
8004                         tag: 'span',
8005                         cls: 'combobox-clear',
8006                         cn  : [
8007                             {
8008                                 tag : 'i',
8009                                 cls: 'icon-remove'
8010                             }
8011                         ]
8012                     }
8013                 ]
8014
8015             })
8016         }
8017         
8018         if(this.multiple){
8019             combobox.cls += ' select2-container-multi';
8020         }
8021         
8022         if (align ==='left' && this.fieldLabel.length) {
8023             
8024                 Roo.log("left and has label");
8025                 cfg.cn = [
8026                     
8027                     {
8028                         tag: 'label',
8029                         'for' :  id,
8030                         cls : 'control-label col-sm-' + this.labelWidth,
8031                         html : this.fieldLabel
8032                         
8033                     },
8034                     {
8035                         cls : "col-sm-" + (12 - this.labelWidth), 
8036                         cn: [
8037                             combobox
8038                         ]
8039                     }
8040                     
8041                 ];
8042         } else if ( this.fieldLabel.length) {
8043                 Roo.log(" label");
8044                  cfg.cn = [
8045                    
8046                     {
8047                         tag: 'label',
8048                         //cls : 'input-group-addon',
8049                         html : this.fieldLabel
8050                         
8051                     },
8052                     
8053                     combobox
8054                     
8055                 ];
8056
8057         } else {
8058             
8059                 Roo.log(" no label && no align");
8060                 cfg = combobox
8061                      
8062                 
8063         }
8064          
8065         var settings=this;
8066         ['xs','sm','md','lg'].map(function(size){
8067             if (settings[size]) {
8068                 cfg.cls += ' col-' + size + '-' + settings[size];
8069             }
8070         });
8071         
8072         return cfg;
8073         
8074     },
8075     
8076     
8077     
8078     // private
8079     onResize : function(w, h){
8080 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8081 //        if(typeof w == 'number'){
8082 //            var x = w - this.trigger.getWidth();
8083 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8084 //            this.trigger.setStyle('left', x+'px');
8085 //        }
8086     },
8087
8088     // private
8089     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8090
8091     // private
8092     getResizeEl : function(){
8093         return this.inputEl();
8094     },
8095
8096     // private
8097     getPositionEl : function(){
8098         return this.inputEl();
8099     },
8100
8101     // private
8102     alignErrorIcon : function(){
8103         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8104     },
8105
8106     // private
8107     initEvents : function(){
8108         
8109         this.createList();
8110         
8111         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8112         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8113         if(!this.multiple && this.showToggleBtn){
8114             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8115             if(this.hideTrigger){
8116                 this.trigger.setDisplayed(false);
8117             }
8118             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8119         }
8120         
8121         if(this.multiple){
8122             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8123         }
8124         
8125         //this.trigger.addClassOnOver('x-form-trigger-over');
8126         //this.trigger.addClassOnClick('x-form-trigger-click');
8127         
8128         //if(!this.width){
8129         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8130         //}
8131     },
8132     
8133     createList : function()
8134     {
8135         this.list = Roo.get(document.body).createChild({
8136             tag: 'ul',
8137             cls: 'typeahead typeahead-long dropdown-menu',
8138             style: 'display:none'
8139         });
8140         
8141         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8142         
8143     },
8144
8145     // private
8146     initTrigger : function(){
8147        
8148     },
8149
8150     // private
8151     onDestroy : function(){
8152         if(this.trigger){
8153             this.trigger.removeAllListeners();
8154           //  this.trigger.remove();
8155         }
8156         //if(this.wrap){
8157         //    this.wrap.remove();
8158         //}
8159         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8160     },
8161
8162     // private
8163     onFocus : function(){
8164         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8165         /*
8166         if(!this.mimicing){
8167             this.wrap.addClass('x-trigger-wrap-focus');
8168             this.mimicing = true;
8169             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8170             if(this.monitorTab){
8171                 this.el.on("keydown", this.checkTab, this);
8172             }
8173         }
8174         */
8175     },
8176
8177     // private
8178     checkTab : function(e){
8179         if(e.getKey() == e.TAB){
8180             this.triggerBlur();
8181         }
8182     },
8183
8184     // private
8185     onBlur : function(){
8186         // do nothing
8187     },
8188
8189     // private
8190     mimicBlur : function(e, t){
8191         /*
8192         if(!this.wrap.contains(t) && this.validateBlur()){
8193             this.triggerBlur();
8194         }
8195         */
8196     },
8197
8198     // private
8199     triggerBlur : function(){
8200         this.mimicing = false;
8201         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8202         if(this.monitorTab){
8203             this.el.un("keydown", this.checkTab, this);
8204         }
8205         //this.wrap.removeClass('x-trigger-wrap-focus');
8206         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8207     },
8208
8209     // private
8210     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8211     validateBlur : function(e, t){
8212         return true;
8213     },
8214
8215     // private
8216     onDisable : function(){
8217         this.inputEl().dom.disabled = true;
8218         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8219         //if(this.wrap){
8220         //    this.wrap.addClass('x-item-disabled');
8221         //}
8222     },
8223
8224     // private
8225     onEnable : function(){
8226         this.inputEl().dom.disabled = false;
8227         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8228         //if(this.wrap){
8229         //    this.el.removeClass('x-item-disabled');
8230         //}
8231     },
8232
8233     // private
8234     onShow : function(){
8235         var ae = this.getActionEl();
8236         
8237         if(ae){
8238             ae.dom.style.display = '';
8239             ae.dom.style.visibility = 'visible';
8240         }
8241     },
8242
8243     // private
8244     
8245     onHide : function(){
8246         var ae = this.getActionEl();
8247         ae.dom.style.display = 'none';
8248     },
8249
8250     /**
8251      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8252      * by an implementing function.
8253      * @method
8254      * @param {EventObject} e
8255      */
8256     onTriggerClick : Roo.emptyFn
8257 });
8258  /*
8259  * Based on:
8260  * Ext JS Library 1.1.1
8261  * Copyright(c) 2006-2007, Ext JS, LLC.
8262  *
8263  * Originally Released Under LGPL - original licence link has changed is not relivant.
8264  *
8265  * Fork - LGPL
8266  * <script type="text/javascript">
8267  */
8268
8269
8270 /**
8271  * @class Roo.data.SortTypes
8272  * @singleton
8273  * Defines the default sorting (casting?) comparison functions used when sorting data.
8274  */
8275 Roo.data.SortTypes = {
8276     /**
8277      * Default sort that does nothing
8278      * @param {Mixed} s The value being converted
8279      * @return {Mixed} The comparison value
8280      */
8281     none : function(s){
8282         return s;
8283     },
8284     
8285     /**
8286      * The regular expression used to strip tags
8287      * @type {RegExp}
8288      * @property
8289      */
8290     stripTagsRE : /<\/?[^>]+>/gi,
8291     
8292     /**
8293      * Strips all HTML tags to sort on text only
8294      * @param {Mixed} s The value being converted
8295      * @return {String} The comparison value
8296      */
8297     asText : function(s){
8298         return String(s).replace(this.stripTagsRE, "");
8299     },
8300     
8301     /**
8302      * Strips all HTML tags to sort on text only - Case insensitive
8303      * @param {Mixed} s The value being converted
8304      * @return {String} The comparison value
8305      */
8306     asUCText : function(s){
8307         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8308     },
8309     
8310     /**
8311      * Case insensitive string
8312      * @param {Mixed} s The value being converted
8313      * @return {String} The comparison value
8314      */
8315     asUCString : function(s) {
8316         return String(s).toUpperCase();
8317     },
8318     
8319     /**
8320      * Date sorting
8321      * @param {Mixed} s The value being converted
8322      * @return {Number} The comparison value
8323      */
8324     asDate : function(s) {
8325         if(!s){
8326             return 0;
8327         }
8328         if(s instanceof Date){
8329             return s.getTime();
8330         }
8331         return Date.parse(String(s));
8332     },
8333     
8334     /**
8335      * Float sorting
8336      * @param {Mixed} s The value being converted
8337      * @return {Float} The comparison value
8338      */
8339     asFloat : function(s) {
8340         var val = parseFloat(String(s).replace(/,/g, ""));
8341         if(isNaN(val)) val = 0;
8342         return val;
8343     },
8344     
8345     /**
8346      * Integer sorting
8347      * @param {Mixed} s The value being converted
8348      * @return {Number} The comparison value
8349      */
8350     asInt : function(s) {
8351         var val = parseInt(String(s).replace(/,/g, ""));
8352         if(isNaN(val)) val = 0;
8353         return val;
8354     }
8355 };/*
8356  * Based on:
8357  * Ext JS Library 1.1.1
8358  * Copyright(c) 2006-2007, Ext JS, LLC.
8359  *
8360  * Originally Released Under LGPL - original licence link has changed is not relivant.
8361  *
8362  * Fork - LGPL
8363  * <script type="text/javascript">
8364  */
8365
8366 /**
8367 * @class Roo.data.Record
8368  * Instances of this class encapsulate both record <em>definition</em> information, and record
8369  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8370  * to access Records cached in an {@link Roo.data.Store} object.<br>
8371  * <p>
8372  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8373  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8374  * objects.<br>
8375  * <p>
8376  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8377  * @constructor
8378  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8379  * {@link #create}. The parameters are the same.
8380  * @param {Array} data An associative Array of data values keyed by the field name.
8381  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8382  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8383  * not specified an integer id is generated.
8384  */
8385 Roo.data.Record = function(data, id){
8386     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8387     this.data = data;
8388 };
8389
8390 /**
8391  * Generate a constructor for a specific record layout.
8392  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8393  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8394  * Each field definition object may contain the following properties: <ul>
8395  * <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,
8396  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8397  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8398  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8399  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8400  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8401  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8402  * this may be omitted.</p></li>
8403  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8404  * <ul><li>auto (Default, implies no conversion)</li>
8405  * <li>string</li>
8406  * <li>int</li>
8407  * <li>float</li>
8408  * <li>boolean</li>
8409  * <li>date</li></ul></p></li>
8410  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8411  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8412  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8413  * by the Reader into an object that will be stored in the Record. It is passed the
8414  * following parameters:<ul>
8415  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8416  * </ul></p></li>
8417  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8418  * </ul>
8419  * <br>usage:<br><pre><code>
8420 var TopicRecord = Roo.data.Record.create(
8421     {name: 'title', mapping: 'topic_title'},
8422     {name: 'author', mapping: 'username'},
8423     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8424     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8425     {name: 'lastPoster', mapping: 'user2'},
8426     {name: 'excerpt', mapping: 'post_text'}
8427 );
8428
8429 var myNewRecord = new TopicRecord({
8430     title: 'Do my job please',
8431     author: 'noobie',
8432     totalPosts: 1,
8433     lastPost: new Date(),
8434     lastPoster: 'Animal',
8435     excerpt: 'No way dude!'
8436 });
8437 myStore.add(myNewRecord);
8438 </code></pre>
8439  * @method create
8440  * @static
8441  */
8442 Roo.data.Record.create = function(o){
8443     var f = function(){
8444         f.superclass.constructor.apply(this, arguments);
8445     };
8446     Roo.extend(f, Roo.data.Record);
8447     var p = f.prototype;
8448     p.fields = new Roo.util.MixedCollection(false, function(field){
8449         return field.name;
8450     });
8451     for(var i = 0, len = o.length; i < len; i++){
8452         p.fields.add(new Roo.data.Field(o[i]));
8453     }
8454     f.getField = function(name){
8455         return p.fields.get(name);  
8456     };
8457     return f;
8458 };
8459
8460 Roo.data.Record.AUTO_ID = 1000;
8461 Roo.data.Record.EDIT = 'edit';
8462 Roo.data.Record.REJECT = 'reject';
8463 Roo.data.Record.COMMIT = 'commit';
8464
8465 Roo.data.Record.prototype = {
8466     /**
8467      * Readonly flag - true if this record has been modified.
8468      * @type Boolean
8469      */
8470     dirty : false,
8471     editing : false,
8472     error: null,
8473     modified: null,
8474
8475     // private
8476     join : function(store){
8477         this.store = store;
8478     },
8479
8480     /**
8481      * Set the named field to the specified value.
8482      * @param {String} name The name of the field to set.
8483      * @param {Object} value The value to set the field to.
8484      */
8485     set : function(name, value){
8486         if(this.data[name] == value){
8487             return;
8488         }
8489         this.dirty = true;
8490         if(!this.modified){
8491             this.modified = {};
8492         }
8493         if(typeof this.modified[name] == 'undefined'){
8494             this.modified[name] = this.data[name];
8495         }
8496         this.data[name] = value;
8497         if(!this.editing && this.store){
8498             this.store.afterEdit(this);
8499         }       
8500     },
8501
8502     /**
8503      * Get the value of the named field.
8504      * @param {String} name The name of the field to get the value of.
8505      * @return {Object} The value of the field.
8506      */
8507     get : function(name){
8508         return this.data[name]; 
8509     },
8510
8511     // private
8512     beginEdit : function(){
8513         this.editing = true;
8514         this.modified = {}; 
8515     },
8516
8517     // private
8518     cancelEdit : function(){
8519         this.editing = false;
8520         delete this.modified;
8521     },
8522
8523     // private
8524     endEdit : function(){
8525         this.editing = false;
8526         if(this.dirty && this.store){
8527             this.store.afterEdit(this);
8528         }
8529     },
8530
8531     /**
8532      * Usually called by the {@link Roo.data.Store} which owns the Record.
8533      * Rejects all changes made to the Record since either creation, or the last commit operation.
8534      * Modified fields are reverted to their original values.
8535      * <p>
8536      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8537      * of reject operations.
8538      */
8539     reject : function(){
8540         var m = this.modified;
8541         for(var n in m){
8542             if(typeof m[n] != "function"){
8543                 this.data[n] = m[n];
8544             }
8545         }
8546         this.dirty = false;
8547         delete this.modified;
8548         this.editing = false;
8549         if(this.store){
8550             this.store.afterReject(this);
8551         }
8552     },
8553
8554     /**
8555      * Usually called by the {@link Roo.data.Store} which owns the Record.
8556      * Commits all changes made to the Record since either creation, or the last commit operation.
8557      * <p>
8558      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8559      * of commit operations.
8560      */
8561     commit : function(){
8562         this.dirty = false;
8563         delete this.modified;
8564         this.editing = false;
8565         if(this.store){
8566             this.store.afterCommit(this);
8567         }
8568     },
8569
8570     // private
8571     hasError : function(){
8572         return this.error != null;
8573     },
8574
8575     // private
8576     clearError : function(){
8577         this.error = null;
8578     },
8579
8580     /**
8581      * Creates a copy of this record.
8582      * @param {String} id (optional) A new record id if you don't want to use this record's id
8583      * @return {Record}
8584      */
8585     copy : function(newId) {
8586         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8587     }
8588 };/*
8589  * Based on:
8590  * Ext JS Library 1.1.1
8591  * Copyright(c) 2006-2007, Ext JS, LLC.
8592  *
8593  * Originally Released Under LGPL - original licence link has changed is not relivant.
8594  *
8595  * Fork - LGPL
8596  * <script type="text/javascript">
8597  */
8598
8599
8600
8601 /**
8602  * @class Roo.data.Store
8603  * @extends Roo.util.Observable
8604  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8605  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8606  * <p>
8607  * 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
8608  * has no knowledge of the format of the data returned by the Proxy.<br>
8609  * <p>
8610  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8611  * instances from the data object. These records are cached and made available through accessor functions.
8612  * @constructor
8613  * Creates a new Store.
8614  * @param {Object} config A config object containing the objects needed for the Store to access data,
8615  * and read the data into Records.
8616  */
8617 Roo.data.Store = function(config){
8618     this.data = new Roo.util.MixedCollection(false);
8619     this.data.getKey = function(o){
8620         return o.id;
8621     };
8622     this.baseParams = {};
8623     // private
8624     this.paramNames = {
8625         "start" : "start",
8626         "limit" : "limit",
8627         "sort" : "sort",
8628         "dir" : "dir",
8629         "multisort" : "_multisort"
8630     };
8631
8632     if(config && config.data){
8633         this.inlineData = config.data;
8634         delete config.data;
8635     }
8636
8637     Roo.apply(this, config);
8638     
8639     if(this.reader){ // reader passed
8640         this.reader = Roo.factory(this.reader, Roo.data);
8641         this.reader.xmodule = this.xmodule || false;
8642         if(!this.recordType){
8643             this.recordType = this.reader.recordType;
8644         }
8645         if(this.reader.onMetaChange){
8646             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8647         }
8648     }
8649
8650     if(this.recordType){
8651         this.fields = this.recordType.prototype.fields;
8652     }
8653     this.modified = [];
8654
8655     this.addEvents({
8656         /**
8657          * @event datachanged
8658          * Fires when the data cache has changed, and a widget which is using this Store
8659          * as a Record cache should refresh its view.
8660          * @param {Store} this
8661          */
8662         datachanged : true,
8663         /**
8664          * @event metachange
8665          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8666          * @param {Store} this
8667          * @param {Object} meta The JSON metadata
8668          */
8669         metachange : true,
8670         /**
8671          * @event add
8672          * Fires when Records have been added to the Store
8673          * @param {Store} this
8674          * @param {Roo.data.Record[]} records The array of Records added
8675          * @param {Number} index The index at which the record(s) were added
8676          */
8677         add : true,
8678         /**
8679          * @event remove
8680          * Fires when a Record has been removed from the Store
8681          * @param {Store} this
8682          * @param {Roo.data.Record} record The Record that was removed
8683          * @param {Number} index The index at which the record was removed
8684          */
8685         remove : true,
8686         /**
8687          * @event update
8688          * Fires when a Record has been updated
8689          * @param {Store} this
8690          * @param {Roo.data.Record} record The Record that was updated
8691          * @param {String} operation The update operation being performed.  Value may be one of:
8692          * <pre><code>
8693  Roo.data.Record.EDIT
8694  Roo.data.Record.REJECT
8695  Roo.data.Record.COMMIT
8696          * </code></pre>
8697          */
8698         update : true,
8699         /**
8700          * @event clear
8701          * Fires when the data cache has been cleared.
8702          * @param {Store} this
8703          */
8704         clear : true,
8705         /**
8706          * @event beforeload
8707          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8708          * the load action will be canceled.
8709          * @param {Store} this
8710          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8711          */
8712         beforeload : true,
8713         /**
8714          * @event beforeloadadd
8715          * Fires after a new set of Records has been loaded.
8716          * @param {Store} this
8717          * @param {Roo.data.Record[]} records The Records that were loaded
8718          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8719          */
8720         beforeloadadd : true,
8721         /**
8722          * @event load
8723          * Fires after a new set of Records has been loaded, before they are added to the store.
8724          * @param {Store} this
8725          * @param {Roo.data.Record[]} records The Records that were loaded
8726          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8727          * @params {Object} return from reader
8728          */
8729         load : true,
8730         /**
8731          * @event loadexception
8732          * Fires if an exception occurs in the Proxy during loading.
8733          * Called with the signature of the Proxy's "loadexception" event.
8734          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8735          * 
8736          * @param {Proxy} 
8737          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8738          * @param {Object} load options 
8739          * @param {Object} jsonData from your request (normally this contains the Exception)
8740          */
8741         loadexception : true
8742     });
8743     
8744     if(this.proxy){
8745         this.proxy = Roo.factory(this.proxy, Roo.data);
8746         this.proxy.xmodule = this.xmodule || false;
8747         this.relayEvents(this.proxy,  ["loadexception"]);
8748     }
8749     this.sortToggle = {};
8750     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8751
8752     Roo.data.Store.superclass.constructor.call(this);
8753
8754     if(this.inlineData){
8755         this.loadData(this.inlineData);
8756         delete this.inlineData;
8757     }
8758 };
8759
8760 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8761      /**
8762     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8763     * without a remote query - used by combo/forms at present.
8764     */
8765     
8766     /**
8767     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8768     */
8769     /**
8770     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8771     */
8772     /**
8773     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8774     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8775     */
8776     /**
8777     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8778     * on any HTTP request
8779     */
8780     /**
8781     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8782     */
8783     /**
8784     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8785     */
8786     multiSort: false,
8787     /**
8788     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8789     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8790     */
8791     remoteSort : false,
8792
8793     /**
8794     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8795      * loaded or when a record is removed. (defaults to false).
8796     */
8797     pruneModifiedRecords : false,
8798
8799     // private
8800     lastOptions : null,
8801
8802     /**
8803      * Add Records to the Store and fires the add event.
8804      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8805      */
8806     add : function(records){
8807         records = [].concat(records);
8808         for(var i = 0, len = records.length; i < len; i++){
8809             records[i].join(this);
8810         }
8811         var index = this.data.length;
8812         this.data.addAll(records);
8813         this.fireEvent("add", this, records, index);
8814     },
8815
8816     /**
8817      * Remove a Record from the Store and fires the remove event.
8818      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8819      */
8820     remove : function(record){
8821         var index = this.data.indexOf(record);
8822         this.data.removeAt(index);
8823         if(this.pruneModifiedRecords){
8824             this.modified.remove(record);
8825         }
8826         this.fireEvent("remove", this, record, index);
8827     },
8828
8829     /**
8830      * Remove all Records from the Store and fires the clear event.
8831      */
8832     removeAll : function(){
8833         this.data.clear();
8834         if(this.pruneModifiedRecords){
8835             this.modified = [];
8836         }
8837         this.fireEvent("clear", this);
8838     },
8839
8840     /**
8841      * Inserts Records to the Store at the given index and fires the add event.
8842      * @param {Number} index The start index at which to insert the passed Records.
8843      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8844      */
8845     insert : function(index, records){
8846         records = [].concat(records);
8847         for(var i = 0, len = records.length; i < len; i++){
8848             this.data.insert(index, records[i]);
8849             records[i].join(this);
8850         }
8851         this.fireEvent("add", this, records, index);
8852     },
8853
8854     /**
8855      * Get the index within the cache of the passed Record.
8856      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8857      * @return {Number} The index of the passed Record. Returns -1 if not found.
8858      */
8859     indexOf : function(record){
8860         return this.data.indexOf(record);
8861     },
8862
8863     /**
8864      * Get the index within the cache of the Record with the passed id.
8865      * @param {String} id The id of the Record to find.
8866      * @return {Number} The index of the Record. Returns -1 if not found.
8867      */
8868     indexOfId : function(id){
8869         return this.data.indexOfKey(id);
8870     },
8871
8872     /**
8873      * Get the Record with the specified id.
8874      * @param {String} id The id of the Record to find.
8875      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8876      */
8877     getById : function(id){
8878         return this.data.key(id);
8879     },
8880
8881     /**
8882      * Get the Record at the specified index.
8883      * @param {Number} index The index of the Record to find.
8884      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8885      */
8886     getAt : function(index){
8887         return this.data.itemAt(index);
8888     },
8889
8890     /**
8891      * Returns a range of Records between specified indices.
8892      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8893      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8894      * @return {Roo.data.Record[]} An array of Records
8895      */
8896     getRange : function(start, end){
8897         return this.data.getRange(start, end);
8898     },
8899
8900     // private
8901     storeOptions : function(o){
8902         o = Roo.apply({}, o);
8903         delete o.callback;
8904         delete o.scope;
8905         this.lastOptions = o;
8906     },
8907
8908     /**
8909      * Loads the Record cache from the configured Proxy using the configured Reader.
8910      * <p>
8911      * If using remote paging, then the first load call must specify the <em>start</em>
8912      * and <em>limit</em> properties in the options.params property to establish the initial
8913      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8914      * <p>
8915      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8916      * and this call will return before the new data has been loaded. Perform any post-processing
8917      * in a callback function, or in a "load" event handler.</strong>
8918      * <p>
8919      * @param {Object} options An object containing properties which control loading options:<ul>
8920      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8921      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8922      * passed the following arguments:<ul>
8923      * <li>r : Roo.data.Record[]</li>
8924      * <li>options: Options object from the load call</li>
8925      * <li>success: Boolean success indicator</li></ul></li>
8926      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8927      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8928      * </ul>
8929      */
8930     load : function(options){
8931         options = options || {};
8932         if(this.fireEvent("beforeload", this, options) !== false){
8933             this.storeOptions(options);
8934             var p = Roo.apply(options.params || {}, this.baseParams);
8935             // if meta was not loaded from remote source.. try requesting it.
8936             if (!this.reader.metaFromRemote) {
8937                 p._requestMeta = 1;
8938             }
8939             if(this.sortInfo && this.remoteSort){
8940                 var pn = this.paramNames;
8941                 p[pn["sort"]] = this.sortInfo.field;
8942                 p[pn["dir"]] = this.sortInfo.direction;
8943             }
8944             if (this.multiSort) {
8945                 var pn = this.paramNames;
8946                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8947             }
8948             
8949             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8950         }
8951     },
8952
8953     /**
8954      * Reloads the Record cache from the configured Proxy using the configured Reader and
8955      * the options from the last load operation performed.
8956      * @param {Object} options (optional) An object containing properties which may override the options
8957      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8958      * the most recently used options are reused).
8959      */
8960     reload : function(options){
8961         this.load(Roo.applyIf(options||{}, this.lastOptions));
8962     },
8963
8964     // private
8965     // Called as a callback by the Reader during a load operation.
8966     loadRecords : function(o, options, success){
8967         if(!o || success === false){
8968             if(success !== false){
8969                 this.fireEvent("load", this, [], options, o);
8970             }
8971             if(options.callback){
8972                 options.callback.call(options.scope || this, [], options, false);
8973             }
8974             return;
8975         }
8976         // if data returned failure - throw an exception.
8977         if (o.success === false) {
8978             // show a message if no listener is registered.
8979             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8980                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8981             }
8982             // loadmask wil be hooked into this..
8983             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8984             return;
8985         }
8986         var r = o.records, t = o.totalRecords || r.length;
8987         
8988         this.fireEvent("beforeloadadd", this, r, options, o);
8989         
8990         if(!options || options.add !== true){
8991             if(this.pruneModifiedRecords){
8992                 this.modified = [];
8993             }
8994             for(var i = 0, len = r.length; i < len; i++){
8995                 r[i].join(this);
8996             }
8997             if(this.snapshot){
8998                 this.data = this.snapshot;
8999                 delete this.snapshot;
9000             }
9001             this.data.clear();
9002             this.data.addAll(r);
9003             this.totalLength = t;
9004             this.applySort();
9005             this.fireEvent("datachanged", this);
9006         }else{
9007             this.totalLength = Math.max(t, this.data.length+r.length);
9008             this.add(r);
9009         }
9010         this.fireEvent("load", this, r, options, o);
9011         if(options.callback){
9012             options.callback.call(options.scope || this, r, options, true);
9013         }
9014     },
9015
9016
9017     /**
9018      * Loads data from a passed data block. A Reader which understands the format of the data
9019      * must have been configured in the constructor.
9020      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9021      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9022      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9023      */
9024     loadData : function(o, append){
9025         var r = this.reader.readRecords(o);
9026         this.loadRecords(r, {add: append}, true);
9027     },
9028
9029     /**
9030      * Gets the number of cached records.
9031      * <p>
9032      * <em>If using paging, this may not be the total size of the dataset. If the data object
9033      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9034      * the data set size</em>
9035      */
9036     getCount : function(){
9037         return this.data.length || 0;
9038     },
9039
9040     /**
9041      * Gets the total number of records in the dataset as returned by the server.
9042      * <p>
9043      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9044      * the dataset size</em>
9045      */
9046     getTotalCount : function(){
9047         return this.totalLength || 0;
9048     },
9049
9050     /**
9051      * Returns the sort state of the Store as an object with two properties:
9052      * <pre><code>
9053  field {String} The name of the field by which the Records are sorted
9054  direction {String} The sort order, "ASC" or "DESC"
9055      * </code></pre>
9056      */
9057     getSortState : function(){
9058         return this.sortInfo;
9059     },
9060
9061     // private
9062     applySort : function(){
9063         if(this.sortInfo && !this.remoteSort){
9064             var s = this.sortInfo, f = s.field;
9065             var st = this.fields.get(f).sortType;
9066             var fn = function(r1, r2){
9067                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9068                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9069             };
9070             this.data.sort(s.direction, fn);
9071             if(this.snapshot && this.snapshot != this.data){
9072                 this.snapshot.sort(s.direction, fn);
9073             }
9074         }
9075     },
9076
9077     /**
9078      * Sets the default sort column and order to be used by the next load operation.
9079      * @param {String} fieldName The name of the field to sort by.
9080      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9081      */
9082     setDefaultSort : function(field, dir){
9083         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9084     },
9085
9086     /**
9087      * Sort the Records.
9088      * If remote sorting is used, the sort is performed on the server, and the cache is
9089      * reloaded. If local sorting is used, the cache is sorted internally.
9090      * @param {String} fieldName The name of the field to sort by.
9091      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9092      */
9093     sort : function(fieldName, dir){
9094         var f = this.fields.get(fieldName);
9095         if(!dir){
9096             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9097             
9098             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9099                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9100             }else{
9101                 dir = f.sortDir;
9102             }
9103         }
9104         this.sortToggle[f.name] = dir;
9105         this.sortInfo = {field: f.name, direction: dir};
9106         if(!this.remoteSort){
9107             this.applySort();
9108             this.fireEvent("datachanged", this);
9109         }else{
9110             this.load(this.lastOptions);
9111         }
9112     },
9113
9114     /**
9115      * Calls the specified function for each of the Records in the cache.
9116      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9117      * Returning <em>false</em> aborts and exits the iteration.
9118      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9119      */
9120     each : function(fn, scope){
9121         this.data.each(fn, scope);
9122     },
9123
9124     /**
9125      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9126      * (e.g., during paging).
9127      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9128      */
9129     getModifiedRecords : function(){
9130         return this.modified;
9131     },
9132
9133     // private
9134     createFilterFn : function(property, value, anyMatch){
9135         if(!value.exec){ // not a regex
9136             value = String(value);
9137             if(value.length == 0){
9138                 return false;
9139             }
9140             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9141         }
9142         return function(r){
9143             return value.test(r.data[property]);
9144         };
9145     },
9146
9147     /**
9148      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9149      * @param {String} property A field on your records
9150      * @param {Number} start The record index to start at (defaults to 0)
9151      * @param {Number} end The last record index to include (defaults to length - 1)
9152      * @return {Number} The sum
9153      */
9154     sum : function(property, start, end){
9155         var rs = this.data.items, v = 0;
9156         start = start || 0;
9157         end = (end || end === 0) ? end : rs.length-1;
9158
9159         for(var i = start; i <= end; i++){
9160             v += (rs[i].data[property] || 0);
9161         }
9162         return v;
9163     },
9164
9165     /**
9166      * Filter the records by a specified property.
9167      * @param {String} field A field on your records
9168      * @param {String/RegExp} value Either a string that the field
9169      * should start with or a RegExp to test against the field
9170      * @param {Boolean} anyMatch True to match any part not just the beginning
9171      */
9172     filter : function(property, value, anyMatch){
9173         var fn = this.createFilterFn(property, value, anyMatch);
9174         return fn ? this.filterBy(fn) : this.clearFilter();
9175     },
9176
9177     /**
9178      * Filter by a function. The specified function will be called with each
9179      * record in this data source. If the function returns true the record is included,
9180      * otherwise it is filtered.
9181      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9182      * @param {Object} scope (optional) The scope of the function (defaults to this)
9183      */
9184     filterBy : function(fn, scope){
9185         this.snapshot = this.snapshot || this.data;
9186         this.data = this.queryBy(fn, scope||this);
9187         this.fireEvent("datachanged", this);
9188     },
9189
9190     /**
9191      * Query the records by a specified property.
9192      * @param {String} field A field on your records
9193      * @param {String/RegExp} value Either a string that the field
9194      * should start with or a RegExp to test against the field
9195      * @param {Boolean} anyMatch True to match any part not just the beginning
9196      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9197      */
9198     query : function(property, value, anyMatch){
9199         var fn = this.createFilterFn(property, value, anyMatch);
9200         return fn ? this.queryBy(fn) : this.data.clone();
9201     },
9202
9203     /**
9204      * Query by a function. The specified function will be called with each
9205      * record in this data source. If the function returns true the record is included
9206      * in the results.
9207      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9208      * @param {Object} scope (optional) The scope of the function (defaults to this)
9209       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9210      **/
9211     queryBy : function(fn, scope){
9212         var data = this.snapshot || this.data;
9213         return data.filterBy(fn, scope||this);
9214     },
9215
9216     /**
9217      * Collects unique values for a particular dataIndex from this store.
9218      * @param {String} dataIndex The property to collect
9219      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9220      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9221      * @return {Array} An array of the unique values
9222      **/
9223     collect : function(dataIndex, allowNull, bypassFilter){
9224         var d = (bypassFilter === true && this.snapshot) ?
9225                 this.snapshot.items : this.data.items;
9226         var v, sv, r = [], l = {};
9227         for(var i = 0, len = d.length; i < len; i++){
9228             v = d[i].data[dataIndex];
9229             sv = String(v);
9230             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9231                 l[sv] = true;
9232                 r[r.length] = v;
9233             }
9234         }
9235         return r;
9236     },
9237
9238     /**
9239      * Revert to a view of the Record cache with no filtering applied.
9240      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9241      */
9242     clearFilter : function(suppressEvent){
9243         if(this.snapshot && this.snapshot != this.data){
9244             this.data = this.snapshot;
9245             delete this.snapshot;
9246             if(suppressEvent !== true){
9247                 this.fireEvent("datachanged", this);
9248             }
9249         }
9250     },
9251
9252     // private
9253     afterEdit : function(record){
9254         if(this.modified.indexOf(record) == -1){
9255             this.modified.push(record);
9256         }
9257         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9258     },
9259     
9260     // private
9261     afterReject : function(record){
9262         this.modified.remove(record);
9263         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9264     },
9265
9266     // private
9267     afterCommit : function(record){
9268         this.modified.remove(record);
9269         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9270     },
9271
9272     /**
9273      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9274      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9275      */
9276     commitChanges : function(){
9277         var m = this.modified.slice(0);
9278         this.modified = [];
9279         for(var i = 0, len = m.length; i < len; i++){
9280             m[i].commit();
9281         }
9282     },
9283
9284     /**
9285      * Cancel outstanding changes on all changed records.
9286      */
9287     rejectChanges : function(){
9288         var m = this.modified.slice(0);
9289         this.modified = [];
9290         for(var i = 0, len = m.length; i < len; i++){
9291             m[i].reject();
9292         }
9293     },
9294
9295     onMetaChange : function(meta, rtype, o){
9296         this.recordType = rtype;
9297         this.fields = rtype.prototype.fields;
9298         delete this.snapshot;
9299         this.sortInfo = meta.sortInfo || this.sortInfo;
9300         this.modified = [];
9301         this.fireEvent('metachange', this, this.reader.meta);
9302     },
9303     
9304     moveIndex : function(data, type)
9305     {
9306         var index = this.indexOf(data);
9307         
9308         var newIndex = index + type;
9309         
9310         this.remove(data);
9311         
9312         this.insert(newIndex, data);
9313         
9314     }
9315 });/*
9316  * Based on:
9317  * Ext JS Library 1.1.1
9318  * Copyright(c) 2006-2007, Ext JS, LLC.
9319  *
9320  * Originally Released Under LGPL - original licence link has changed is not relivant.
9321  *
9322  * Fork - LGPL
9323  * <script type="text/javascript">
9324  */
9325
9326 /**
9327  * @class Roo.data.SimpleStore
9328  * @extends Roo.data.Store
9329  * Small helper class to make creating Stores from Array data easier.
9330  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9331  * @cfg {Array} fields An array of field definition objects, or field name strings.
9332  * @cfg {Array} data The multi-dimensional array of data
9333  * @constructor
9334  * @param {Object} config
9335  */
9336 Roo.data.SimpleStore = function(config){
9337     Roo.data.SimpleStore.superclass.constructor.call(this, {
9338         isLocal : true,
9339         reader: new Roo.data.ArrayReader({
9340                 id: config.id
9341             },
9342             Roo.data.Record.create(config.fields)
9343         ),
9344         proxy : new Roo.data.MemoryProxy(config.data)
9345     });
9346     this.load();
9347 };
9348 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9349  * Based on:
9350  * Ext JS Library 1.1.1
9351  * Copyright(c) 2006-2007, Ext JS, LLC.
9352  *
9353  * Originally Released Under LGPL - original licence link has changed is not relivant.
9354  *
9355  * Fork - LGPL
9356  * <script type="text/javascript">
9357  */
9358
9359 /**
9360 /**
9361  * @extends Roo.data.Store
9362  * @class Roo.data.JsonStore
9363  * Small helper class to make creating Stores for JSON data easier. <br/>
9364 <pre><code>
9365 var store = new Roo.data.JsonStore({
9366     url: 'get-images.php',
9367     root: 'images',
9368     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9369 });
9370 </code></pre>
9371  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9372  * JsonReader and HttpProxy (unless inline data is provided).</b>
9373  * @cfg {Array} fields An array of field definition objects, or field name strings.
9374  * @constructor
9375  * @param {Object} config
9376  */
9377 Roo.data.JsonStore = function(c){
9378     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9379         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9380         reader: new Roo.data.JsonReader(c, c.fields)
9381     }));
9382 };
9383 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9384  * Based on:
9385  * Ext JS Library 1.1.1
9386  * Copyright(c) 2006-2007, Ext JS, LLC.
9387  *
9388  * Originally Released Under LGPL - original licence link has changed is not relivant.
9389  *
9390  * Fork - LGPL
9391  * <script type="text/javascript">
9392  */
9393
9394  
9395 Roo.data.Field = function(config){
9396     if(typeof config == "string"){
9397         config = {name: config};
9398     }
9399     Roo.apply(this, config);
9400     
9401     if(!this.type){
9402         this.type = "auto";
9403     }
9404     
9405     var st = Roo.data.SortTypes;
9406     // named sortTypes are supported, here we look them up
9407     if(typeof this.sortType == "string"){
9408         this.sortType = st[this.sortType];
9409     }
9410     
9411     // set default sortType for strings and dates
9412     if(!this.sortType){
9413         switch(this.type){
9414             case "string":
9415                 this.sortType = st.asUCString;
9416                 break;
9417             case "date":
9418                 this.sortType = st.asDate;
9419                 break;
9420             default:
9421                 this.sortType = st.none;
9422         }
9423     }
9424
9425     // define once
9426     var stripRe = /[\$,%]/g;
9427
9428     // prebuilt conversion function for this field, instead of
9429     // switching every time we're reading a value
9430     if(!this.convert){
9431         var cv, dateFormat = this.dateFormat;
9432         switch(this.type){
9433             case "":
9434             case "auto":
9435             case undefined:
9436                 cv = function(v){ return v; };
9437                 break;
9438             case "string":
9439                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9440                 break;
9441             case "int":
9442                 cv = function(v){
9443                     return v !== undefined && v !== null && v !== '' ?
9444                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9445                     };
9446                 break;
9447             case "float":
9448                 cv = function(v){
9449                     return v !== undefined && v !== null && v !== '' ?
9450                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9451                     };
9452                 break;
9453             case "bool":
9454             case "boolean":
9455                 cv = function(v){ return v === true || v === "true" || v == 1; };
9456                 break;
9457             case "date":
9458                 cv = function(v){
9459                     if(!v){
9460                         return '';
9461                     }
9462                     if(v instanceof Date){
9463                         return v;
9464                     }
9465                     if(dateFormat){
9466                         if(dateFormat == "timestamp"){
9467                             return new Date(v*1000);
9468                         }
9469                         return Date.parseDate(v, dateFormat);
9470                     }
9471                     var parsed = Date.parse(v);
9472                     return parsed ? new Date(parsed) : null;
9473                 };
9474              break;
9475             
9476         }
9477         this.convert = cv;
9478     }
9479 };
9480
9481 Roo.data.Field.prototype = {
9482     dateFormat: null,
9483     defaultValue: "",
9484     mapping: null,
9485     sortType : null,
9486     sortDir : "ASC"
9487 };/*
9488  * Based on:
9489  * Ext JS Library 1.1.1
9490  * Copyright(c) 2006-2007, Ext JS, LLC.
9491  *
9492  * Originally Released Under LGPL - original licence link has changed is not relivant.
9493  *
9494  * Fork - LGPL
9495  * <script type="text/javascript">
9496  */
9497  
9498 // Base class for reading structured data from a data source.  This class is intended to be
9499 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9500
9501 /**
9502  * @class Roo.data.DataReader
9503  * Base class for reading structured data from a data source.  This class is intended to be
9504  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9505  */
9506
9507 Roo.data.DataReader = function(meta, recordType){
9508     
9509     this.meta = meta;
9510     
9511     this.recordType = recordType instanceof Array ? 
9512         Roo.data.Record.create(recordType) : recordType;
9513 };
9514
9515 Roo.data.DataReader.prototype = {
9516      /**
9517      * Create an empty record
9518      * @param {Object} data (optional) - overlay some values
9519      * @return {Roo.data.Record} record created.
9520      */
9521     newRow :  function(d) {
9522         var da =  {};
9523         this.recordType.prototype.fields.each(function(c) {
9524             switch( c.type) {
9525                 case 'int' : da[c.name] = 0; break;
9526                 case 'date' : da[c.name] = new Date(); break;
9527                 case 'float' : da[c.name] = 0.0; break;
9528                 case 'boolean' : da[c.name] = false; break;
9529                 default : da[c.name] = ""; break;
9530             }
9531             
9532         });
9533         return new this.recordType(Roo.apply(da, d));
9534     }
9535     
9536 };/*
9537  * Based on:
9538  * Ext JS Library 1.1.1
9539  * Copyright(c) 2006-2007, Ext JS, LLC.
9540  *
9541  * Originally Released Under LGPL - original licence link has changed is not relivant.
9542  *
9543  * Fork - LGPL
9544  * <script type="text/javascript">
9545  */
9546
9547 /**
9548  * @class Roo.data.DataProxy
9549  * @extends Roo.data.Observable
9550  * This class is an abstract base class for implementations which provide retrieval of
9551  * unformatted data objects.<br>
9552  * <p>
9553  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9554  * (of the appropriate type which knows how to parse the data object) to provide a block of
9555  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9556  * <p>
9557  * Custom implementations must implement the load method as described in
9558  * {@link Roo.data.HttpProxy#load}.
9559  */
9560 Roo.data.DataProxy = function(){
9561     this.addEvents({
9562         /**
9563          * @event beforeload
9564          * Fires before a network request is made to retrieve a data object.
9565          * @param {Object} This DataProxy object.
9566          * @param {Object} params The params parameter to the load function.
9567          */
9568         beforeload : true,
9569         /**
9570          * @event load
9571          * Fires before the load method's callback is called.
9572          * @param {Object} This DataProxy object.
9573          * @param {Object} o The data object.
9574          * @param {Object} arg The callback argument object passed to the load function.
9575          */
9576         load : true,
9577         /**
9578          * @event loadexception
9579          * Fires if an Exception occurs during data retrieval.
9580          * @param {Object} This DataProxy object.
9581          * @param {Object} o The data object.
9582          * @param {Object} arg The callback argument object passed to the load function.
9583          * @param {Object} e The Exception.
9584          */
9585         loadexception : true
9586     });
9587     Roo.data.DataProxy.superclass.constructor.call(this);
9588 };
9589
9590 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9591
9592     /**
9593      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9594      */
9595 /*
9596  * Based on:
9597  * Ext JS Library 1.1.1
9598  * Copyright(c) 2006-2007, Ext JS, LLC.
9599  *
9600  * Originally Released Under LGPL - original licence link has changed is not relivant.
9601  *
9602  * Fork - LGPL
9603  * <script type="text/javascript">
9604  */
9605 /**
9606  * @class Roo.data.MemoryProxy
9607  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9608  * to the Reader when its load method is called.
9609  * @constructor
9610  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9611  */
9612 Roo.data.MemoryProxy = function(data){
9613     if (data.data) {
9614         data = data.data;
9615     }
9616     Roo.data.MemoryProxy.superclass.constructor.call(this);
9617     this.data = data;
9618 };
9619
9620 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9621     /**
9622      * Load data from the requested source (in this case an in-memory
9623      * data object passed to the constructor), read the data object into
9624      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9625      * process that block using the passed callback.
9626      * @param {Object} params This parameter is not used by the MemoryProxy class.
9627      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9628      * object into a block of Roo.data.Records.
9629      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9630      * The function must be passed <ul>
9631      * <li>The Record block object</li>
9632      * <li>The "arg" argument from the load function</li>
9633      * <li>A boolean success indicator</li>
9634      * </ul>
9635      * @param {Object} scope The scope in which to call the callback
9636      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9637      */
9638     load : function(params, reader, callback, scope, arg){
9639         params = params || {};
9640         var result;
9641         try {
9642             result = reader.readRecords(this.data);
9643         }catch(e){
9644             this.fireEvent("loadexception", this, arg, null, e);
9645             callback.call(scope, null, arg, false);
9646             return;
9647         }
9648         callback.call(scope, result, arg, true);
9649     },
9650     
9651     // private
9652     update : function(params, records){
9653         
9654     }
9655 });/*
9656  * Based on:
9657  * Ext JS Library 1.1.1
9658  * Copyright(c) 2006-2007, Ext JS, LLC.
9659  *
9660  * Originally Released Under LGPL - original licence link has changed is not relivant.
9661  *
9662  * Fork - LGPL
9663  * <script type="text/javascript">
9664  */
9665 /**
9666  * @class Roo.data.HttpProxy
9667  * @extends Roo.data.DataProxy
9668  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9669  * configured to reference a certain URL.<br><br>
9670  * <p>
9671  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9672  * from which the running page was served.<br><br>
9673  * <p>
9674  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9675  * <p>
9676  * Be aware that to enable the browser to parse an XML document, the server must set
9677  * the Content-Type header in the HTTP response to "text/xml".
9678  * @constructor
9679  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9680  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9681  * will be used to make the request.
9682  */
9683 Roo.data.HttpProxy = function(conn){
9684     Roo.data.HttpProxy.superclass.constructor.call(this);
9685     // is conn a conn config or a real conn?
9686     this.conn = conn;
9687     this.useAjax = !conn || !conn.events;
9688   
9689 };
9690
9691 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9692     // thse are take from connection...
9693     
9694     /**
9695      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9696      */
9697     /**
9698      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9699      * extra parameters to each request made by this object. (defaults to undefined)
9700      */
9701     /**
9702      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9703      *  to each request made by this object. (defaults to undefined)
9704      */
9705     /**
9706      * @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)
9707      */
9708     /**
9709      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9710      */
9711      /**
9712      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9713      * @type Boolean
9714      */
9715   
9716
9717     /**
9718      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9719      * @type Boolean
9720      */
9721     /**
9722      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9723      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9724      * a finer-grained basis than the DataProxy events.
9725      */
9726     getConnection : function(){
9727         return this.useAjax ? Roo.Ajax : this.conn;
9728     },
9729
9730     /**
9731      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9732      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9733      * process that block using the passed callback.
9734      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9735      * for the request to the remote server.
9736      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9737      * object into a block of Roo.data.Records.
9738      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9739      * The function must be passed <ul>
9740      * <li>The Record block object</li>
9741      * <li>The "arg" argument from the load function</li>
9742      * <li>A boolean success indicator</li>
9743      * </ul>
9744      * @param {Object} scope The scope in which to call the callback
9745      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9746      */
9747     load : function(params, reader, callback, scope, arg){
9748         if(this.fireEvent("beforeload", this, params) !== false){
9749             var  o = {
9750                 params : params || {},
9751                 request: {
9752                     callback : callback,
9753                     scope : scope,
9754                     arg : arg
9755                 },
9756                 reader: reader,
9757                 callback : this.loadResponse,
9758                 scope: this
9759             };
9760             if(this.useAjax){
9761                 Roo.applyIf(o, this.conn);
9762                 if(this.activeRequest){
9763                     Roo.Ajax.abort(this.activeRequest);
9764                 }
9765                 this.activeRequest = Roo.Ajax.request(o);
9766             }else{
9767                 this.conn.request(o);
9768             }
9769         }else{
9770             callback.call(scope||this, null, arg, false);
9771         }
9772     },
9773
9774     // private
9775     loadResponse : function(o, success, response){
9776         delete this.activeRequest;
9777         if(!success){
9778             this.fireEvent("loadexception", this, o, response);
9779             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9780             return;
9781         }
9782         var result;
9783         try {
9784             result = o.reader.read(response);
9785         }catch(e){
9786             this.fireEvent("loadexception", this, o, response, e);
9787             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9788             return;
9789         }
9790         
9791         this.fireEvent("load", this, o, o.request.arg);
9792         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9793     },
9794
9795     // private
9796     update : function(dataSet){
9797
9798     },
9799
9800     // private
9801     updateResponse : function(dataSet){
9802
9803     }
9804 });/*
9805  * Based on:
9806  * Ext JS Library 1.1.1
9807  * Copyright(c) 2006-2007, Ext JS, LLC.
9808  *
9809  * Originally Released Under LGPL - original licence link has changed is not relivant.
9810  *
9811  * Fork - LGPL
9812  * <script type="text/javascript">
9813  */
9814
9815 /**
9816  * @class Roo.data.ScriptTagProxy
9817  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9818  * other than the originating domain of the running page.<br><br>
9819  * <p>
9820  * <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
9821  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9822  * <p>
9823  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9824  * source code that is used as the source inside a &lt;script> tag.<br><br>
9825  * <p>
9826  * In order for the browser to process the returned data, the server must wrap the data object
9827  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9828  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9829  * depending on whether the callback name was passed:
9830  * <p>
9831  * <pre><code>
9832 boolean scriptTag = false;
9833 String cb = request.getParameter("callback");
9834 if (cb != null) {
9835     scriptTag = true;
9836     response.setContentType("text/javascript");
9837 } else {
9838     response.setContentType("application/x-json");
9839 }
9840 Writer out = response.getWriter();
9841 if (scriptTag) {
9842     out.write(cb + "(");
9843 }
9844 out.print(dataBlock.toJsonString());
9845 if (scriptTag) {
9846     out.write(");");
9847 }
9848 </pre></code>
9849  *
9850  * @constructor
9851  * @param {Object} config A configuration object.
9852  */
9853 Roo.data.ScriptTagProxy = function(config){
9854     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9855     Roo.apply(this, config);
9856     this.head = document.getElementsByTagName("head")[0];
9857 };
9858
9859 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9860
9861 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9862     /**
9863      * @cfg {String} url The URL from which to request the data object.
9864      */
9865     /**
9866      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9867      */
9868     timeout : 30000,
9869     /**
9870      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9871      * the server the name of the callback function set up by the load call to process the returned data object.
9872      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9873      * javascript output which calls this named function passing the data object as its only parameter.
9874      */
9875     callbackParam : "callback",
9876     /**
9877      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9878      * name to the request.
9879      */
9880     nocache : true,
9881
9882     /**
9883      * Load data from the configured URL, read the data object into
9884      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9885      * process that block using the passed callback.
9886      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9887      * for the request to the remote server.
9888      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9889      * object into a block of Roo.data.Records.
9890      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9891      * The function must be passed <ul>
9892      * <li>The Record block object</li>
9893      * <li>The "arg" argument from the load function</li>
9894      * <li>A boolean success indicator</li>
9895      * </ul>
9896      * @param {Object} scope The scope in which to call the callback
9897      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9898      */
9899     load : function(params, reader, callback, scope, arg){
9900         if(this.fireEvent("beforeload", this, params) !== false){
9901
9902             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9903
9904             var url = this.url;
9905             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9906             if(this.nocache){
9907                 url += "&_dc=" + (new Date().getTime());
9908             }
9909             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9910             var trans = {
9911                 id : transId,
9912                 cb : "stcCallback"+transId,
9913                 scriptId : "stcScript"+transId,
9914                 params : params,
9915                 arg : arg,
9916                 url : url,
9917                 callback : callback,
9918                 scope : scope,
9919                 reader : reader
9920             };
9921             var conn = this;
9922
9923             window[trans.cb] = function(o){
9924                 conn.handleResponse(o, trans);
9925             };
9926
9927             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9928
9929             if(this.autoAbort !== false){
9930                 this.abort();
9931             }
9932
9933             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9934
9935             var script = document.createElement("script");
9936             script.setAttribute("src", url);
9937             script.setAttribute("type", "text/javascript");
9938             script.setAttribute("id", trans.scriptId);
9939             this.head.appendChild(script);
9940
9941             this.trans = trans;
9942         }else{
9943             callback.call(scope||this, null, arg, false);
9944         }
9945     },
9946
9947     // private
9948     isLoading : function(){
9949         return this.trans ? true : false;
9950     },
9951
9952     /**
9953      * Abort the current server request.
9954      */
9955     abort : function(){
9956         if(this.isLoading()){
9957             this.destroyTrans(this.trans);
9958         }
9959     },
9960
9961     // private
9962     destroyTrans : function(trans, isLoaded){
9963         this.head.removeChild(document.getElementById(trans.scriptId));
9964         clearTimeout(trans.timeoutId);
9965         if(isLoaded){
9966             window[trans.cb] = undefined;
9967             try{
9968                 delete window[trans.cb];
9969             }catch(e){}
9970         }else{
9971             // if hasn't been loaded, wait for load to remove it to prevent script error
9972             window[trans.cb] = function(){
9973                 window[trans.cb] = undefined;
9974                 try{
9975                     delete window[trans.cb];
9976                 }catch(e){}
9977             };
9978         }
9979     },
9980
9981     // private
9982     handleResponse : function(o, trans){
9983         this.trans = false;
9984         this.destroyTrans(trans, true);
9985         var result;
9986         try {
9987             result = trans.reader.readRecords(o);
9988         }catch(e){
9989             this.fireEvent("loadexception", this, o, trans.arg, e);
9990             trans.callback.call(trans.scope||window, null, trans.arg, false);
9991             return;
9992         }
9993         this.fireEvent("load", this, o, trans.arg);
9994         trans.callback.call(trans.scope||window, result, trans.arg, true);
9995     },
9996
9997     // private
9998     handleFailure : function(trans){
9999         this.trans = false;
10000         this.destroyTrans(trans, false);
10001         this.fireEvent("loadexception", this, null, trans.arg);
10002         trans.callback.call(trans.scope||window, null, trans.arg, false);
10003     }
10004 });/*
10005  * Based on:
10006  * Ext JS Library 1.1.1
10007  * Copyright(c) 2006-2007, Ext JS, LLC.
10008  *
10009  * Originally Released Under LGPL - original licence link has changed is not relivant.
10010  *
10011  * Fork - LGPL
10012  * <script type="text/javascript">
10013  */
10014
10015 /**
10016  * @class Roo.data.JsonReader
10017  * @extends Roo.data.DataReader
10018  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10019  * based on mappings in a provided Roo.data.Record constructor.
10020  * 
10021  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10022  * in the reply previously. 
10023  * 
10024  * <p>
10025  * Example code:
10026  * <pre><code>
10027 var RecordDef = Roo.data.Record.create([
10028     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10029     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10030 ]);
10031 var myReader = new Roo.data.JsonReader({
10032     totalProperty: "results",    // The property which contains the total dataset size (optional)
10033     root: "rows",                // The property which contains an Array of row objects
10034     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10035 }, RecordDef);
10036 </code></pre>
10037  * <p>
10038  * This would consume a JSON file like this:
10039  * <pre><code>
10040 { 'results': 2, 'rows': [
10041     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10042     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10043 }
10044 </code></pre>
10045  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10046  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10047  * paged from the remote server.
10048  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10049  * @cfg {String} root name of the property which contains the Array of row objects.
10050  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10051  * @constructor
10052  * Create a new JsonReader
10053  * @param {Object} meta Metadata configuration options
10054  * @param {Object} recordType Either an Array of field definition objects,
10055  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10056  */
10057 Roo.data.JsonReader = function(meta, recordType){
10058     
10059     meta = meta || {};
10060     // set some defaults:
10061     Roo.applyIf(meta, {
10062         totalProperty: 'total',
10063         successProperty : 'success',
10064         root : 'data',
10065         id : 'id'
10066     });
10067     
10068     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10069 };
10070 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10071     
10072     /**
10073      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10074      * Used by Store query builder to append _requestMeta to params.
10075      * 
10076      */
10077     metaFromRemote : false,
10078     /**
10079      * This method is only used by a DataProxy which has retrieved data from a remote server.
10080      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10081      * @return {Object} data A data block which is used by an Roo.data.Store object as
10082      * a cache of Roo.data.Records.
10083      */
10084     read : function(response){
10085         var json = response.responseText;
10086        
10087         var o = /* eval:var:o */ eval("("+json+")");
10088         if(!o) {
10089             throw {message: "JsonReader.read: Json object not found"};
10090         }
10091         
10092         if(o.metaData){
10093             
10094             delete this.ef;
10095             this.metaFromRemote = true;
10096             this.meta = o.metaData;
10097             this.recordType = Roo.data.Record.create(o.metaData.fields);
10098             this.onMetaChange(this.meta, this.recordType, o);
10099         }
10100         return this.readRecords(o);
10101     },
10102
10103     // private function a store will implement
10104     onMetaChange : function(meta, recordType, o){
10105
10106     },
10107
10108     /**
10109          * @ignore
10110          */
10111     simpleAccess: function(obj, subsc) {
10112         return obj[subsc];
10113     },
10114
10115         /**
10116          * @ignore
10117          */
10118     getJsonAccessor: function(){
10119         var re = /[\[\.]/;
10120         return function(expr) {
10121             try {
10122                 return(re.test(expr))
10123                     ? new Function("obj", "return obj." + expr)
10124                     : function(obj){
10125                         return obj[expr];
10126                     };
10127             } catch(e){}
10128             return Roo.emptyFn;
10129         };
10130     }(),
10131
10132     /**
10133      * Create a data block containing Roo.data.Records from an XML document.
10134      * @param {Object} o An object which contains an Array of row objects in the property specified
10135      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10136      * which contains the total size of the dataset.
10137      * @return {Object} data A data block which is used by an Roo.data.Store object as
10138      * a cache of Roo.data.Records.
10139      */
10140     readRecords : function(o){
10141         /**
10142          * After any data loads, the raw JSON data is available for further custom processing.
10143          * @type Object
10144          */
10145         this.o = o;
10146         var s = this.meta, Record = this.recordType,
10147             f = Record.prototype.fields, fi = f.items, fl = f.length;
10148
10149 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10150         if (!this.ef) {
10151             if(s.totalProperty) {
10152                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10153                 }
10154                 if(s.successProperty) {
10155                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10156                 }
10157                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10158                 if (s.id) {
10159                         var g = this.getJsonAccessor(s.id);
10160                         this.getId = function(rec) {
10161                                 var r = g(rec);  
10162                                 return (r === undefined || r === "") ? null : r;
10163                         };
10164                 } else {
10165                         this.getId = function(){return null;};
10166                 }
10167             this.ef = [];
10168             for(var jj = 0; jj < fl; jj++){
10169                 f = fi[jj];
10170                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10171                 this.ef[jj] = this.getJsonAccessor(map);
10172             }
10173         }
10174
10175         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10176         if(s.totalProperty){
10177             var vt = parseInt(this.getTotal(o), 10);
10178             if(!isNaN(vt)){
10179                 totalRecords = vt;
10180             }
10181         }
10182         if(s.successProperty){
10183             var vs = this.getSuccess(o);
10184             if(vs === false || vs === 'false'){
10185                 success = false;
10186             }
10187         }
10188         var records = [];
10189             for(var i = 0; i < c; i++){
10190                     var n = root[i];
10191                 var values = {};
10192                 var id = this.getId(n);
10193                 for(var j = 0; j < fl; j++){
10194                     f = fi[j];
10195                 var v = this.ef[j](n);
10196                 if (!f.convert) {
10197                     Roo.log('missing convert for ' + f.name);
10198                     Roo.log(f);
10199                     continue;
10200                 }
10201                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10202                 }
10203                 var record = new Record(values, id);
10204                 record.json = n;
10205                 records[i] = record;
10206             }
10207             return {
10208             raw : o,
10209                 success : success,
10210                 records : records,
10211                 totalRecords : totalRecords
10212             };
10213     }
10214 });/*
10215  * Based on:
10216  * Ext JS Library 1.1.1
10217  * Copyright(c) 2006-2007, Ext JS, LLC.
10218  *
10219  * Originally Released Under LGPL - original licence link has changed is not relivant.
10220  *
10221  * Fork - LGPL
10222  * <script type="text/javascript">
10223  */
10224
10225 /**
10226  * @class Roo.data.ArrayReader
10227  * @extends Roo.data.DataReader
10228  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10229  * Each element of that Array represents a row of data fields. The
10230  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10231  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10232  * <p>
10233  * Example code:.
10234  * <pre><code>
10235 var RecordDef = Roo.data.Record.create([
10236     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10237     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10238 ]);
10239 var myReader = new Roo.data.ArrayReader({
10240     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10241 }, RecordDef);
10242 </code></pre>
10243  * <p>
10244  * This would consume an Array like this:
10245  * <pre><code>
10246 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10247   </code></pre>
10248  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10249  * @constructor
10250  * Create a new JsonReader
10251  * @param {Object} meta Metadata configuration options.
10252  * @param {Object} recordType Either an Array of field definition objects
10253  * as specified to {@link Roo.data.Record#create},
10254  * or an {@link Roo.data.Record} object
10255  * created using {@link Roo.data.Record#create}.
10256  */
10257 Roo.data.ArrayReader = function(meta, recordType){
10258     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10259 };
10260
10261 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10262     /**
10263      * Create a data block containing Roo.data.Records from an XML document.
10264      * @param {Object} o An Array of row objects which represents the dataset.
10265      * @return {Object} data A data block which is used by an Roo.data.Store object as
10266      * a cache of Roo.data.Records.
10267      */
10268     readRecords : function(o){
10269         var sid = this.meta ? this.meta.id : null;
10270         var recordType = this.recordType, fields = recordType.prototype.fields;
10271         var records = [];
10272         var root = o;
10273             for(var i = 0; i < root.length; i++){
10274                     var n = root[i];
10275                 var values = {};
10276                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10277                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10278                 var f = fields.items[j];
10279                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10280                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10281                 v = f.convert(v);
10282                 values[f.name] = v;
10283             }
10284                 var record = new recordType(values, id);
10285                 record.json = n;
10286                 records[records.length] = record;
10287             }
10288             return {
10289                 records : records,
10290                 totalRecords : records.length
10291             };
10292     }
10293 });/*
10294  * - LGPL
10295  * * 
10296  */
10297
10298 /**
10299  * @class Roo.bootstrap.ComboBox
10300  * @extends Roo.bootstrap.TriggerField
10301  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10302  * @cfg {Boolean} append (true|false) default false
10303  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10304  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10305  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10306  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10307  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10308  * @constructor
10309  * Create a new ComboBox.
10310  * @param {Object} config Configuration options
10311  */
10312 Roo.bootstrap.ComboBox = function(config){
10313     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10314     this.addEvents({
10315         /**
10316          * @event expand
10317          * Fires when the dropdown list is expanded
10318              * @param {Roo.bootstrap.ComboBox} combo This combo box
10319              */
10320         'expand' : true,
10321         /**
10322          * @event collapse
10323          * Fires when the dropdown list is collapsed
10324              * @param {Roo.bootstrap.ComboBox} combo This combo box
10325              */
10326         'collapse' : true,
10327         /**
10328          * @event beforeselect
10329          * Fires before a list item is selected. Return false to cancel the selection.
10330              * @param {Roo.bootstrap.ComboBox} combo This combo box
10331              * @param {Roo.data.Record} record The data record returned from the underlying store
10332              * @param {Number} index The index of the selected item in the dropdown list
10333              */
10334         'beforeselect' : true,
10335         /**
10336          * @event select
10337          * Fires when a list item is selected
10338              * @param {Roo.bootstrap.ComboBox} combo This combo box
10339              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10340              * @param {Number} index The index of the selected item in the dropdown list
10341              */
10342         'select' : true,
10343         /**
10344          * @event beforequery
10345          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10346          * The event object passed has these properties:
10347              * @param {Roo.bootstrap.ComboBox} combo This combo box
10348              * @param {String} query The query
10349              * @param {Boolean} forceAll true to force "all" query
10350              * @param {Boolean} cancel true to cancel the query
10351              * @param {Object} e The query event object
10352              */
10353         'beforequery': true,
10354          /**
10355          * @event add
10356          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10357              * @param {Roo.bootstrap.ComboBox} combo This combo box
10358              */
10359         'add' : true,
10360         /**
10361          * @event edit
10362          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10363              * @param {Roo.bootstrap.ComboBox} combo This combo box
10364              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10365              */
10366         'edit' : true,
10367         /**
10368          * @event remove
10369          * Fires when the remove value from the combobox array
10370              * @param {Roo.bootstrap.ComboBox} combo This combo box
10371              */
10372         'remove' : true
10373         
10374     });
10375     
10376     this.item = [];
10377     this.tickItems = [];
10378     
10379     this.selectedIndex = -1;
10380     if(this.mode == 'local'){
10381         if(config.queryDelay === undefined){
10382             this.queryDelay = 10;
10383         }
10384         if(config.minChars === undefined){
10385             this.minChars = 0;
10386         }
10387     }
10388 };
10389
10390 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10391      
10392     /**
10393      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10394      * rendering into an Roo.Editor, defaults to false)
10395      */
10396     /**
10397      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10398      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10399      */
10400     /**
10401      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10402      */
10403     /**
10404      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10405      * the dropdown list (defaults to undefined, with no header element)
10406      */
10407
10408      /**
10409      * @cfg {String/Roo.Template} tpl The template to use to render the output
10410      */
10411      
10412      /**
10413      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10414      */
10415     listWidth: undefined,
10416     /**
10417      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10418      * mode = 'remote' or 'text' if mode = 'local')
10419      */
10420     displayField: undefined,
10421     /**
10422      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10423      * mode = 'remote' or 'value' if mode = 'local'). 
10424      * Note: use of a valueField requires the user make a selection
10425      * in order for a value to be mapped.
10426      */
10427     valueField: undefined,
10428     
10429     
10430     /**
10431      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10432      * field's data value (defaults to the underlying DOM element's name)
10433      */
10434     hiddenName: undefined,
10435     /**
10436      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10437      */
10438     listClass: '',
10439     /**
10440      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10441      */
10442     selectedClass: 'active',
10443     
10444     /**
10445      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10446      */
10447     shadow:'sides',
10448     /**
10449      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10450      * anchor positions (defaults to 'tl-bl')
10451      */
10452     listAlign: 'tl-bl?',
10453     /**
10454      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10455      */
10456     maxHeight: 300,
10457     /**
10458      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10459      * query specified by the allQuery config option (defaults to 'query')
10460      */
10461     triggerAction: 'query',
10462     /**
10463      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10464      * (defaults to 4, does not apply if editable = false)
10465      */
10466     minChars : 4,
10467     /**
10468      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10469      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10470      */
10471     typeAhead: false,
10472     /**
10473      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10474      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10475      */
10476     queryDelay: 500,
10477     /**
10478      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10479      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10480      */
10481     pageSize: 0,
10482     /**
10483      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10484      * when editable = true (defaults to false)
10485      */
10486     selectOnFocus:false,
10487     /**
10488      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10489      */
10490     queryParam: 'query',
10491     /**
10492      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10493      * when mode = 'remote' (defaults to 'Loading...')
10494      */
10495     loadingText: 'Loading...',
10496     /**
10497      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10498      */
10499     resizable: false,
10500     /**
10501      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10502      */
10503     handleHeight : 8,
10504     /**
10505      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10506      * traditional select (defaults to true)
10507      */
10508     editable: true,
10509     /**
10510      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10511      */
10512     allQuery: '',
10513     /**
10514      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10515      */
10516     mode: 'remote',
10517     /**
10518      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10519      * listWidth has a higher value)
10520      */
10521     minListWidth : 70,
10522     /**
10523      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10524      * allow the user to set arbitrary text into the field (defaults to false)
10525      */
10526     forceSelection:false,
10527     /**
10528      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10529      * if typeAhead = true (defaults to 250)
10530      */
10531     typeAheadDelay : 250,
10532     /**
10533      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10534      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10535      */
10536     valueNotFoundText : undefined,
10537     /**
10538      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10539      */
10540     blockFocus : false,
10541     
10542     /**
10543      * @cfg {Boolean} disableClear Disable showing of clear button.
10544      */
10545     disableClear : false,
10546     /**
10547      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10548      */
10549     alwaysQuery : false,
10550     
10551     /**
10552      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10553      */
10554     multiple : false,
10555     
10556     //private
10557     addicon : false,
10558     editicon: false,
10559     
10560     page: 0,
10561     hasQuery: false,
10562     append: false,
10563     loadNext: false,
10564     autoFocus : true,
10565     tickable : false,
10566     btnPosition : 'right',
10567     triggerList : true,
10568     showToggleBtn : true,
10569     // element that contains real text value.. (when hidden is used..)
10570     
10571     getAutoCreate : function()
10572     {
10573         var cfg = false;
10574         
10575         /*
10576          *  Normal ComboBox
10577          */
10578         if(!this.tickable){
10579             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10580             return cfg;
10581         }
10582         
10583         /*
10584          *  ComboBox with tickable selections
10585          */
10586              
10587         var align = this.labelAlign || this.parentLabelAlign();
10588         
10589         cfg = {
10590             cls : 'form-group roo-combobox-tickable' //input-group
10591         };
10592         
10593         
10594         var buttons = {
10595             tag : 'div',
10596             cls : 'tickable-buttons',
10597             cn : [
10598                 {
10599                     tag : 'button',
10600                     type : 'button',
10601                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10602                     html : 'Edit'
10603                 },
10604                 {
10605                     tag : 'button',
10606                     type : 'button',
10607                     name : 'ok',
10608                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10609                     html : 'Done'
10610                 },
10611                 {
10612                     tag : 'button',
10613                     type : 'button',
10614                     name : 'cancel',
10615                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10616                     html : 'Cancel'
10617                 }
10618             ]
10619         };
10620         
10621         var _this = this;
10622         Roo.each(buttons.cn, function(c){
10623             if (_this.size) {
10624                 c.cls += ' btn-' + _this.size;
10625             }
10626
10627             if (_this.disabled) {
10628                 c.disabled = true;
10629             }
10630         });
10631         
10632         var box = {
10633             tag: 'div',
10634             cn: [
10635                 {
10636                     tag: 'input',
10637                     type : 'hidden',
10638                     cls: 'form-hidden-field'
10639                 },
10640                 {
10641                     tag: 'ul',
10642                     cls: 'select2-choices',
10643                     cn:[
10644                         {
10645                             tag: 'li',
10646                             cls: 'select2-search-field',
10647                             cn: [
10648
10649                                 buttons
10650                             ]
10651                         }
10652                     ]
10653                 }
10654             ]
10655         }
10656         
10657         var combobox = {
10658             cls: 'select2-container input-group select2-container-multi',
10659             cn: [
10660                 box
10661 //                {
10662 //                    tag: 'ul',
10663 //                    cls: 'typeahead typeahead-long dropdown-menu',
10664 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10665 //                }
10666             ]
10667         };
10668         
10669         if (align ==='left' && this.fieldLabel.length) {
10670             
10671                 Roo.log("left and has label");
10672                 cfg.cn = [
10673                     
10674                     {
10675                         tag: 'label',
10676                         'for' :  id,
10677                         cls : 'control-label col-sm-' + this.labelWidth,
10678                         html : this.fieldLabel
10679                         
10680                     },
10681                     {
10682                         cls : "col-sm-" + (12 - this.labelWidth), 
10683                         cn: [
10684                             combobox
10685                         ]
10686                     }
10687                     
10688                 ];
10689         } else if ( this.fieldLabel.length) {
10690                 Roo.log(" label");
10691                  cfg.cn = [
10692                    
10693                     {
10694                         tag: 'label',
10695                         //cls : 'input-group-addon',
10696                         html : this.fieldLabel
10697                         
10698                     },
10699                     
10700                     combobox
10701                     
10702                 ];
10703
10704         } else {
10705             
10706                 Roo.log(" no label && no align");
10707                 cfg = combobox
10708                      
10709                 
10710         }
10711          
10712         var settings=this;
10713         ['xs','sm','md','lg'].map(function(size){
10714             if (settings[size]) {
10715                 cfg.cls += ' col-' + size + '-' + settings[size];
10716             }
10717         });
10718         
10719         return cfg;
10720         
10721     },
10722     
10723     // private
10724     initEvents: function()
10725     {
10726         
10727         if (!this.store) {
10728             throw "can not find store for combo";
10729         }
10730         this.store = Roo.factory(this.store, Roo.data);
10731         
10732         if(this.tickable){
10733             this.initTickableEvents();
10734             return;
10735         }
10736         
10737         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10738         
10739         if(this.hiddenName){
10740             
10741             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10742             
10743             this.hiddenField.dom.value =
10744                 this.hiddenValue !== undefined ? this.hiddenValue :
10745                 this.value !== undefined ? this.value : '';
10746
10747             // prevent input submission
10748             this.el.dom.removeAttribute('name');
10749             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10750              
10751              
10752         }
10753         //if(Roo.isGecko){
10754         //    this.el.dom.setAttribute('autocomplete', 'off');
10755         //}
10756         
10757         var cls = 'x-combo-list';
10758         
10759         //this.list = new Roo.Layer({
10760         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10761         //});
10762         
10763         var _this = this;
10764         
10765         (function(){
10766             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10767             _this.list.setWidth(lw);
10768         }).defer(100);
10769         
10770         this.list.on('mouseover', this.onViewOver, this);
10771         this.list.on('mousemove', this.onViewMove, this);
10772         
10773         this.list.on('scroll', this.onViewScroll, this);
10774         
10775         /*
10776         this.list.swallowEvent('mousewheel');
10777         this.assetHeight = 0;
10778
10779         if(this.title){
10780             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10781             this.assetHeight += this.header.getHeight();
10782         }
10783
10784         this.innerList = this.list.createChild({cls:cls+'-inner'});
10785         this.innerList.on('mouseover', this.onViewOver, this);
10786         this.innerList.on('mousemove', this.onViewMove, this);
10787         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10788         
10789         if(this.allowBlank && !this.pageSize && !this.disableClear){
10790             this.footer = this.list.createChild({cls:cls+'-ft'});
10791             this.pageTb = new Roo.Toolbar(this.footer);
10792            
10793         }
10794         if(this.pageSize){
10795             this.footer = this.list.createChild({cls:cls+'-ft'});
10796             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10797                     {pageSize: this.pageSize});
10798             
10799         }
10800         
10801         if (this.pageTb && this.allowBlank && !this.disableClear) {
10802             var _this = this;
10803             this.pageTb.add(new Roo.Toolbar.Fill(), {
10804                 cls: 'x-btn-icon x-btn-clear',
10805                 text: '&#160;',
10806                 handler: function()
10807                 {
10808                     _this.collapse();
10809                     _this.clearValue();
10810                     _this.onSelect(false, -1);
10811                 }
10812             });
10813         }
10814         if (this.footer) {
10815             this.assetHeight += this.footer.getHeight();
10816         }
10817         */
10818             
10819         if(!this.tpl){
10820             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10821         }
10822
10823         this.view = new Roo.View(this.list, this.tpl, {
10824             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10825         });
10826         //this.view.wrapEl.setDisplayed(false);
10827         this.view.on('click', this.onViewClick, this);
10828         
10829         
10830         
10831         this.store.on('beforeload', this.onBeforeLoad, this);
10832         this.store.on('load', this.onLoad, this);
10833         this.store.on('loadexception', this.onLoadException, this);
10834         /*
10835         if(this.resizable){
10836             this.resizer = new Roo.Resizable(this.list,  {
10837                pinned:true, handles:'se'
10838             });
10839             this.resizer.on('resize', function(r, w, h){
10840                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10841                 this.listWidth = w;
10842                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10843                 this.restrictHeight();
10844             }, this);
10845             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10846         }
10847         */
10848         if(!this.editable){
10849             this.editable = true;
10850             this.setEditable(false);
10851         }
10852         
10853         /*
10854         
10855         if (typeof(this.events.add.listeners) != 'undefined') {
10856             
10857             this.addicon = this.wrap.createChild(
10858                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10859        
10860             this.addicon.on('click', function(e) {
10861                 this.fireEvent('add', this);
10862             }, this);
10863         }
10864         if (typeof(this.events.edit.listeners) != 'undefined') {
10865             
10866             this.editicon = this.wrap.createChild(
10867                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10868             if (this.addicon) {
10869                 this.editicon.setStyle('margin-left', '40px');
10870             }
10871             this.editicon.on('click', function(e) {
10872                 
10873                 // we fire even  if inothing is selected..
10874                 this.fireEvent('edit', this, this.lastData );
10875                 
10876             }, this);
10877         }
10878         */
10879         
10880         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10881             "up" : function(e){
10882                 this.inKeyMode = true;
10883                 this.selectPrev();
10884             },
10885
10886             "down" : function(e){
10887                 if(!this.isExpanded()){
10888                     this.onTriggerClick();
10889                 }else{
10890                     this.inKeyMode = true;
10891                     this.selectNext();
10892                 }
10893             },
10894
10895             "enter" : function(e){
10896 //                this.onViewClick();
10897                 //return true;
10898                 this.collapse();
10899                 
10900                 if(this.fireEvent("specialkey", this, e)){
10901                     this.onViewClick(false);
10902                 }
10903                 
10904                 return true;
10905             },
10906
10907             "esc" : function(e){
10908                 this.collapse();
10909             },
10910
10911             "tab" : function(e){
10912                 this.collapse();
10913                 
10914                 if(this.fireEvent("specialkey", this, e)){
10915                     this.onViewClick(false);
10916                 }
10917                 
10918                 return true;
10919             },
10920
10921             scope : this,
10922
10923             doRelay : function(foo, bar, hname){
10924                 if(hname == 'down' || this.scope.isExpanded()){
10925                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10926                 }
10927                 return true;
10928             },
10929
10930             forceKeyDown: true
10931         });
10932         
10933         
10934         this.queryDelay = Math.max(this.queryDelay || 10,
10935                 this.mode == 'local' ? 10 : 250);
10936         
10937         
10938         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10939         
10940         if(this.typeAhead){
10941             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10942         }
10943         if(this.editable !== false){
10944             this.inputEl().on("keyup", this.onKeyUp, this);
10945         }
10946         if(this.forceSelection){
10947             this.inputEl().on('blur', this.doForce, this);
10948         }
10949         
10950         if(this.multiple){
10951             this.choices = this.el.select('ul.select2-choices', true).first();
10952             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10953         }
10954     },
10955     
10956     initTickableEvents: function()
10957     {   
10958         this.createList();
10959         
10960         if(this.hiddenName){
10961             
10962             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10963             
10964             this.hiddenField.dom.value =
10965                 this.hiddenValue !== undefined ? this.hiddenValue :
10966                 this.value !== undefined ? this.value : '';
10967
10968             // prevent input submission
10969             this.el.dom.removeAttribute('name');
10970             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10971              
10972              
10973         }
10974         
10975 //        this.list = this.el.select('ul.dropdown-menu',true).first();
10976         
10977         this.choices = this.el.select('ul.select2-choices', true).first();
10978         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10979         if(this.triggerList){
10980             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10981         }
10982          
10983         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10984         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10985         
10986         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10987         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10988         
10989         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10990         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10991         
10992         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10993         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10994         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10995         
10996         this.okBtn.hide();
10997         this.cancelBtn.hide();
10998         
10999         var _this = this;
11000         
11001         (function(){
11002             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11003             _this.list.setWidth(lw);
11004         }).defer(100);
11005         
11006         this.list.on('mouseover', this.onViewOver, this);
11007         this.list.on('mousemove', this.onViewMove, this);
11008         
11009         this.list.on('scroll', this.onViewScroll, this);
11010         
11011         if(!this.tpl){
11012             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11013         }
11014
11015         this.view = new Roo.View(this.list, this.tpl, {
11016             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11017         });
11018         
11019         //this.view.wrapEl.setDisplayed(false);
11020         this.view.on('click', this.onViewClick, this);
11021         
11022         
11023         
11024         this.store.on('beforeload', this.onBeforeLoad, this);
11025         this.store.on('load', this.onLoad, this);
11026         this.store.on('loadexception', this.onLoadException, this);
11027         
11028 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11029 //            "up" : function(e){
11030 //                this.inKeyMode = true;
11031 //                this.selectPrev();
11032 //            },
11033 //
11034 //            "down" : function(e){
11035 //                if(!this.isExpanded()){
11036 //                    this.onTriggerClick();
11037 //                }else{
11038 //                    this.inKeyMode = true;
11039 //                    this.selectNext();
11040 //                }
11041 //            },
11042 //
11043 //            "enter" : function(e){
11044 ////                this.onViewClick();
11045 //                //return true;
11046 //                this.collapse();
11047 //                
11048 //                if(this.fireEvent("specialkey", this, e)){
11049 //                    this.onViewClick(false);
11050 //                }
11051 //                
11052 //                return true;
11053 //            },
11054 //
11055 //            "esc" : function(e){
11056 //                this.collapse();
11057 //            },
11058 //
11059 //            "tab" : function(e){
11060 //                this.collapse();
11061 //                
11062 //                if(this.fireEvent("specialkey", this, e)){
11063 //                    this.onViewClick(false);
11064 //                }
11065 //                
11066 //                return true;
11067 //            },
11068 //
11069 //            scope : this,
11070 //
11071 //            doRelay : function(foo, bar, hname){
11072 //                if(hname == 'down' || this.scope.isExpanded()){
11073 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11074 //                }
11075 //                return true;
11076 //            },
11077 //
11078 //            forceKeyDown: true
11079 //        });
11080         
11081         
11082         this.queryDelay = Math.max(this.queryDelay || 10,
11083                 this.mode == 'local' ? 10 : 250);
11084         
11085         
11086         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11087         
11088         if(this.typeAhead){
11089             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11090         }
11091     },
11092
11093     onDestroy : function(){
11094         if(this.view){
11095             this.view.setStore(null);
11096             this.view.el.removeAllListeners();
11097             this.view.el.remove();
11098             this.view.purgeListeners();
11099         }
11100         if(this.list){
11101             this.list.dom.innerHTML  = '';
11102         }
11103         
11104         if(this.store){
11105             this.store.un('beforeload', this.onBeforeLoad, this);
11106             this.store.un('load', this.onLoad, this);
11107             this.store.un('loadexception', this.onLoadException, this);
11108         }
11109         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11110     },
11111
11112     // private
11113     fireKey : function(e){
11114         if(e.isNavKeyPress() && !this.list.isVisible()){
11115             this.fireEvent("specialkey", this, e);
11116         }
11117     },
11118
11119     // private
11120     onResize: function(w, h){
11121 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11122 //        
11123 //        if(typeof w != 'number'){
11124 //            // we do not handle it!?!?
11125 //            return;
11126 //        }
11127 //        var tw = this.trigger.getWidth();
11128 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11129 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11130 //        var x = w - tw;
11131 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11132 //            
11133 //        //this.trigger.setStyle('left', x+'px');
11134 //        
11135 //        if(this.list && this.listWidth === undefined){
11136 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11137 //            this.list.setWidth(lw);
11138 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11139 //        }
11140         
11141     
11142         
11143     },
11144
11145     /**
11146      * Allow or prevent the user from directly editing the field text.  If false is passed,
11147      * the user will only be able to select from the items defined in the dropdown list.  This method
11148      * is the runtime equivalent of setting the 'editable' config option at config time.
11149      * @param {Boolean} value True to allow the user to directly edit the field text
11150      */
11151     setEditable : function(value){
11152         if(value == this.editable){
11153             return;
11154         }
11155         this.editable = value;
11156         if(!value){
11157             this.inputEl().dom.setAttribute('readOnly', true);
11158             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11159             this.inputEl().addClass('x-combo-noedit');
11160         }else{
11161             this.inputEl().dom.setAttribute('readOnly', false);
11162             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11163             this.inputEl().removeClass('x-combo-noedit');
11164         }
11165     },
11166
11167     // private
11168     
11169     onBeforeLoad : function(combo,opts){
11170         if(!this.hasFocus){
11171             return;
11172         }
11173          if (!opts.add) {
11174             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11175          }
11176         this.restrictHeight();
11177         this.selectedIndex = -1;
11178     },
11179
11180     // private
11181     onLoad : function(){
11182         
11183         this.hasQuery = false;
11184         
11185         if(!this.hasFocus){
11186             return;
11187         }
11188         
11189         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11190             this.loading.hide();
11191         }
11192         
11193         if(this.store.getCount() > 0){
11194             this.expand();
11195 //            this.restrictHeight();
11196             if(this.lastQuery == this.allQuery){
11197                 if(this.editable && !this.tickable){
11198                     this.inputEl().dom.select();
11199                 }
11200                 
11201                 if(
11202                     !this.selectByValue(this.value, true) &&
11203                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11204                     this.store.lastOptions.add != true)
11205                 ){
11206                     this.select(0, true);
11207                 }
11208             }else{
11209                 if(this.autoFocus){
11210                     this.selectNext();
11211                 }
11212                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11213                     this.taTask.delay(this.typeAheadDelay);
11214                 }
11215             }
11216         }else{
11217             this.onEmptyResults();
11218         }
11219         
11220         //this.el.focus();
11221     },
11222     // private
11223     onLoadException : function()
11224     {
11225         this.hasQuery = false;
11226         
11227         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11228             this.loading.hide();
11229         }
11230         
11231         this.collapse();
11232         Roo.log(this.store.reader.jsonData);
11233         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11234             // fixme
11235             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11236         }
11237         
11238         
11239     },
11240     // private
11241     onTypeAhead : function(){
11242         if(this.store.getCount() > 0){
11243             var r = this.store.getAt(0);
11244             var newValue = r.data[this.displayField];
11245             var len = newValue.length;
11246             var selStart = this.getRawValue().length;
11247             
11248             if(selStart != len){
11249                 this.setRawValue(newValue);
11250                 this.selectText(selStart, newValue.length);
11251             }
11252         }
11253     },
11254
11255     // private
11256     onSelect : function(record, index){
11257         
11258         if(this.fireEvent('beforeselect', this, record, index) !== false){
11259         
11260             this.setFromData(index > -1 ? record.data : false);
11261             
11262             this.collapse();
11263             this.fireEvent('select', this, record, index);
11264         }
11265     },
11266
11267     /**
11268      * Returns the currently selected field value or empty string if no value is set.
11269      * @return {String} value The selected value
11270      */
11271     getValue : function(){
11272         
11273         if(this.multiple){
11274             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11275         }
11276         
11277         if(this.valueField){
11278             return typeof this.value != 'undefined' ? this.value : '';
11279         }else{
11280             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11281         }
11282     },
11283
11284     /**
11285      * Clears any text/value currently set in the field
11286      */
11287     clearValue : function(){
11288         if(this.hiddenField){
11289             this.hiddenField.dom.value = '';
11290         }
11291         this.value = '';
11292         this.setRawValue('');
11293         this.lastSelectionText = '';
11294         
11295     },
11296
11297     /**
11298      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11299      * will be displayed in the field.  If the value does not match the data value of an existing item,
11300      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11301      * Otherwise the field will be blank (although the value will still be set).
11302      * @param {String} value The value to match
11303      */
11304     setValue : function(v){
11305         if(this.multiple){
11306             this.syncValue();
11307             return;
11308         }
11309         
11310         var text = v;
11311         if(this.valueField){
11312             var r = this.findRecord(this.valueField, v);
11313             if(r){
11314                 text = r.data[this.displayField];
11315             }else if(this.valueNotFoundText !== undefined){
11316                 text = this.valueNotFoundText;
11317             }
11318         }
11319         this.lastSelectionText = text;
11320         if(this.hiddenField){
11321             this.hiddenField.dom.value = v;
11322         }
11323         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11324         this.value = v;
11325     },
11326     /**
11327      * @property {Object} the last set data for the element
11328      */
11329     
11330     lastData : false,
11331     /**
11332      * Sets the value of the field based on a object which is related to the record format for the store.
11333      * @param {Object} value the value to set as. or false on reset?
11334      */
11335     setFromData : function(o){
11336         
11337         if(this.multiple){
11338             this.addItem(o);
11339             return;
11340         }
11341             
11342         var dv = ''; // display value
11343         var vv = ''; // value value..
11344         this.lastData = o;
11345         if (this.displayField) {
11346             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11347         } else {
11348             // this is an error condition!!!
11349             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11350         }
11351         
11352         if(this.valueField){
11353             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11354         }
11355         
11356         if(this.hiddenField){
11357             this.hiddenField.dom.value = vv;
11358             
11359             this.lastSelectionText = dv;
11360             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11361             this.value = vv;
11362             return;
11363         }
11364         // no hidden field.. - we store the value in 'value', but still display
11365         // display field!!!!
11366         this.lastSelectionText = dv;
11367         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11368         this.value = vv;
11369         
11370         
11371     },
11372     // private
11373     reset : function(){
11374         // overridden so that last data is reset..
11375         this.setValue(this.originalValue);
11376         this.clearInvalid();
11377         this.lastData = false;
11378         if (this.view) {
11379             this.view.clearSelections();
11380         }
11381     },
11382     // private
11383     findRecord : function(prop, value){
11384         var record;
11385         if(this.store.getCount() > 0){
11386             this.store.each(function(r){
11387                 if(r.data[prop] == value){
11388                     record = r;
11389                     return false;
11390                 }
11391                 return true;
11392             });
11393         }
11394         return record;
11395     },
11396     
11397     getName: function()
11398     {
11399         // returns hidden if it's set..
11400         if (!this.rendered) {return ''};
11401         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11402         
11403     },
11404     // private
11405     onViewMove : function(e, t){
11406         this.inKeyMode = false;
11407     },
11408
11409     // private
11410     onViewOver : function(e, t){
11411         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11412             return;
11413         }
11414         var item = this.view.findItemFromChild(t);
11415         
11416         if(item){
11417             var index = this.view.indexOf(item);
11418             this.select(index, false);
11419         }
11420     },
11421
11422     // private
11423     onViewClick : function(view, doFocus, el, e)
11424     {
11425         var index = this.view.getSelectedIndexes()[0];
11426         
11427         var r = this.store.getAt(index);
11428         
11429         if(this.tickable){
11430             
11431             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11432                 return;
11433             }
11434             
11435             var rm = false;
11436             var _this = this;
11437             
11438             Roo.each(this.tickItems, function(v,k){
11439                 
11440                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11441                     _this.tickItems.splice(k, 1);
11442                     rm = true;
11443                     return;
11444                 }
11445             })
11446             
11447             if(rm){
11448                 return;
11449             }
11450             
11451             this.tickItems.push(r.data);
11452             return;
11453         }
11454         
11455         if(r){
11456             this.onSelect(r, index);
11457         }
11458         if(doFocus !== false && !this.blockFocus){
11459             this.inputEl().focus();
11460         }
11461     },
11462
11463     // private
11464     restrictHeight : function(){
11465         //this.innerList.dom.style.height = '';
11466         //var inner = this.innerList.dom;
11467         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11468         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11469         //this.list.beginUpdate();
11470         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11471         this.list.alignTo(this.inputEl(), this.listAlign);
11472         this.list.alignTo(this.inputEl(), this.listAlign);
11473         //this.list.endUpdate();
11474     },
11475
11476     // private
11477     onEmptyResults : function(){
11478         this.collapse();
11479     },
11480
11481     /**
11482      * Returns true if the dropdown list is expanded, else false.
11483      */
11484     isExpanded : function(){
11485         return this.list.isVisible();
11486     },
11487
11488     /**
11489      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11490      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11491      * @param {String} value The data value of the item to select
11492      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11493      * selected item if it is not currently in view (defaults to true)
11494      * @return {Boolean} True if the value matched an item in the list, else false
11495      */
11496     selectByValue : function(v, scrollIntoView){
11497         if(v !== undefined && v !== null){
11498             var r = this.findRecord(this.valueField || this.displayField, v);
11499             if(r){
11500                 this.select(this.store.indexOf(r), scrollIntoView);
11501                 return true;
11502             }
11503         }
11504         return false;
11505     },
11506
11507     /**
11508      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11509      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11510      * @param {Number} index The zero-based index of the list item to select
11511      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11512      * selected item if it is not currently in view (defaults to true)
11513      */
11514     select : function(index, scrollIntoView){
11515         this.selectedIndex = index;
11516         this.view.select(index);
11517         if(scrollIntoView !== false){
11518             var el = this.view.getNode(index);
11519             if(el && !this.multiple && !this.tickable){
11520                 this.list.scrollChildIntoView(el, false);
11521             }
11522         }
11523     },
11524
11525     // private
11526     selectNext : function(){
11527         var ct = this.store.getCount();
11528         if(ct > 0){
11529             if(this.selectedIndex == -1){
11530                 this.select(0);
11531             }else if(this.selectedIndex < ct-1){
11532                 this.select(this.selectedIndex+1);
11533             }
11534         }
11535     },
11536
11537     // private
11538     selectPrev : function(){
11539         var ct = this.store.getCount();
11540         if(ct > 0){
11541             if(this.selectedIndex == -1){
11542                 this.select(0);
11543             }else if(this.selectedIndex != 0){
11544                 this.select(this.selectedIndex-1);
11545             }
11546         }
11547     },
11548
11549     // private
11550     onKeyUp : function(e){
11551         if(this.editable !== false && !e.isSpecialKey()){
11552             this.lastKey = e.getKey();
11553             this.dqTask.delay(this.queryDelay);
11554         }
11555     },
11556
11557     // private
11558     validateBlur : function(){
11559         return !this.list || !this.list.isVisible();   
11560     },
11561
11562     // private
11563     initQuery : function(){
11564         this.doQuery(this.getRawValue());
11565     },
11566
11567     // private
11568     doForce : function(){
11569         if(this.inputEl().dom.value.length > 0){
11570             this.inputEl().dom.value =
11571                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11572              
11573         }
11574     },
11575
11576     /**
11577      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11578      * query allowing the query action to be canceled if needed.
11579      * @param {String} query The SQL query to execute
11580      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11581      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11582      * saved in the current store (defaults to false)
11583      */
11584     doQuery : function(q, forceAll){
11585         
11586         if(q === undefined || q === null){
11587             q = '';
11588         }
11589         var qe = {
11590             query: q,
11591             forceAll: forceAll,
11592             combo: this,
11593             cancel:false
11594         };
11595         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11596             return false;
11597         }
11598         q = qe.query;
11599         
11600         forceAll = qe.forceAll;
11601         if(forceAll === true || (q.length >= this.minChars)){
11602             
11603             this.hasQuery = true;
11604             
11605             if(this.lastQuery != q || this.alwaysQuery){
11606                 this.lastQuery = q;
11607                 if(this.mode == 'local'){
11608                     this.selectedIndex = -1;
11609                     if(forceAll){
11610                         this.store.clearFilter();
11611                     }else{
11612                         this.store.filter(this.displayField, q);
11613                     }
11614                     this.onLoad();
11615                 }else{
11616                     this.store.baseParams[this.queryParam] = q;
11617                     
11618                     var options = {params : this.getParams(q)};
11619                     
11620                     if(this.loadNext){
11621                         options.add = true;
11622                         options.params.start = this.page * this.pageSize;
11623                     }
11624                     
11625                     this.store.load(options);
11626                     /*
11627                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11628                      *  we should expand the list on onLoad
11629                      *  so command out it
11630                      */
11631 //                    this.expand();
11632                 }
11633             }else{
11634                 this.selectedIndex = -1;
11635                 this.onLoad();   
11636             }
11637         }
11638         
11639         this.loadNext = false;
11640     },
11641
11642     // private
11643     getParams : function(q){
11644         var p = {};
11645         //p[this.queryParam] = q;
11646         
11647         if(this.pageSize){
11648             p.start = 0;
11649             p.limit = this.pageSize;
11650         }
11651         return p;
11652     },
11653
11654     /**
11655      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11656      */
11657     collapse : function(){
11658         if(!this.isExpanded()){
11659             return;
11660         }
11661         
11662         this.list.hide();
11663         
11664         if(this.tickable){
11665             this.okBtn.hide();
11666             this.cancelBtn.hide();
11667             this.trigger.show();
11668         }
11669         
11670         Roo.get(document).un('mousedown', this.collapseIf, this);
11671         Roo.get(document).un('mousewheel', this.collapseIf, this);
11672         if (!this.editable) {
11673             Roo.get(document).un('keydown', this.listKeyPress, this);
11674         }
11675         this.fireEvent('collapse', this);
11676     },
11677
11678     // private
11679     collapseIf : function(e){
11680         var in_combo  = e.within(this.el);
11681         var in_list =  e.within(this.list);
11682         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11683         
11684         if (in_combo || in_list || is_list) {
11685             //e.stopPropagation();
11686             return;
11687         }
11688         
11689         if(this.tickable){
11690             this.onTickableFooterButtonClick(e, false, false);
11691         }
11692
11693         this.collapse();
11694         
11695     },
11696
11697     /**
11698      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11699      */
11700     expand : function(){
11701        
11702         if(this.isExpanded() || !this.hasFocus){
11703             return;
11704         }
11705         
11706         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11707         this.list.setWidth(lw);
11708         
11709         
11710          Roo.log('expand');
11711         
11712         this.list.show();
11713         
11714         this.restrictHeight();
11715         
11716         if(this.tickable){
11717             
11718             this.tickItems = Roo.apply([], this.item);
11719             
11720             this.okBtn.show();
11721             this.cancelBtn.show();
11722             this.trigger.hide();
11723             
11724         }
11725         
11726         Roo.get(document).on('mousedown', this.collapseIf, this);
11727         Roo.get(document).on('mousewheel', this.collapseIf, this);
11728         if (!this.editable) {
11729             Roo.get(document).on('keydown', this.listKeyPress, this);
11730         }
11731         
11732         this.fireEvent('expand', this);
11733     },
11734
11735     // private
11736     // Implements the default empty TriggerField.onTriggerClick function
11737     onTriggerClick : function(e)
11738     {
11739         Roo.log('trigger click');
11740         
11741         if(this.disabled || !this.triggerList){
11742             return;
11743         }
11744         
11745         this.page = 0;
11746         this.loadNext = false;
11747         
11748         if(this.isExpanded()){
11749             this.collapse();
11750             if (!this.blockFocus) {
11751                 this.inputEl().focus();
11752             }
11753             
11754         }else {
11755             this.hasFocus = true;
11756             if(this.triggerAction == 'all') {
11757                 this.doQuery(this.allQuery, true);
11758             } else {
11759                 this.doQuery(this.getRawValue());
11760             }
11761             if (!this.blockFocus) {
11762                 this.inputEl().focus();
11763             }
11764         }
11765     },
11766     
11767     onTickableTriggerClick : function(e)
11768     {
11769         if(this.disabled){
11770             return;
11771         }
11772         
11773         this.page = 0;
11774         this.loadNext = false;
11775         this.hasFocus = true;
11776         
11777         if(this.triggerAction == 'all') {
11778             this.doQuery(this.allQuery, true);
11779         } else {
11780             this.doQuery(this.getRawValue());
11781         }
11782     },
11783     
11784     onSearchFieldClick : function(e)
11785     {
11786         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11787             return;
11788         }
11789         
11790         this.page = 0;
11791         this.loadNext = false;
11792         this.hasFocus = true;
11793         
11794         if(this.triggerAction == 'all') {
11795             this.doQuery(this.allQuery, true);
11796         } else {
11797             this.doQuery(this.getRawValue());
11798         }
11799     },
11800     
11801     listKeyPress : function(e)
11802     {
11803         //Roo.log('listkeypress');
11804         // scroll to first matching element based on key pres..
11805         if (e.isSpecialKey()) {
11806             return false;
11807         }
11808         var k = String.fromCharCode(e.getKey()).toUpperCase();
11809         //Roo.log(k);
11810         var match  = false;
11811         var csel = this.view.getSelectedNodes();
11812         var cselitem = false;
11813         if (csel.length) {
11814             var ix = this.view.indexOf(csel[0]);
11815             cselitem  = this.store.getAt(ix);
11816             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11817                 cselitem = false;
11818             }
11819             
11820         }
11821         
11822         this.store.each(function(v) { 
11823             if (cselitem) {
11824                 // start at existing selection.
11825                 if (cselitem.id == v.id) {
11826                     cselitem = false;
11827                 }
11828                 return true;
11829             }
11830                 
11831             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11832                 match = this.store.indexOf(v);
11833                 return false;
11834             }
11835             return true;
11836         }, this);
11837         
11838         if (match === false) {
11839             return true; // no more action?
11840         }
11841         // scroll to?
11842         this.view.select(match);
11843         var sn = Roo.get(this.view.getSelectedNodes()[0])
11844         //sn.scrollIntoView(sn.dom.parentNode, false);
11845     },
11846     
11847     onViewScroll : function(e, t){
11848         
11849         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){
11850             return;
11851         }
11852         
11853         this.hasQuery = true;
11854         
11855         this.loading = this.list.select('.loading', true).first();
11856         
11857         if(this.loading === null){
11858             this.list.createChild({
11859                 tag: 'div',
11860                 cls: 'loading select2-more-results select2-active',
11861                 html: 'Loading more results...'
11862             })
11863             
11864             this.loading = this.list.select('.loading', true).first();
11865             
11866             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11867             
11868             this.loading.hide();
11869         }
11870         
11871         this.loading.show();
11872         
11873         var _combo = this;
11874         
11875         this.page++;
11876         this.loadNext = true;
11877         
11878         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11879         
11880         return;
11881     },
11882     
11883     addItem : function(o)
11884     {   
11885         var dv = ''; // display value
11886         
11887         if (this.displayField) {
11888             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11889         } else {
11890             // this is an error condition!!!
11891             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11892         }
11893         
11894         if(!dv.length){
11895             return;
11896         }
11897         
11898         var choice = this.choices.createChild({
11899             tag: 'li',
11900             cls: 'select2-search-choice',
11901             cn: [
11902                 {
11903                     tag: 'div',
11904                     html: dv
11905                 },
11906                 {
11907                     tag: 'a',
11908                     href: '#',
11909                     cls: 'select2-search-choice-close',
11910                     tabindex: '-1'
11911                 }
11912             ]
11913             
11914         }, this.searchField);
11915         
11916         var close = choice.select('a.select2-search-choice-close', true).first()
11917         
11918         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11919         
11920         this.item.push(o);
11921         
11922         this.lastData = o;
11923         
11924         this.syncValue();
11925         
11926         this.inputEl().dom.value = '';
11927         
11928     },
11929     
11930     onRemoveItem : function(e, _self, o)
11931     {
11932         e.preventDefault();
11933         var index = this.item.indexOf(o.data) * 1;
11934         
11935         if( index < 0){
11936             Roo.log('not this item?!');
11937             return;
11938         }
11939         
11940         this.item.splice(index, 1);
11941         o.item.remove();
11942         
11943         this.syncValue();
11944         
11945         this.fireEvent('remove', this, e);
11946         
11947     },
11948     
11949     syncValue : function()
11950     {
11951         if(!this.item.length){
11952             this.clearValue();
11953             return;
11954         }
11955             
11956         var value = [];
11957         var _this = this;
11958         Roo.each(this.item, function(i){
11959             if(_this.valueField){
11960                 value.push(i[_this.valueField]);
11961                 return;
11962             }
11963
11964             value.push(i);
11965         });
11966
11967         this.value = value.join(',');
11968
11969         if(this.hiddenField){
11970             this.hiddenField.dom.value = this.value;
11971         }
11972     },
11973     
11974     clearItem : function()
11975     {
11976         if(!this.multiple){
11977             return;
11978         }
11979         
11980         this.item = [];
11981         
11982         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11983            c.remove();
11984         });
11985         
11986         this.syncValue();
11987     },
11988     
11989     inputEl: function ()
11990     {
11991         if(this.tickable){
11992             return this.searchField;
11993         }
11994         return this.el.select('input.form-control',true).first();
11995     },
11996     
11997     
11998     onTickableFooterButtonClick : function(e, btn, el)
11999     {
12000         e.preventDefault();
12001         
12002         if(btn && btn.name == 'cancel'){
12003             this.tickItems = Roo.apply([], this.item);
12004             this.collapse();
12005             return;
12006         }
12007         
12008         this.clearItem();
12009         
12010         var _this = this;
12011         
12012         Roo.each(this.tickItems, function(o){
12013             _this.addItem(o);
12014         });
12015         
12016         this.collapse();
12017         
12018     }
12019     
12020     
12021
12022     /** 
12023     * @cfg {Boolean} grow 
12024     * @hide 
12025     */
12026     /** 
12027     * @cfg {Number} growMin 
12028     * @hide 
12029     */
12030     /** 
12031     * @cfg {Number} growMax 
12032     * @hide 
12033     */
12034     /**
12035      * @hide
12036      * @method autoSize
12037      */
12038 });
12039 /*
12040  * Based on:
12041  * Ext JS Library 1.1.1
12042  * Copyright(c) 2006-2007, Ext JS, LLC.
12043  *
12044  * Originally Released Under LGPL - original licence link has changed is not relivant.
12045  *
12046  * Fork - LGPL
12047  * <script type="text/javascript">
12048  */
12049
12050 /**
12051  * @class Roo.View
12052  * @extends Roo.util.Observable
12053  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12054  * This class also supports single and multi selection modes. <br>
12055  * Create a data model bound view:
12056  <pre><code>
12057  var store = new Roo.data.Store(...);
12058
12059  var view = new Roo.View({
12060     el : "my-element",
12061     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12062  
12063     singleSelect: true,
12064     selectedClass: "ydataview-selected",
12065     store: store
12066  });
12067
12068  // listen for node click?
12069  view.on("click", function(vw, index, node, e){
12070  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12071  });
12072
12073  // load XML data
12074  dataModel.load("foobar.xml");
12075  </code></pre>
12076  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12077  * <br><br>
12078  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12079  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12080  * 
12081  * Note: old style constructor is still suported (container, template, config)
12082  * 
12083  * @constructor
12084  * Create a new View
12085  * @param {Object} config The config object
12086  * 
12087  */
12088 Roo.View = function(config, depreciated_tpl, depreciated_config){
12089     
12090     this.parent = false;
12091     
12092     if (typeof(depreciated_tpl) == 'undefined') {
12093         // new way.. - universal constructor.
12094         Roo.apply(this, config);
12095         this.el  = Roo.get(this.el);
12096     } else {
12097         // old format..
12098         this.el  = Roo.get(config);
12099         this.tpl = depreciated_tpl;
12100         Roo.apply(this, depreciated_config);
12101     }
12102     this.wrapEl  = this.el.wrap().wrap();
12103     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12104     
12105     
12106     if(typeof(this.tpl) == "string"){
12107         this.tpl = new Roo.Template(this.tpl);
12108     } else {
12109         // support xtype ctors..
12110         this.tpl = new Roo.factory(this.tpl, Roo);
12111     }
12112     
12113     
12114     this.tpl.compile();
12115     
12116     /** @private */
12117     this.addEvents({
12118         /**
12119          * @event beforeclick
12120          * Fires before a click is processed. Returns false to cancel the default action.
12121          * @param {Roo.View} this
12122          * @param {Number} index The index of the target node
12123          * @param {HTMLElement} node The target node
12124          * @param {Roo.EventObject} e The raw event object
12125          */
12126             "beforeclick" : true,
12127         /**
12128          * @event click
12129          * Fires when a template node is clicked.
12130          * @param {Roo.View} this
12131          * @param {Number} index The index of the target node
12132          * @param {HTMLElement} node The target node
12133          * @param {Roo.EventObject} e The raw event object
12134          */
12135             "click" : true,
12136         /**
12137          * @event dblclick
12138          * Fires when a template node is double clicked.
12139          * @param {Roo.View} this
12140          * @param {Number} index The index of the target node
12141          * @param {HTMLElement} node The target node
12142          * @param {Roo.EventObject} e The raw event object
12143          */
12144             "dblclick" : true,
12145         /**
12146          * @event contextmenu
12147          * Fires when a template node is right clicked.
12148          * @param {Roo.View} this
12149          * @param {Number} index The index of the target node
12150          * @param {HTMLElement} node The target node
12151          * @param {Roo.EventObject} e The raw event object
12152          */
12153             "contextmenu" : true,
12154         /**
12155          * @event selectionchange
12156          * Fires when the selected nodes change.
12157          * @param {Roo.View} this
12158          * @param {Array} selections Array of the selected nodes
12159          */
12160             "selectionchange" : true,
12161     
12162         /**
12163          * @event beforeselect
12164          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12165          * @param {Roo.View} this
12166          * @param {HTMLElement} node The node to be selected
12167          * @param {Array} selections Array of currently selected nodes
12168          */
12169             "beforeselect" : true,
12170         /**
12171          * @event preparedata
12172          * Fires on every row to render, to allow you to change the data.
12173          * @param {Roo.View} this
12174          * @param {Object} data to be rendered (change this)
12175          */
12176           "preparedata" : true
12177           
12178           
12179         });
12180
12181
12182
12183     this.el.on({
12184         "click": this.onClick,
12185         "dblclick": this.onDblClick,
12186         "contextmenu": this.onContextMenu,
12187         scope:this
12188     });
12189
12190     this.selections = [];
12191     this.nodes = [];
12192     this.cmp = new Roo.CompositeElementLite([]);
12193     if(this.store){
12194         this.store = Roo.factory(this.store, Roo.data);
12195         this.setStore(this.store, true);
12196     }
12197     
12198     if ( this.footer && this.footer.xtype) {
12199            
12200          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12201         
12202         this.footer.dataSource = this.store
12203         this.footer.container = fctr;
12204         this.footer = Roo.factory(this.footer, Roo);
12205         fctr.insertFirst(this.el);
12206         
12207         // this is a bit insane - as the paging toolbar seems to detach the el..
12208 //        dom.parentNode.parentNode.parentNode
12209          // they get detached?
12210     }
12211     
12212     
12213     Roo.View.superclass.constructor.call(this);
12214     
12215     
12216 };
12217
12218 Roo.extend(Roo.View, Roo.util.Observable, {
12219     
12220      /**
12221      * @cfg {Roo.data.Store} store Data store to load data from.
12222      */
12223     store : false,
12224     
12225     /**
12226      * @cfg {String|Roo.Element} el The container element.
12227      */
12228     el : '',
12229     
12230     /**
12231      * @cfg {String|Roo.Template} tpl The template used by this View 
12232      */
12233     tpl : false,
12234     /**
12235      * @cfg {String} dataName the named area of the template to use as the data area
12236      *                          Works with domtemplates roo-name="name"
12237      */
12238     dataName: false,
12239     /**
12240      * @cfg {String} selectedClass The css class to add to selected nodes
12241      */
12242     selectedClass : "x-view-selected",
12243      /**
12244      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12245      */
12246     emptyText : "",
12247     
12248     /**
12249      * @cfg {String} text to display on mask (default Loading)
12250      */
12251     mask : false,
12252     /**
12253      * @cfg {Boolean} multiSelect Allow multiple selection
12254      */
12255     multiSelect : false,
12256     /**
12257      * @cfg {Boolean} singleSelect Allow single selection
12258      */
12259     singleSelect:  false,
12260     
12261     /**
12262      * @cfg {Boolean} toggleSelect - selecting 
12263      */
12264     toggleSelect : false,
12265     
12266     /**
12267      * @cfg {Boolean} tickable - selecting 
12268      */
12269     tickable : false,
12270     
12271     /**
12272      * Returns the element this view is bound to.
12273      * @return {Roo.Element}
12274      */
12275     getEl : function(){
12276         return this.wrapEl;
12277     },
12278     
12279     
12280
12281     /**
12282      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12283      */
12284     refresh : function(){
12285         Roo.log('refresh');
12286         var t = this.tpl;
12287         
12288         // if we are using something like 'domtemplate', then
12289         // the what gets used is:
12290         // t.applySubtemplate(NAME, data, wrapping data..)
12291         // the outer template then get' applied with
12292         //     the store 'extra data'
12293         // and the body get's added to the
12294         //      roo-name="data" node?
12295         //      <span class='roo-tpl-{name}'></span> ?????
12296         
12297         
12298         
12299         this.clearSelections();
12300         this.el.update("");
12301         var html = [];
12302         var records = this.store.getRange();
12303         if(records.length < 1) {
12304             
12305             // is this valid??  = should it render a template??
12306             
12307             this.el.update(this.emptyText);
12308             return;
12309         }
12310         var el = this.el;
12311         if (this.dataName) {
12312             this.el.update(t.apply(this.store.meta)); //????
12313             el = this.el.child('.roo-tpl-' + this.dataName);
12314         }
12315         
12316         for(var i = 0, len = records.length; i < len; i++){
12317             var data = this.prepareData(records[i].data, i, records[i]);
12318             this.fireEvent("preparedata", this, data, i, records[i]);
12319             
12320             var d = Roo.apply({}, data);
12321             
12322             if(this.tickable){
12323                 Roo.apply(d, {'roo-id' : Roo.id()});
12324                 
12325                 var _this = this;
12326             
12327                 Roo.each(this.parent.item, function(item){
12328                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12329                         return;
12330                     }
12331                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12332                 });
12333             }
12334             
12335             html[html.length] = Roo.util.Format.trim(
12336                 this.dataName ?
12337                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12338                     t.apply(d)
12339             );
12340         }
12341         
12342         
12343         
12344         el.update(html.join(""));
12345         this.nodes = el.dom.childNodes;
12346         this.updateIndexes(0);
12347     },
12348     
12349
12350     /**
12351      * Function to override to reformat the data that is sent to
12352      * the template for each node.
12353      * DEPRICATED - use the preparedata event handler.
12354      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12355      * a JSON object for an UpdateManager bound view).
12356      */
12357     prepareData : function(data, index, record)
12358     {
12359         this.fireEvent("preparedata", this, data, index, record);
12360         return data;
12361     },
12362
12363     onUpdate : function(ds, record){
12364          Roo.log('on update');   
12365         this.clearSelections();
12366         var index = this.store.indexOf(record);
12367         var n = this.nodes[index];
12368         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12369         n.parentNode.removeChild(n);
12370         this.updateIndexes(index, index);
12371     },
12372
12373     
12374     
12375 // --------- FIXME     
12376     onAdd : function(ds, records, index)
12377     {
12378         Roo.log(['on Add', ds, records, index] );        
12379         this.clearSelections();
12380         if(this.nodes.length == 0){
12381             this.refresh();
12382             return;
12383         }
12384         var n = this.nodes[index];
12385         for(var i = 0, len = records.length; i < len; i++){
12386             var d = this.prepareData(records[i].data, i, records[i]);
12387             if(n){
12388                 this.tpl.insertBefore(n, d);
12389             }else{
12390                 
12391                 this.tpl.append(this.el, d);
12392             }
12393         }
12394         this.updateIndexes(index);
12395     },
12396
12397     onRemove : function(ds, record, index){
12398         Roo.log('onRemove');
12399         this.clearSelections();
12400         var el = this.dataName  ?
12401             this.el.child('.roo-tpl-' + this.dataName) :
12402             this.el; 
12403         
12404         el.dom.removeChild(this.nodes[index]);
12405         this.updateIndexes(index);
12406     },
12407
12408     /**
12409      * Refresh an individual node.
12410      * @param {Number} index
12411      */
12412     refreshNode : function(index){
12413         this.onUpdate(this.store, this.store.getAt(index));
12414     },
12415
12416     updateIndexes : function(startIndex, endIndex){
12417         var ns = this.nodes;
12418         startIndex = startIndex || 0;
12419         endIndex = endIndex || ns.length - 1;
12420         for(var i = startIndex; i <= endIndex; i++){
12421             ns[i].nodeIndex = i;
12422         }
12423     },
12424
12425     /**
12426      * Changes the data store this view uses and refresh the view.
12427      * @param {Store} store
12428      */
12429     setStore : function(store, initial){
12430         if(!initial && this.store){
12431             this.store.un("datachanged", this.refresh);
12432             this.store.un("add", this.onAdd);
12433             this.store.un("remove", this.onRemove);
12434             this.store.un("update", this.onUpdate);
12435             this.store.un("clear", this.refresh);
12436             this.store.un("beforeload", this.onBeforeLoad);
12437             this.store.un("load", this.onLoad);
12438             this.store.un("loadexception", this.onLoad);
12439         }
12440         if(store){
12441           
12442             store.on("datachanged", this.refresh, this);
12443             store.on("add", this.onAdd, this);
12444             store.on("remove", this.onRemove, this);
12445             store.on("update", this.onUpdate, this);
12446             store.on("clear", this.refresh, this);
12447             store.on("beforeload", this.onBeforeLoad, this);
12448             store.on("load", this.onLoad, this);
12449             store.on("loadexception", this.onLoad, this);
12450         }
12451         
12452         if(store){
12453             this.refresh();
12454         }
12455     },
12456     /**
12457      * onbeforeLoad - masks the loading area.
12458      *
12459      */
12460     onBeforeLoad : function(store,opts)
12461     {
12462          Roo.log('onBeforeLoad');   
12463         if (!opts.add) {
12464             this.el.update("");
12465         }
12466         this.el.mask(this.mask ? this.mask : "Loading" ); 
12467     },
12468     onLoad : function ()
12469     {
12470         this.el.unmask();
12471     },
12472     
12473
12474     /**
12475      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12476      * @param {HTMLElement} node
12477      * @return {HTMLElement} The template node
12478      */
12479     findItemFromChild : function(node){
12480         var el = this.dataName  ?
12481             this.el.child('.roo-tpl-' + this.dataName,true) :
12482             this.el.dom; 
12483         
12484         if(!node || node.parentNode == el){
12485                     return node;
12486             }
12487             var p = node.parentNode;
12488             while(p && p != el){
12489             if(p.parentNode == el){
12490                 return p;
12491             }
12492             p = p.parentNode;
12493         }
12494             return null;
12495     },
12496
12497     /** @ignore */
12498     onClick : function(e){
12499         var item = this.findItemFromChild(e.getTarget());
12500         if(item){
12501             var index = this.indexOf(item);
12502             if(this.onItemClick(item, index, e) !== false){
12503                 this.fireEvent("click", this, index, item, e);
12504             }
12505         }else{
12506             this.clearSelections();
12507         }
12508     },
12509
12510     /** @ignore */
12511     onContextMenu : function(e){
12512         var item = this.findItemFromChild(e.getTarget());
12513         if(item){
12514             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12515         }
12516     },
12517
12518     /** @ignore */
12519     onDblClick : function(e){
12520         var item = this.findItemFromChild(e.getTarget());
12521         if(item){
12522             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12523         }
12524     },
12525
12526     onItemClick : function(item, index, e)
12527     {
12528         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12529             return false;
12530         }
12531         if (this.toggleSelect) {
12532             var m = this.isSelected(item) ? 'unselect' : 'select';
12533             Roo.log(m);
12534             var _t = this;
12535             _t[m](item, true, false);
12536             return true;
12537         }
12538         if(this.multiSelect || this.singleSelect){
12539             if(this.multiSelect && e.shiftKey && this.lastSelection){
12540                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12541             }else{
12542                 this.select(item, this.multiSelect && e.ctrlKey);
12543                 this.lastSelection = item;
12544             }
12545             
12546             if(!this.tickable){
12547                 e.preventDefault();
12548             }
12549             
12550         }
12551         return true;
12552     },
12553
12554     /**
12555      * Get the number of selected nodes.
12556      * @return {Number}
12557      */
12558     getSelectionCount : function(){
12559         return this.selections.length;
12560     },
12561
12562     /**
12563      * Get the currently selected nodes.
12564      * @return {Array} An array of HTMLElements
12565      */
12566     getSelectedNodes : function(){
12567         return this.selections;
12568     },
12569
12570     /**
12571      * Get the indexes of the selected nodes.
12572      * @return {Array}
12573      */
12574     getSelectedIndexes : function(){
12575         var indexes = [], s = this.selections;
12576         for(var i = 0, len = s.length; i < len; i++){
12577             indexes.push(s[i].nodeIndex);
12578         }
12579         return indexes;
12580     },
12581
12582     /**
12583      * Clear all selections
12584      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12585      */
12586     clearSelections : function(suppressEvent){
12587         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12588             this.cmp.elements = this.selections;
12589             this.cmp.removeClass(this.selectedClass);
12590             this.selections = [];
12591             if(!suppressEvent){
12592                 this.fireEvent("selectionchange", this, this.selections);
12593             }
12594         }
12595     },
12596
12597     /**
12598      * Returns true if the passed node is selected
12599      * @param {HTMLElement/Number} node The node or node index
12600      * @return {Boolean}
12601      */
12602     isSelected : function(node){
12603         var s = this.selections;
12604         if(s.length < 1){
12605             return false;
12606         }
12607         node = this.getNode(node);
12608         return s.indexOf(node) !== -1;
12609     },
12610
12611     /**
12612      * Selects nodes.
12613      * @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
12614      * @param {Boolean} keepExisting (optional) true to keep existing selections
12615      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12616      */
12617     select : function(nodeInfo, keepExisting, suppressEvent){
12618         if(nodeInfo instanceof Array){
12619             if(!keepExisting){
12620                 this.clearSelections(true);
12621             }
12622             for(var i = 0, len = nodeInfo.length; i < len; i++){
12623                 this.select(nodeInfo[i], true, true);
12624             }
12625             return;
12626         } 
12627         var node = this.getNode(nodeInfo);
12628         if(!node || this.isSelected(node)){
12629             return; // already selected.
12630         }
12631         if(!keepExisting){
12632             this.clearSelections(true);
12633         }
12634         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12635             Roo.fly(node).addClass(this.selectedClass);
12636             this.selections.push(node);
12637             if(!suppressEvent){
12638                 this.fireEvent("selectionchange", this, this.selections);
12639             }
12640         }
12641         
12642         
12643     },
12644       /**
12645      * Unselects nodes.
12646      * @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
12647      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12648      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12649      */
12650     unselect : function(nodeInfo, keepExisting, suppressEvent)
12651     {
12652         if(nodeInfo instanceof Array){
12653             Roo.each(this.selections, function(s) {
12654                 this.unselect(s, nodeInfo);
12655             }, this);
12656             return;
12657         }
12658         var node = this.getNode(nodeInfo);
12659         if(!node || !this.isSelected(node)){
12660             Roo.log("not selected");
12661             return; // not selected.
12662         }
12663         // fireevent???
12664         var ns = [];
12665         Roo.each(this.selections, function(s) {
12666             if (s == node ) {
12667                 Roo.fly(node).removeClass(this.selectedClass);
12668
12669                 return;
12670             }
12671             ns.push(s);
12672         },this);
12673         
12674         this.selections= ns;
12675         this.fireEvent("selectionchange", this, this.selections);
12676     },
12677
12678     /**
12679      * Gets a template node.
12680      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12681      * @return {HTMLElement} The node or null if it wasn't found
12682      */
12683     getNode : function(nodeInfo){
12684         if(typeof nodeInfo == "string"){
12685             return document.getElementById(nodeInfo);
12686         }else if(typeof nodeInfo == "number"){
12687             return this.nodes[nodeInfo];
12688         }
12689         return nodeInfo;
12690     },
12691
12692     /**
12693      * Gets a range template nodes.
12694      * @param {Number} startIndex
12695      * @param {Number} endIndex
12696      * @return {Array} An array of nodes
12697      */
12698     getNodes : function(start, end){
12699         var ns = this.nodes;
12700         start = start || 0;
12701         end = typeof end == "undefined" ? ns.length - 1 : end;
12702         var nodes = [];
12703         if(start <= end){
12704             for(var i = start; i <= end; i++){
12705                 nodes.push(ns[i]);
12706             }
12707         } else{
12708             for(var i = start; i >= end; i--){
12709                 nodes.push(ns[i]);
12710             }
12711         }
12712         return nodes;
12713     },
12714
12715     /**
12716      * Finds the index of the passed node
12717      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12718      * @return {Number} The index of the node or -1
12719      */
12720     indexOf : function(node){
12721         node = this.getNode(node);
12722         if(typeof node.nodeIndex == "number"){
12723             return node.nodeIndex;
12724         }
12725         var ns = this.nodes;
12726         for(var i = 0, len = ns.length; i < len; i++){
12727             if(ns[i] == node){
12728                 return i;
12729             }
12730         }
12731         return -1;
12732     }
12733 });
12734 /*
12735  * - LGPL
12736  *
12737  * based on jquery fullcalendar
12738  * 
12739  */
12740
12741 Roo.bootstrap = Roo.bootstrap || {};
12742 /**
12743  * @class Roo.bootstrap.Calendar
12744  * @extends Roo.bootstrap.Component
12745  * Bootstrap Calendar class
12746  * @cfg {Boolean} loadMask (true|false) default false
12747  * @cfg {Object} header generate the user specific header of the calendar, default false
12748
12749  * @constructor
12750  * Create a new Container
12751  * @param {Object} config The config object
12752  */
12753
12754
12755
12756 Roo.bootstrap.Calendar = function(config){
12757     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12758      this.addEvents({
12759         /**
12760              * @event select
12761              * Fires when a date is selected
12762              * @param {DatePicker} this
12763              * @param {Date} date The selected date
12764              */
12765         'select': true,
12766         /**
12767              * @event monthchange
12768              * Fires when the displayed month changes 
12769              * @param {DatePicker} this
12770              * @param {Date} date The selected month
12771              */
12772         'monthchange': true,
12773         /**
12774              * @event evententer
12775              * Fires when mouse over an event
12776              * @param {Calendar} this
12777              * @param {event} Event
12778              */
12779         'evententer': true,
12780         /**
12781              * @event eventleave
12782              * Fires when the mouse leaves an
12783              * @param {Calendar} this
12784              * @param {event}
12785              */
12786         'eventleave': true,
12787         /**
12788              * @event eventclick
12789              * Fires when the mouse click an
12790              * @param {Calendar} this
12791              * @param {event}
12792              */
12793         'eventclick': true
12794         
12795     });
12796
12797 };
12798
12799 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12800     
12801      /**
12802      * @cfg {Number} startDay
12803      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12804      */
12805     startDay : 0,
12806     
12807     loadMask : false,
12808     
12809     header : false,
12810       
12811     getAutoCreate : function(){
12812         
12813         
12814         var fc_button = function(name, corner, style, content ) {
12815             return Roo.apply({},{
12816                 tag : 'span',
12817                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12818                          (corner.length ?
12819                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12820                             ''
12821                         ),
12822                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12823                 unselectable: 'on'
12824             });
12825         };
12826         
12827         var header = {};
12828         
12829         if(!this.header){
12830             header = {
12831                 tag : 'table',
12832                 cls : 'fc-header',
12833                 style : 'width:100%',
12834                 cn : [
12835                     {
12836                         tag: 'tr',
12837                         cn : [
12838                             {
12839                                 tag : 'td',
12840                                 cls : 'fc-header-left',
12841                                 cn : [
12842                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12843                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12844                                     { tag: 'span', cls: 'fc-header-space' },
12845                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12846
12847
12848                                 ]
12849                             },
12850
12851                             {
12852                                 tag : 'td',
12853                                 cls : 'fc-header-center',
12854                                 cn : [
12855                                     {
12856                                         tag: 'span',
12857                                         cls: 'fc-header-title',
12858                                         cn : {
12859                                             tag: 'H2',
12860                                             html : 'month / year'
12861                                         }
12862                                     }
12863
12864                                 ]
12865                             },
12866                             {
12867                                 tag : 'td',
12868                                 cls : 'fc-header-right',
12869                                 cn : [
12870                               /*      fc_button('month', 'left', '', 'month' ),
12871                                     fc_button('week', '', '', 'week' ),
12872                                     fc_button('day', 'right', '', 'day' )
12873                                 */    
12874
12875                                 ]
12876                             }
12877
12878                         ]
12879                     }
12880                 ]
12881             };
12882         }
12883         
12884         header = this.header;
12885         
12886        
12887         var cal_heads = function() {
12888             var ret = [];
12889             // fixme - handle this.
12890             
12891             for (var i =0; i < Date.dayNames.length; i++) {
12892                 var d = Date.dayNames[i];
12893                 ret.push({
12894                     tag: 'th',
12895                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12896                     html : d.substring(0,3)
12897                 });
12898                 
12899             }
12900             ret[0].cls += ' fc-first';
12901             ret[6].cls += ' fc-last';
12902             return ret;
12903         };
12904         var cal_cell = function(n) {
12905             return  {
12906                 tag: 'td',
12907                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12908                 cn : [
12909                     {
12910                         cn : [
12911                             {
12912                                 cls: 'fc-day-number',
12913                                 html: 'D'
12914                             },
12915                             {
12916                                 cls: 'fc-day-content',
12917                              
12918                                 cn : [
12919                                      {
12920                                         style: 'position: relative;' // height: 17px;
12921                                     }
12922                                 ]
12923                             }
12924                             
12925                             
12926                         ]
12927                     }
12928                 ]
12929                 
12930             }
12931         };
12932         var cal_rows = function() {
12933             
12934             var ret = []
12935             for (var r = 0; r < 6; r++) {
12936                 var row= {
12937                     tag : 'tr',
12938                     cls : 'fc-week',
12939                     cn : []
12940                 };
12941                 
12942                 for (var i =0; i < Date.dayNames.length; i++) {
12943                     var d = Date.dayNames[i];
12944                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12945
12946                 }
12947                 row.cn[0].cls+=' fc-first';
12948                 row.cn[0].cn[0].style = 'min-height:90px';
12949                 row.cn[6].cls+=' fc-last';
12950                 ret.push(row);
12951                 
12952             }
12953             ret[0].cls += ' fc-first';
12954             ret[4].cls += ' fc-prev-last';
12955             ret[5].cls += ' fc-last';
12956             return ret;
12957             
12958         };
12959         
12960         var cal_table = {
12961             tag: 'table',
12962             cls: 'fc-border-separate',
12963             style : 'width:100%',
12964             cellspacing  : 0,
12965             cn : [
12966                 { 
12967                     tag: 'thead',
12968                     cn : [
12969                         { 
12970                             tag: 'tr',
12971                             cls : 'fc-first fc-last',
12972                             cn : cal_heads()
12973                         }
12974                     ]
12975                 },
12976                 { 
12977                     tag: 'tbody',
12978                     cn : cal_rows()
12979                 }
12980                   
12981             ]
12982         };
12983          
12984          var cfg = {
12985             cls : 'fc fc-ltr',
12986             cn : [
12987                 header,
12988                 {
12989                     cls : 'fc-content',
12990                     style : "position: relative;",
12991                     cn : [
12992                         {
12993                             cls : 'fc-view fc-view-month fc-grid',
12994                             style : 'position: relative',
12995                             unselectable : 'on',
12996                             cn : [
12997                                 {
12998                                     cls : 'fc-event-container',
12999                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13000                                 },
13001                                 cal_table
13002                             ]
13003                         }
13004                     ]
13005     
13006                 }
13007            ] 
13008             
13009         };
13010         
13011          
13012         
13013         return cfg;
13014     },
13015     
13016     
13017     initEvents : function()
13018     {
13019         if(!this.store){
13020             throw "can not find store for calendar";
13021         }
13022         
13023         var mark = {
13024             tag: "div",
13025             cls:"x-dlg-mask",
13026             style: "text-align:center",
13027             cn: [
13028                 {
13029                     tag: "div",
13030                     style: "background-color:white;width:50%;margin:250 auto",
13031                     cn: [
13032                         {
13033                             tag: "img",
13034                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13035                         },
13036                         {
13037                             tag: "span",
13038                             html: "Loading"
13039                         }
13040                         
13041                     ]
13042                 }
13043             ]
13044         }
13045         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13046         
13047         var size = this.el.select('.fc-content', true).first().getSize();
13048         this.maskEl.setSize(size.width, size.height);
13049         this.maskEl.enableDisplayMode("block");
13050         if(!this.loadMask){
13051             this.maskEl.hide();
13052         }
13053         
13054         this.store = Roo.factory(this.store, Roo.data);
13055         this.store.on('load', this.onLoad, this);
13056         this.store.on('beforeload', this.onBeforeLoad, this);
13057         
13058         this.resize();
13059         
13060         this.cells = this.el.select('.fc-day',true);
13061         //Roo.log(this.cells);
13062         this.textNodes = this.el.query('.fc-day-number');
13063         this.cells.addClassOnOver('fc-state-hover');
13064         
13065         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13066         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13067         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13068         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13069         
13070         this.on('monthchange', this.onMonthChange, this);
13071         
13072         this.update(new Date().clearTime());
13073     },
13074     
13075     resize : function() {
13076         var sz  = this.el.getSize();
13077         
13078         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13079         this.el.select('.fc-day-content div',true).setHeight(34);
13080     },
13081     
13082     
13083     // private
13084     showPrevMonth : function(e){
13085         this.update(this.activeDate.add("mo", -1));
13086     },
13087     showToday : function(e){
13088         this.update(new Date().clearTime());
13089     },
13090     // private
13091     showNextMonth : function(e){
13092         this.update(this.activeDate.add("mo", 1));
13093     },
13094
13095     // private
13096     showPrevYear : function(){
13097         this.update(this.activeDate.add("y", -1));
13098     },
13099
13100     // private
13101     showNextYear : function(){
13102         this.update(this.activeDate.add("y", 1));
13103     },
13104
13105     
13106    // private
13107     update : function(date)
13108     {
13109         var vd = this.activeDate;
13110         this.activeDate = date;
13111 //        if(vd && this.el){
13112 //            var t = date.getTime();
13113 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13114 //                Roo.log('using add remove');
13115 //                
13116 //                this.fireEvent('monthchange', this, date);
13117 //                
13118 //                this.cells.removeClass("fc-state-highlight");
13119 //                this.cells.each(function(c){
13120 //                   if(c.dateValue == t){
13121 //                       c.addClass("fc-state-highlight");
13122 //                       setTimeout(function(){
13123 //                            try{c.dom.firstChild.focus();}catch(e){}
13124 //                       }, 50);
13125 //                       return false;
13126 //                   }
13127 //                   return true;
13128 //                });
13129 //                return;
13130 //            }
13131 //        }
13132         
13133         var days = date.getDaysInMonth();
13134         
13135         var firstOfMonth = date.getFirstDateOfMonth();
13136         var startingPos = firstOfMonth.getDay()-this.startDay;
13137         
13138         if(startingPos < this.startDay){
13139             startingPos += 7;
13140         }
13141         
13142         var pm = date.add(Date.MONTH, -1);
13143         var prevStart = pm.getDaysInMonth()-startingPos;
13144 //        
13145         this.cells = this.el.select('.fc-day',true);
13146         this.textNodes = this.el.query('.fc-day-number');
13147         this.cells.addClassOnOver('fc-state-hover');
13148         
13149         var cells = this.cells.elements;
13150         var textEls = this.textNodes;
13151         
13152         Roo.each(cells, function(cell){
13153             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13154         });
13155         
13156         days += startingPos;
13157
13158         // convert everything to numbers so it's fast
13159         var day = 86400000;
13160         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13161         //Roo.log(d);
13162         //Roo.log(pm);
13163         //Roo.log(prevStart);
13164         
13165         var today = new Date().clearTime().getTime();
13166         var sel = date.clearTime().getTime();
13167         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13168         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13169         var ddMatch = this.disabledDatesRE;
13170         var ddText = this.disabledDatesText;
13171         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13172         var ddaysText = this.disabledDaysText;
13173         var format = this.format;
13174         
13175         var setCellClass = function(cal, cell){
13176             cell.row = 0;
13177             cell.events = [];
13178             cell.more = [];
13179             //Roo.log('set Cell Class');
13180             cell.title = "";
13181             var t = d.getTime();
13182             
13183             //Roo.log(d);
13184             
13185             cell.dateValue = t;
13186             if(t == today){
13187                 cell.className += " fc-today";
13188                 cell.className += " fc-state-highlight";
13189                 cell.title = cal.todayText;
13190             }
13191             if(t == sel){
13192                 // disable highlight in other month..
13193                 //cell.className += " fc-state-highlight";
13194                 
13195             }
13196             // disabling
13197             if(t < min) {
13198                 cell.className = " fc-state-disabled";
13199                 cell.title = cal.minText;
13200                 return;
13201             }
13202             if(t > max) {
13203                 cell.className = " fc-state-disabled";
13204                 cell.title = cal.maxText;
13205                 return;
13206             }
13207             if(ddays){
13208                 if(ddays.indexOf(d.getDay()) != -1){
13209                     cell.title = ddaysText;
13210                     cell.className = " fc-state-disabled";
13211                 }
13212             }
13213             if(ddMatch && format){
13214                 var fvalue = d.dateFormat(format);
13215                 if(ddMatch.test(fvalue)){
13216                     cell.title = ddText.replace("%0", fvalue);
13217                     cell.className = " fc-state-disabled";
13218                 }
13219             }
13220             
13221             if (!cell.initialClassName) {
13222                 cell.initialClassName = cell.dom.className;
13223             }
13224             
13225             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13226         };
13227
13228         var i = 0;
13229         
13230         for(; i < startingPos; i++) {
13231             textEls[i].innerHTML = (++prevStart);
13232             d.setDate(d.getDate()+1);
13233             
13234             cells[i].className = "fc-past fc-other-month";
13235             setCellClass(this, cells[i]);
13236         }
13237         
13238         var intDay = 0;
13239         
13240         for(; i < days; i++){
13241             intDay = i - startingPos + 1;
13242             textEls[i].innerHTML = (intDay);
13243             d.setDate(d.getDate()+1);
13244             
13245             cells[i].className = ''; // "x-date-active";
13246             setCellClass(this, cells[i]);
13247         }
13248         var extraDays = 0;
13249         
13250         for(; i < 42; i++) {
13251             textEls[i].innerHTML = (++extraDays);
13252             d.setDate(d.getDate()+1);
13253             
13254             cells[i].className = "fc-future fc-other-month";
13255             setCellClass(this, cells[i]);
13256         }
13257         
13258         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13259         
13260         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13261         
13262         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13263         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13264         
13265         if(totalRows != 6){
13266             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13267             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13268         }
13269         
13270         this.fireEvent('monthchange', this, date);
13271         
13272         
13273         /*
13274         if(!this.internalRender){
13275             var main = this.el.dom.firstChild;
13276             var w = main.offsetWidth;
13277             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13278             Roo.fly(main).setWidth(w);
13279             this.internalRender = true;
13280             // opera does not respect the auto grow header center column
13281             // then, after it gets a width opera refuses to recalculate
13282             // without a second pass
13283             if(Roo.isOpera && !this.secondPass){
13284                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13285                 this.secondPass = true;
13286                 this.update.defer(10, this, [date]);
13287             }
13288         }
13289         */
13290         
13291     },
13292     
13293     findCell : function(dt) {
13294         dt = dt.clearTime().getTime();
13295         var ret = false;
13296         this.cells.each(function(c){
13297             //Roo.log("check " +c.dateValue + '?=' + dt);
13298             if(c.dateValue == dt){
13299                 ret = c;
13300                 return false;
13301             }
13302             return true;
13303         });
13304         
13305         return ret;
13306     },
13307     
13308     findCells : function(ev) {
13309         var s = ev.start.clone().clearTime().getTime();
13310        // Roo.log(s);
13311         var e= ev.end.clone().clearTime().getTime();
13312        // Roo.log(e);
13313         var ret = [];
13314         this.cells.each(function(c){
13315              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13316             
13317             if(c.dateValue > e){
13318                 return ;
13319             }
13320             if(c.dateValue < s){
13321                 return ;
13322             }
13323             ret.push(c);
13324         });
13325         
13326         return ret;    
13327     },
13328     
13329 //    findBestRow: function(cells)
13330 //    {
13331 //        var ret = 0;
13332 //        
13333 //        for (var i =0 ; i < cells.length;i++) {
13334 //            ret  = Math.max(cells[i].rows || 0,ret);
13335 //        }
13336 //        return ret;
13337 //        
13338 //    },
13339     
13340     
13341     addItem : function(ev)
13342     {
13343         // look for vertical location slot in
13344         var cells = this.findCells(ev);
13345         
13346 //        ev.row = this.findBestRow(cells);
13347         
13348         // work out the location.
13349         
13350         var crow = false;
13351         var rows = [];
13352         for(var i =0; i < cells.length; i++) {
13353             
13354             cells[i].row = cells[0].row;
13355             
13356             if(i == 0){
13357                 cells[i].row = cells[i].row + 1;
13358             }
13359             
13360             if (!crow) {
13361                 crow = {
13362                     start : cells[i],
13363                     end :  cells[i]
13364                 };
13365                 continue;
13366             }
13367             if (crow.start.getY() == cells[i].getY()) {
13368                 // on same row.
13369                 crow.end = cells[i];
13370                 continue;
13371             }
13372             // different row.
13373             rows.push(crow);
13374             crow = {
13375                 start: cells[i],
13376                 end : cells[i]
13377             };
13378             
13379         }
13380         
13381         rows.push(crow);
13382         ev.els = [];
13383         ev.rows = rows;
13384         ev.cells = cells;
13385         
13386         cells[0].events.push(ev);
13387         
13388         this.calevents.push(ev);
13389     },
13390     
13391     clearEvents: function() {
13392         
13393         if(!this.calevents){
13394             return;
13395         }
13396         
13397         Roo.each(this.cells.elements, function(c){
13398             c.row = 0;
13399             c.events = [];
13400             c.more = [];
13401         });
13402         
13403         Roo.each(this.calevents, function(e) {
13404             Roo.each(e.els, function(el) {
13405                 el.un('mouseenter' ,this.onEventEnter, this);
13406                 el.un('mouseleave' ,this.onEventLeave, this);
13407                 el.remove();
13408             },this);
13409         },this);
13410         
13411         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13412             e.remove();
13413         });
13414         
13415     },
13416     
13417     renderEvents: function()
13418     {   
13419         var _this = this;
13420         
13421         this.cells.each(function(c) {
13422             
13423             if(c.row < 5){
13424                 return;
13425             }
13426             
13427             var ev = c.events;
13428             
13429             var r = 4;
13430             if(c.row != c.events.length){
13431                 r = 4 - (4 - (c.row - c.events.length));
13432             }
13433             
13434             c.events = ev.slice(0, r);
13435             c.more = ev.slice(r);
13436             
13437             if(c.more.length && c.more.length == 1){
13438                 c.events.push(c.more.pop());
13439             }
13440             
13441             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13442             
13443         });
13444             
13445         this.cells.each(function(c) {
13446             
13447             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13448             
13449             
13450             for (var e = 0; e < c.events.length; e++){
13451                 var ev = c.events[e];
13452                 var rows = ev.rows;
13453                 
13454                 for(var i = 0; i < rows.length; i++) {
13455                 
13456                     // how many rows should it span..
13457
13458                     var  cfg = {
13459                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13460                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13461
13462                         unselectable : "on",
13463                         cn : [
13464                             {
13465                                 cls: 'fc-event-inner',
13466                                 cn : [
13467     //                                {
13468     //                                  tag:'span',
13469     //                                  cls: 'fc-event-time',
13470     //                                  html : cells.length > 1 ? '' : ev.time
13471     //                                },
13472                                     {
13473                                       tag:'span',
13474                                       cls: 'fc-event-title',
13475                                       html : String.format('{0}', ev.title)
13476                                     }
13477
13478
13479                                 ]
13480                             },
13481                             {
13482                                 cls: 'ui-resizable-handle ui-resizable-e',
13483                                 html : '&nbsp;&nbsp;&nbsp'
13484                             }
13485
13486                         ]
13487                     };
13488
13489                     if (i == 0) {
13490                         cfg.cls += ' fc-event-start';
13491                     }
13492                     if ((i+1) == rows.length) {
13493                         cfg.cls += ' fc-event-end';
13494                     }
13495
13496                     var ctr = _this.el.select('.fc-event-container',true).first();
13497                     var cg = ctr.createChild(cfg);
13498
13499                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13500                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13501
13502                     var r = (c.more.length) ? 1 : 0;
13503                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13504                     cg.setWidth(ebox.right - sbox.x -2);
13505
13506                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13507                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13508                     cg.on('click', _this.onEventClick, _this, ev);
13509
13510                     ev.els.push(cg);
13511                     
13512                 }
13513                 
13514             }
13515             
13516             
13517             if(c.more.length){
13518                 var  cfg = {
13519                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13520                     style : 'position: absolute',
13521                     unselectable : "on",
13522                     cn : [
13523                         {
13524                             cls: 'fc-event-inner',
13525                             cn : [
13526                                 {
13527                                   tag:'span',
13528                                   cls: 'fc-event-title',
13529                                   html : 'More'
13530                                 }
13531
13532
13533                             ]
13534                         },
13535                         {
13536                             cls: 'ui-resizable-handle ui-resizable-e',
13537                             html : '&nbsp;&nbsp;&nbsp'
13538                         }
13539
13540                     ]
13541                 };
13542
13543                 var ctr = _this.el.select('.fc-event-container',true).first();
13544                 var cg = ctr.createChild(cfg);
13545
13546                 var sbox = c.select('.fc-day-content',true).first().getBox();
13547                 var ebox = c.select('.fc-day-content',true).first().getBox();
13548                 //Roo.log(cg);
13549                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13550                 cg.setWidth(ebox.right - sbox.x -2);
13551
13552                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13553                 
13554             }
13555             
13556         });
13557         
13558         
13559         
13560     },
13561     
13562     onEventEnter: function (e, el,event,d) {
13563         this.fireEvent('evententer', this, el, event);
13564     },
13565     
13566     onEventLeave: function (e, el,event,d) {
13567         this.fireEvent('eventleave', this, el, event);
13568     },
13569     
13570     onEventClick: function (e, el,event,d) {
13571         this.fireEvent('eventclick', this, el, event);
13572     },
13573     
13574     onMonthChange: function () {
13575         this.store.load();
13576     },
13577     
13578     onMoreEventClick: function(e, el, more)
13579     {
13580         var _this = this;
13581         
13582         this.calpopover.placement = 'right';
13583         this.calpopover.setTitle('More');
13584         
13585         this.calpopover.setContent('');
13586         
13587         var ctr = this.calpopover.el.select('.popover-content', true).first();
13588         
13589         Roo.each(more, function(m){
13590             var cfg = {
13591                 cls : 'fc-event-hori fc-event-draggable',
13592                 html : m.title
13593             }
13594             var cg = ctr.createChild(cfg);
13595             
13596             cg.on('click', _this.onEventClick, _this, m);
13597         });
13598         
13599         this.calpopover.show(el);
13600         
13601         
13602     },
13603     
13604     onLoad: function () 
13605     {   
13606         this.calevents = [];
13607         var cal = this;
13608         
13609         if(this.store.getCount() > 0){
13610             this.store.data.each(function(d){
13611                cal.addItem({
13612                     id : d.data.id,
13613                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13614                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13615                     time : d.data.start_time,
13616                     title : d.data.title,
13617                     description : d.data.description,
13618                     venue : d.data.venue
13619                 });
13620             });
13621         }
13622         
13623         this.renderEvents();
13624         
13625         if(this.calevents.length && this.loadMask){
13626             this.maskEl.hide();
13627         }
13628     },
13629     
13630     onBeforeLoad: function()
13631     {
13632         this.clearEvents();
13633         if(this.loadMask){
13634             this.maskEl.show();
13635         }
13636     }
13637 });
13638
13639  
13640  /*
13641  * - LGPL
13642  *
13643  * element
13644  * 
13645  */
13646
13647 /**
13648  * @class Roo.bootstrap.Popover
13649  * @extends Roo.bootstrap.Component
13650  * Bootstrap Popover class
13651  * @cfg {String} html contents of the popover   (or false to use children..)
13652  * @cfg {String} title of popover (or false to hide)
13653  * @cfg {String} placement how it is placed
13654  * @cfg {String} trigger click || hover (or false to trigger manually)
13655  * @cfg {String} over what (parent or false to trigger manually.)
13656  * @cfg {Number} delay - delay before showing
13657  
13658  * @constructor
13659  * Create a new Popover
13660  * @param {Object} config The config object
13661  */
13662
13663 Roo.bootstrap.Popover = function(config){
13664     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13665 };
13666
13667 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13668     
13669     title: 'Fill in a title',
13670     html: false,
13671     
13672     placement : 'right',
13673     trigger : 'hover', // hover
13674     
13675     delay : 0,
13676     
13677     over: 'parent',
13678     
13679     can_build_overlaid : false,
13680     
13681     getChildContainer : function()
13682     {
13683         return this.el.select('.popover-content',true).first();
13684     },
13685     
13686     getAutoCreate : function(){
13687          Roo.log('make popover?');
13688         var cfg = {
13689            cls : 'popover roo-dynamic',
13690            style: 'display:block',
13691            cn : [
13692                 {
13693                     cls : 'arrow'
13694                 },
13695                 {
13696                     cls : 'popover-inner',
13697                     cn : [
13698                         {
13699                             tag: 'h3',
13700                             cls: 'popover-title',
13701                             html : this.title
13702                         },
13703                         {
13704                             cls : 'popover-content',
13705                             html : this.html
13706                         }
13707                     ]
13708                     
13709                 }
13710            ]
13711         };
13712         
13713         return cfg;
13714     },
13715     setTitle: function(str)
13716     {
13717         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13718     },
13719     setContent: function(str)
13720     {
13721         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13722     },
13723     // as it get's added to the bottom of the page.
13724     onRender : function(ct, position)
13725     {
13726         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13727         if(!this.el){
13728             var cfg = Roo.apply({},  this.getAutoCreate());
13729             cfg.id = Roo.id();
13730             
13731             if (this.cls) {
13732                 cfg.cls += ' ' + this.cls;
13733             }
13734             if (this.style) {
13735                 cfg.style = this.style;
13736             }
13737             Roo.log("adding to ")
13738             this.el = Roo.get(document.body).createChild(cfg, position);
13739             Roo.log(this.el);
13740         }
13741         this.initEvents();
13742     },
13743     
13744     initEvents : function()
13745     {
13746         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13747         this.el.enableDisplayMode('block');
13748         this.el.hide();
13749         if (this.over === false) {
13750             return; 
13751         }
13752         if (this.triggers === false) {
13753             return;
13754         }
13755         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13756         var triggers = this.trigger ? this.trigger.split(' ') : [];
13757         Roo.each(triggers, function(trigger) {
13758         
13759             if (trigger == 'click') {
13760                 on_el.on('click', this.toggle, this);
13761             } else if (trigger != 'manual') {
13762                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13763                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13764       
13765                 on_el.on(eventIn  ,this.enter, this);
13766                 on_el.on(eventOut, this.leave, this);
13767             }
13768         }, this);
13769         
13770     },
13771     
13772     
13773     // private
13774     timeout : null,
13775     hoverState : null,
13776     
13777     toggle : function () {
13778         this.hoverState == 'in' ? this.leave() : this.enter();
13779     },
13780     
13781     enter : function () {
13782        
13783     
13784         clearTimeout(this.timeout);
13785     
13786         this.hoverState = 'in'
13787     
13788         if (!this.delay || !this.delay.show) {
13789             this.show();
13790             return 
13791         }
13792         var _t = this;
13793         this.timeout = setTimeout(function () {
13794             if (_t.hoverState == 'in') {
13795                 _t.show();
13796             }
13797         }, this.delay.show)
13798     },
13799     leave : function() {
13800         clearTimeout(this.timeout);
13801     
13802         this.hoverState = 'out'
13803     
13804         if (!this.delay || !this.delay.hide) {
13805             this.hide();
13806             return 
13807         }
13808         var _t = this;
13809         this.timeout = setTimeout(function () {
13810             if (_t.hoverState == 'out') {
13811                 _t.hide();
13812             }
13813         }, this.delay.hide)
13814     },
13815     
13816     show : function (on_el)
13817     {
13818         if (!on_el) {
13819             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13820         }
13821         // set content.
13822         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13823         if (this.html !== false) {
13824             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13825         }
13826         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13827         if (!this.title.length) {
13828             this.el.select('.popover-title',true).hide();
13829         }
13830         
13831         var placement = typeof this.placement == 'function' ?
13832             this.placement.call(this, this.el, on_el) :
13833             this.placement;
13834             
13835         var autoToken = /\s?auto?\s?/i;
13836         var autoPlace = autoToken.test(placement);
13837         if (autoPlace) {
13838             placement = placement.replace(autoToken, '') || 'top';
13839         }
13840         
13841         //this.el.detach()
13842         //this.el.setXY([0,0]);
13843         this.el.show();
13844         this.el.dom.style.display='block';
13845         this.el.addClass(placement);
13846         
13847         //this.el.appendTo(on_el);
13848         
13849         var p = this.getPosition();
13850         var box = this.el.getBox();
13851         
13852         if (autoPlace) {
13853             // fixme..
13854         }
13855         var align = Roo.bootstrap.Popover.alignment[placement]
13856         this.el.alignTo(on_el, align[0],align[1]);
13857         //var arrow = this.el.select('.arrow',true).first();
13858         //arrow.set(align[2], 
13859         
13860         this.el.addClass('in');
13861         this.hoverState = null;
13862         
13863         if (this.el.hasClass('fade')) {
13864             // fade it?
13865         }
13866         
13867     },
13868     hide : function()
13869     {
13870         this.el.setXY([0,0]);
13871         this.el.removeClass('in');
13872         this.el.hide();
13873         
13874     }
13875     
13876 });
13877
13878 Roo.bootstrap.Popover.alignment = {
13879     'left' : ['r-l', [-10,0], 'right'],
13880     'right' : ['l-r', [10,0], 'left'],
13881     'bottom' : ['t-b', [0,10], 'top'],
13882     'top' : [ 'b-t', [0,-10], 'bottom']
13883 };
13884
13885  /*
13886  * - LGPL
13887  *
13888  * Progress
13889  * 
13890  */
13891
13892 /**
13893  * @class Roo.bootstrap.Progress
13894  * @extends Roo.bootstrap.Component
13895  * Bootstrap Progress class
13896  * @cfg {Boolean} striped striped of the progress bar
13897  * @cfg {Boolean} active animated of the progress bar
13898  * 
13899  * 
13900  * @constructor
13901  * Create a new Progress
13902  * @param {Object} config The config object
13903  */
13904
13905 Roo.bootstrap.Progress = function(config){
13906     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13907 };
13908
13909 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13910     
13911     striped : false,
13912     active: false,
13913     
13914     getAutoCreate : function(){
13915         var cfg = {
13916             tag: 'div',
13917             cls: 'progress'
13918         };
13919         
13920         
13921         if(this.striped){
13922             cfg.cls += ' progress-striped';
13923         }
13924       
13925         if(this.active){
13926             cfg.cls += ' active';
13927         }
13928         
13929         
13930         return cfg;
13931     }
13932    
13933 });
13934
13935  
13936
13937  /*
13938  * - LGPL
13939  *
13940  * ProgressBar
13941  * 
13942  */
13943
13944 /**
13945  * @class Roo.bootstrap.ProgressBar
13946  * @extends Roo.bootstrap.Component
13947  * Bootstrap ProgressBar class
13948  * @cfg {Number} aria_valuenow aria-value now
13949  * @cfg {Number} aria_valuemin aria-value min
13950  * @cfg {Number} aria_valuemax aria-value max
13951  * @cfg {String} label label for the progress bar
13952  * @cfg {String} panel (success | info | warning | danger )
13953  * @cfg {String} role role of the progress bar
13954  * @cfg {String} sr_only text
13955  * 
13956  * 
13957  * @constructor
13958  * Create a new ProgressBar
13959  * @param {Object} config The config object
13960  */
13961
13962 Roo.bootstrap.ProgressBar = function(config){
13963     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13964 };
13965
13966 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13967     
13968     aria_valuenow : 0,
13969     aria_valuemin : 0,
13970     aria_valuemax : 100,
13971     label : false,
13972     panel : false,
13973     role : false,
13974     sr_only: false,
13975     
13976     getAutoCreate : function()
13977     {
13978         
13979         var cfg = {
13980             tag: 'div',
13981             cls: 'progress-bar',
13982             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13983         };
13984         
13985         if(this.sr_only){
13986             cfg.cn = {
13987                 tag: 'span',
13988                 cls: 'sr-only',
13989                 html: this.sr_only
13990             }
13991         }
13992         
13993         if(this.role){
13994             cfg.role = this.role;
13995         }
13996         
13997         if(this.aria_valuenow){
13998             cfg['aria-valuenow'] = this.aria_valuenow;
13999         }
14000         
14001         if(this.aria_valuemin){
14002             cfg['aria-valuemin'] = this.aria_valuemin;
14003         }
14004         
14005         if(this.aria_valuemax){
14006             cfg['aria-valuemax'] = this.aria_valuemax;
14007         }
14008         
14009         if(this.label && !this.sr_only){
14010             cfg.html = this.label;
14011         }
14012         
14013         if(this.panel){
14014             cfg.cls += ' progress-bar-' + this.panel;
14015         }
14016         
14017         return cfg;
14018     },
14019     
14020     update : function(aria_valuenow)
14021     {
14022         this.aria_valuenow = aria_valuenow;
14023         
14024         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14025     }
14026    
14027 });
14028
14029  
14030
14031  /*
14032  * - LGPL
14033  *
14034  * column
14035  * 
14036  */
14037
14038 /**
14039  * @class Roo.bootstrap.TabGroup
14040  * @extends Roo.bootstrap.Column
14041  * Bootstrap Column class
14042  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14043  * @cfg {Boolean} carousel true to make the group behave like a carousel
14044  * 
14045  * @constructor
14046  * Create a new TabGroup
14047  * @param {Object} config The config object
14048  */
14049
14050 Roo.bootstrap.TabGroup = function(config){
14051     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14052     if (!this.navId) {
14053         this.navId = Roo.id();
14054     }
14055     this.tabs = [];
14056     Roo.bootstrap.TabGroup.register(this);
14057     
14058 };
14059
14060 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14061     
14062     carousel : false,
14063     transition : false,
14064      
14065     getAutoCreate : function()
14066     {
14067         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14068         
14069         cfg.cls += ' tab-content';
14070         
14071         if (this.carousel) {
14072             cfg.cls += ' carousel slide';
14073             cfg.cn = [{
14074                cls : 'carousel-inner'
14075             }]
14076         }
14077         
14078         
14079         return cfg;
14080     },
14081     getChildContainer : function()
14082     {
14083         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14084     },
14085     
14086     /**
14087     * register a Navigation item
14088     * @param {Roo.bootstrap.NavItem} the navitem to add
14089     */
14090     register : function(item)
14091     {
14092         this.tabs.push( item);
14093         item.navId = this.navId; // not really needed..
14094     
14095     },
14096     
14097     getActivePanel : function()
14098     {
14099         var r = false;
14100         Roo.each(this.tabs, function(t) {
14101             if (t.active) {
14102                 r = t;
14103                 return false;
14104             }
14105             return null;
14106         });
14107         return r;
14108         
14109     },
14110     getPanelByName : function(n)
14111     {
14112         var r = false;
14113         Roo.each(this.tabs, function(t) {
14114             if (t.tabId == n) {
14115                 r = t;
14116                 return false;
14117             }
14118             return null;
14119         });
14120         return r;
14121     },
14122     indexOfPanel : function(p)
14123     {
14124         var r = false;
14125         Roo.each(this.tabs, function(t,i) {
14126             if (t.tabId == p.tabId) {
14127                 r = i;
14128                 return false;
14129             }
14130             return null;
14131         });
14132         return r;
14133     },
14134     /**
14135      * show a specific panel
14136      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14137      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14138      */
14139     showPanel : function (pan)
14140     {
14141         
14142         if (typeof(pan) == 'number') {
14143             pan = this.tabs[pan];
14144         }
14145         if (typeof(pan) == 'string') {
14146             pan = this.getPanelByName(pan);
14147         }
14148         if (pan.tabId == this.getActivePanel().tabId) {
14149             return true;
14150         }
14151         var cur = this.getActivePanel();
14152         
14153         if (false === cur.fireEvent('beforedeactivate')) {
14154             return false;
14155         }
14156         
14157         if (this.carousel) {
14158             this.transition = true;
14159             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14160             var lr = dir == 'next' ? 'left' : 'right';
14161             pan.el.addClass(dir); // or prev
14162             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14163             cur.el.addClass(lr); // or right
14164             pan.el.addClass(lr);
14165             
14166             var _this = this;
14167             cur.el.on('transitionend', function() {
14168                 Roo.log("trans end?");
14169                 
14170                 pan.el.removeClass([lr,dir]);
14171                 pan.setActive(true);
14172                 
14173                 cur.el.removeClass([lr]);
14174                 cur.setActive(false);
14175                 
14176                 _this.transition = false;
14177                 
14178             }, this, { single:  true } );
14179             return true;
14180         }
14181         
14182         cur.setActive(false);
14183         pan.setActive(true);
14184         return true;
14185         
14186     },
14187     showPanelNext : function()
14188     {
14189         var i = this.indexOfPanel(this.getActivePanel());
14190         if (i > this.tabs.length) {
14191             return;
14192         }
14193         this.showPanel(this.tabs[i+1]);
14194     },
14195     showPanelPrev : function()
14196     {
14197         var i = this.indexOfPanel(this.getActivePanel());
14198         if (i  < 1) {
14199             return;
14200         }
14201         this.showPanel(this.tabs[i-1]);
14202     }
14203     
14204     
14205   
14206 });
14207
14208  
14209
14210  
14211  
14212 Roo.apply(Roo.bootstrap.TabGroup, {
14213     
14214     groups: {},
14215      /**
14216     * register a Navigation Group
14217     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14218     */
14219     register : function(navgrp)
14220     {
14221         this.groups[navgrp.navId] = navgrp;
14222         
14223     },
14224     /**
14225     * fetch a Navigation Group based on the navigation ID
14226     * if one does not exist , it will get created.
14227     * @param {string} the navgroup to add
14228     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14229     */
14230     get: function(navId) {
14231         if (typeof(this.groups[navId]) == 'undefined') {
14232             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14233         }
14234         return this.groups[navId] ;
14235     }
14236     
14237     
14238     
14239 });
14240
14241  /*
14242  * - LGPL
14243  *
14244  * TabPanel
14245  * 
14246  */
14247
14248 /**
14249  * @class Roo.bootstrap.TabPanel
14250  * @extends Roo.bootstrap.Component
14251  * Bootstrap TabPanel class
14252  * @cfg {Boolean} active panel active
14253  * @cfg {String} html panel content
14254  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14255  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14256  * 
14257  * 
14258  * @constructor
14259  * Create a new TabPanel
14260  * @param {Object} config The config object
14261  */
14262
14263 Roo.bootstrap.TabPanel = function(config){
14264     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14265     this.addEvents({
14266         /**
14267              * @event changed
14268              * Fires when the active status changes
14269              * @param {Roo.bootstrap.TabPanel} this
14270              * @param {Boolean} state the new state
14271             
14272          */
14273         'changed': true,
14274         /**
14275              * @event beforedeactivate
14276              * Fires before a tab is de-activated - can be used to do validation on a form.
14277              * @param {Roo.bootstrap.TabPanel} this
14278              * @return {Boolean} false if there is an error
14279             
14280          */
14281         'beforedeactivate': true
14282      });
14283     
14284     this.tabId = this.tabId || Roo.id();
14285   
14286 };
14287
14288 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14289     
14290     active: false,
14291     html: false,
14292     tabId: false,
14293     navId : false,
14294     
14295     getAutoCreate : function(){
14296         var cfg = {
14297             tag: 'div',
14298             // item is needed for carousel - not sure if it has any effect otherwise
14299             cls: 'tab-pane item',
14300             html: this.html || ''
14301         };
14302         
14303         if(this.active){
14304             cfg.cls += ' active';
14305         }
14306         
14307         if(this.tabId){
14308             cfg.tabId = this.tabId;
14309         }
14310         
14311         
14312         return cfg;
14313     },
14314     
14315     initEvents:  function()
14316     {
14317         Roo.log('-------- init events on tab panel ---------');
14318         
14319         var p = this.parent();
14320         this.navId = this.navId || p.navId;
14321         
14322         if (typeof(this.navId) != 'undefined') {
14323             // not really needed.. but just in case.. parent should be a NavGroup.
14324             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14325             Roo.log(['register', tg, this]);
14326             tg.register(this);
14327         }
14328     },
14329     
14330     
14331     onRender : function(ct, position)
14332     {
14333        // Roo.log("Call onRender: " + this.xtype);
14334         
14335         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14336         
14337         
14338         
14339         
14340         
14341     },
14342     
14343     setActive: function(state)
14344     {
14345         Roo.log("panel - set active " + this.tabId + "=" + state);
14346         
14347         this.active = state;
14348         if (!state) {
14349             this.el.removeClass('active');
14350             
14351         } else  if (!this.el.hasClass('active')) {
14352             this.el.addClass('active');
14353         }
14354         this.fireEvent('changed', this, state);
14355     }
14356     
14357     
14358 });
14359  
14360
14361  
14362
14363  /*
14364  * - LGPL
14365  *
14366  * DateField
14367  * 
14368  */
14369
14370 /**
14371  * @class Roo.bootstrap.DateField
14372  * @extends Roo.bootstrap.Input
14373  * Bootstrap DateField class
14374  * @cfg {Number} weekStart default 0
14375  * @cfg {Number} weekStart default 0
14376  * @cfg {Number} viewMode default empty, (months|years)
14377  * @cfg {Number} minViewMode default empty, (months|years)
14378  * @cfg {Number} startDate default -Infinity
14379  * @cfg {Number} endDate default Infinity
14380  * @cfg {Boolean} todayHighlight default false
14381  * @cfg {Boolean} todayBtn default false
14382  * @cfg {Boolean} calendarWeeks default false
14383  * @cfg {Object} daysOfWeekDisabled default empty
14384  * 
14385  * @cfg {Boolean} keyboardNavigation default true
14386  * @cfg {String} language default en
14387  * 
14388  * @constructor
14389  * Create a new DateField
14390  * @param {Object} config The config object
14391  */
14392
14393 Roo.bootstrap.DateField = function(config){
14394     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14395      this.addEvents({
14396             /**
14397              * @event show
14398              * Fires when this field show.
14399              * @param {Roo.bootstrap.DateField} this
14400              * @param {Mixed} date The date value
14401              */
14402             show : true,
14403             /**
14404              * @event show
14405              * Fires when this field hide.
14406              * @param {Roo.bootstrap.DateField} this
14407              * @param {Mixed} date The date value
14408              */
14409             hide : true,
14410             /**
14411              * @event select
14412              * Fires when select a date.
14413              * @param {Roo.bootstrap.DateField} this
14414              * @param {Mixed} date The date value
14415              */
14416             select : true
14417         });
14418 };
14419
14420 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14421     
14422     /**
14423      * @cfg {String} format
14424      * The default date format string which can be overriden for localization support.  The format must be
14425      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14426      */
14427     format : "m/d/y",
14428     /**
14429      * @cfg {String} altFormats
14430      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14431      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14432      */
14433     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14434     
14435     weekStart : 0,
14436     
14437     viewMode : '',
14438     
14439     minViewMode : '',
14440     
14441     todayHighlight : false,
14442     
14443     todayBtn: false,
14444     
14445     language: 'en',
14446     
14447     keyboardNavigation: true,
14448     
14449     calendarWeeks: false,
14450     
14451     startDate: -Infinity,
14452     
14453     endDate: Infinity,
14454     
14455     daysOfWeekDisabled: [],
14456     
14457     _events: [],
14458     
14459     UTCDate: function()
14460     {
14461         return new Date(Date.UTC.apply(Date, arguments));
14462     },
14463     
14464     UTCToday: function()
14465     {
14466         var today = new Date();
14467         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14468     },
14469     
14470     getDate: function() {
14471             var d = this.getUTCDate();
14472             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14473     },
14474     
14475     getUTCDate: function() {
14476             return this.date;
14477     },
14478     
14479     setDate: function(d) {
14480             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14481     },
14482     
14483     setUTCDate: function(d) {
14484             this.date = d;
14485             this.setValue(this.formatDate(this.date));
14486     },
14487         
14488     onRender: function(ct, position)
14489     {
14490         
14491         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14492         
14493         this.language = this.language || 'en';
14494         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14495         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14496         
14497         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14498         this.format = this.format || 'm/d/y';
14499         this.isInline = false;
14500         this.isInput = true;
14501         this.component = this.el.select('.add-on', true).first() || false;
14502         this.component = (this.component && this.component.length === 0) ? false : this.component;
14503         this.hasInput = this.component && this.inputEL().length;
14504         
14505         if (typeof(this.minViewMode === 'string')) {
14506             switch (this.minViewMode) {
14507                 case 'months':
14508                     this.minViewMode = 1;
14509                     break;
14510                 case 'years':
14511                     this.minViewMode = 2;
14512                     break;
14513                 default:
14514                     this.minViewMode = 0;
14515                     break;
14516             }
14517         }
14518         
14519         if (typeof(this.viewMode === 'string')) {
14520             switch (this.viewMode) {
14521                 case 'months':
14522                     this.viewMode = 1;
14523                     break;
14524                 case 'years':
14525                     this.viewMode = 2;
14526                     break;
14527                 default:
14528                     this.viewMode = 0;
14529                     break;
14530             }
14531         }
14532                 
14533         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14534         
14535 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14536         
14537         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14538         
14539         this.picker().on('mousedown', this.onMousedown, this);
14540         this.picker().on('click', this.onClick, this);
14541         
14542         this.picker().addClass('datepicker-dropdown');
14543         
14544         this.startViewMode = this.viewMode;
14545         
14546         
14547         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14548             if(!this.calendarWeeks){
14549                 v.remove();
14550                 return;
14551             };
14552             
14553             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14554             v.attr('colspan', function(i, val){
14555                 return parseInt(val) + 1;
14556             });
14557         })
14558                         
14559         
14560         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14561         
14562         this.setStartDate(this.startDate);
14563         this.setEndDate(this.endDate);
14564         
14565         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14566         
14567         this.fillDow();
14568         this.fillMonths();
14569         this.update();
14570         this.showMode();
14571         
14572         if(this.isInline) {
14573             this.show();
14574         }
14575     },
14576     
14577     picker : function()
14578     {
14579         return this.pickerEl;
14580 //        return this.el.select('.datepicker', true).first();
14581     },
14582     
14583     fillDow: function()
14584     {
14585         var dowCnt = this.weekStart;
14586         
14587         var dow = {
14588             tag: 'tr',
14589             cn: [
14590                 
14591             ]
14592         };
14593         
14594         if(this.calendarWeeks){
14595             dow.cn.push({
14596                 tag: 'th',
14597                 cls: 'cw',
14598                 html: '&nbsp;'
14599             })
14600         }
14601         
14602         while (dowCnt < this.weekStart + 7) {
14603             dow.cn.push({
14604                 tag: 'th',
14605                 cls: 'dow',
14606                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14607             });
14608         }
14609         
14610         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14611     },
14612     
14613     fillMonths: function()
14614     {    
14615         var i = 0
14616         var months = this.picker().select('>.datepicker-months td', true).first();
14617         
14618         months.dom.innerHTML = '';
14619         
14620         while (i < 12) {
14621             var month = {
14622                 tag: 'span',
14623                 cls: 'month',
14624                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14625             }
14626             
14627             months.createChild(month);
14628         }
14629         
14630     },
14631     
14632     update: function()
14633     {
14634         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;
14635         
14636         if (this.date < this.startDate) {
14637             this.viewDate = new Date(this.startDate);
14638         } else if (this.date > this.endDate) {
14639             this.viewDate = new Date(this.endDate);
14640         } else {
14641             this.viewDate = new Date(this.date);
14642         }
14643         
14644         this.fill();
14645     },
14646     
14647     fill: function() 
14648     {
14649         var d = new Date(this.viewDate),
14650                 year = d.getUTCFullYear(),
14651                 month = d.getUTCMonth(),
14652                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14653                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14654                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14655                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14656                 currentDate = this.date && this.date.valueOf(),
14657                 today = this.UTCToday();
14658         
14659         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14660         
14661 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14662         
14663 //        this.picker.select('>tfoot th.today').
14664 //                                              .text(dates[this.language].today)
14665 //                                              .toggle(this.todayBtn !== false);
14666     
14667         this.updateNavArrows();
14668         this.fillMonths();
14669                                                 
14670         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14671         
14672         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14673          
14674         prevMonth.setUTCDate(day);
14675         
14676         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14677         
14678         var nextMonth = new Date(prevMonth);
14679         
14680         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14681         
14682         nextMonth = nextMonth.valueOf();
14683         
14684         var fillMonths = false;
14685         
14686         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14687         
14688         while(prevMonth.valueOf() < nextMonth) {
14689             var clsName = '';
14690             
14691             if (prevMonth.getUTCDay() === this.weekStart) {
14692                 if(fillMonths){
14693                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14694                 }
14695                     
14696                 fillMonths = {
14697                     tag: 'tr',
14698                     cn: []
14699                 };
14700                 
14701                 if(this.calendarWeeks){
14702                     // ISO 8601: First week contains first thursday.
14703                     // ISO also states week starts on Monday, but we can be more abstract here.
14704                     var
14705                     // Start of current week: based on weekstart/current date
14706                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14707                     // Thursday of this week
14708                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14709                     // First Thursday of year, year from thursday
14710                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14711                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14712                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14713                     
14714                     fillMonths.cn.push({
14715                         tag: 'td',
14716                         cls: 'cw',
14717                         html: calWeek
14718                     });
14719                 }
14720             }
14721             
14722             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14723                 clsName += ' old';
14724             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14725                 clsName += ' new';
14726             }
14727             if (this.todayHighlight &&
14728                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14729                 prevMonth.getUTCMonth() == today.getMonth() &&
14730                 prevMonth.getUTCDate() == today.getDate()) {
14731                 clsName += ' today';
14732             }
14733             
14734             if (currentDate && prevMonth.valueOf() === currentDate) {
14735                 clsName += ' active';
14736             }
14737             
14738             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14739                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14740                     clsName += ' disabled';
14741             }
14742             
14743             fillMonths.cn.push({
14744                 tag: 'td',
14745                 cls: 'day ' + clsName,
14746                 html: prevMonth.getDate()
14747             })
14748             
14749             prevMonth.setDate(prevMonth.getDate()+1);
14750         }
14751           
14752         var currentYear = this.date && this.date.getUTCFullYear();
14753         var currentMonth = this.date && this.date.getUTCMonth();
14754         
14755         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14756         
14757         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14758             v.removeClass('active');
14759             
14760             if(currentYear === year && k === currentMonth){
14761                 v.addClass('active');
14762             }
14763             
14764             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14765                 v.addClass('disabled');
14766             }
14767             
14768         });
14769         
14770         
14771         year = parseInt(year/10, 10) * 10;
14772         
14773         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14774         
14775         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14776         
14777         year -= 1;
14778         for (var i = -1; i < 11; i++) {
14779             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14780                 tag: 'span',
14781                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14782                 html: year
14783             })
14784             
14785             year += 1;
14786         }
14787     },
14788     
14789     showMode: function(dir) 
14790     {
14791         if (dir) {
14792             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14793         }
14794         Roo.each(this.picker().select('>div',true).elements, function(v){
14795             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14796             v.hide();
14797         });
14798         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14799     },
14800     
14801     place: function()
14802     {
14803         if(this.isInline) return;
14804         
14805         this.picker().removeClass(['bottom', 'top']);
14806         
14807         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14808             /*
14809              * place to the top of element!
14810              *
14811              */
14812             
14813             this.picker().addClass('top');
14814             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14815             
14816             return;
14817         }
14818         
14819         this.picker().addClass('bottom');
14820         
14821         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14822     },
14823     
14824     parseDate : function(value)
14825     {
14826         if(!value || value instanceof Date){
14827             return value;
14828         }
14829         var v = Date.parseDate(value, this.format);
14830         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14831             v = Date.parseDate(value, 'Y-m-d');
14832         }
14833         if(!v && this.altFormats){
14834             if(!this.altFormatsArray){
14835                 this.altFormatsArray = this.altFormats.split("|");
14836             }
14837             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14838                 v = Date.parseDate(value, this.altFormatsArray[i]);
14839             }
14840         }
14841         return v;
14842     },
14843     
14844     formatDate : function(date, fmt)
14845     {
14846         return (!date || !(date instanceof Date)) ?
14847         date : date.dateFormat(fmt || this.format);
14848     },
14849     
14850     onFocus : function()
14851     {
14852         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14853         this.show();
14854     },
14855     
14856     onBlur : function()
14857     {
14858         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14859         
14860         var d = this.inputEl().getValue();
14861         
14862         this.setValue(d);
14863                 
14864         this.hide();
14865     },
14866     
14867     show : function()
14868     {
14869         this.picker().show();
14870         this.update();
14871         this.place();
14872         
14873         this.fireEvent('show', this, this.date);
14874     },
14875     
14876     hide : function()
14877     {
14878         if(this.isInline) return;
14879         this.picker().hide();
14880         this.viewMode = this.startViewMode;
14881         this.showMode();
14882         
14883         this.fireEvent('hide', this, this.date);
14884         
14885     },
14886     
14887     onMousedown: function(e)
14888     {
14889         e.stopPropagation();
14890         e.preventDefault();
14891     },
14892     
14893     keyup: function(e)
14894     {
14895         Roo.bootstrap.DateField.superclass.keyup.call(this);
14896         this.update();
14897     },
14898
14899     setValue: function(v)
14900     {
14901         
14902         // v can be a string or a date..
14903         
14904         
14905         var d = new Date(this.parseDate(v) ).clearTime();
14906         
14907         Roo.log(d);
14908         Roo.log(d);
14909         if(isNaN(d.getTime())){
14910             this.date = this.viewDate = '';
14911             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14912             return;
14913         }
14914         
14915         v = this.formatDate(d);
14916         
14917         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14918         
14919         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14920      
14921         this.update();
14922
14923         this.fireEvent('select', this, this.date);
14924         
14925     },
14926     
14927     getValue: function()
14928     {
14929         return this.formatDate(this.date);
14930     },
14931     
14932     fireKey: function(e)
14933     {
14934         if (!this.picker().isVisible()){
14935             if (e.keyCode == 27) // allow escape to hide and re-show picker
14936                 this.show();
14937             return;
14938         }
14939         
14940         var dateChanged = false,
14941         dir, day, month,
14942         newDate, newViewDate;
14943         
14944         switch(e.keyCode){
14945             case 27: // escape
14946                 this.hide();
14947                 e.preventDefault();
14948                 break;
14949             case 37: // left
14950             case 39: // right
14951                 if (!this.keyboardNavigation) break;
14952                 dir = e.keyCode == 37 ? -1 : 1;
14953                 
14954                 if (e.ctrlKey){
14955                     newDate = this.moveYear(this.date, dir);
14956                     newViewDate = this.moveYear(this.viewDate, dir);
14957                 } else if (e.shiftKey){
14958                     newDate = this.moveMonth(this.date, dir);
14959                     newViewDate = this.moveMonth(this.viewDate, dir);
14960                 } else {
14961                     newDate = new Date(this.date);
14962                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14963                     newViewDate = new Date(this.viewDate);
14964                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14965                 }
14966                 if (this.dateWithinRange(newDate)){
14967                     this.date = newDate;
14968                     this.viewDate = newViewDate;
14969                     this.setValue(this.formatDate(this.date));
14970 //                    this.update();
14971                     e.preventDefault();
14972                     dateChanged = true;
14973                 }
14974                 break;
14975             case 38: // up
14976             case 40: // down
14977                 if (!this.keyboardNavigation) break;
14978                 dir = e.keyCode == 38 ? -1 : 1;
14979                 if (e.ctrlKey){
14980                     newDate = this.moveYear(this.date, dir);
14981                     newViewDate = this.moveYear(this.viewDate, dir);
14982                 } else if (e.shiftKey){
14983                     newDate = this.moveMonth(this.date, dir);
14984                     newViewDate = this.moveMonth(this.viewDate, dir);
14985                 } else {
14986                     newDate = new Date(this.date);
14987                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14988                     newViewDate = new Date(this.viewDate);
14989                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14990                 }
14991                 if (this.dateWithinRange(newDate)){
14992                     this.date = newDate;
14993                     this.viewDate = newViewDate;
14994                     this.setValue(this.formatDate(this.date));
14995 //                    this.update();
14996                     e.preventDefault();
14997                     dateChanged = true;
14998                 }
14999                 break;
15000             case 13: // enter
15001                 this.setValue(this.formatDate(this.date));
15002                 this.hide();
15003                 e.preventDefault();
15004                 break;
15005             case 9: // tab
15006                 this.setValue(this.formatDate(this.date));
15007                 this.hide();
15008                 break;
15009             case 16: // shift
15010             case 17: // ctrl
15011             case 18: // alt
15012                 break;
15013             default :
15014                 this.hide();
15015                 
15016         }
15017     },
15018     
15019     
15020     onClick: function(e) 
15021     {
15022         e.stopPropagation();
15023         e.preventDefault();
15024         
15025         var target = e.getTarget();
15026         
15027         if(target.nodeName.toLowerCase() === 'i'){
15028             target = Roo.get(target).dom.parentNode;
15029         }
15030         
15031         var nodeName = target.nodeName;
15032         var className = target.className;
15033         var html = target.innerHTML;
15034         //Roo.log(nodeName);
15035         
15036         switch(nodeName.toLowerCase()) {
15037             case 'th':
15038                 switch(className) {
15039                     case 'switch':
15040                         this.showMode(1);
15041                         break;
15042                     case 'prev':
15043                     case 'next':
15044                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15045                         switch(this.viewMode){
15046                                 case 0:
15047                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15048                                         break;
15049                                 case 1:
15050                                 case 2:
15051                                         this.viewDate = this.moveYear(this.viewDate, dir);
15052                                         break;
15053                         }
15054                         this.fill();
15055                         break;
15056                     case 'today':
15057                         var date = new Date();
15058                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15059 //                        this.fill()
15060                         this.setValue(this.formatDate(this.date));
15061                         
15062                         this.hide();
15063                         break;
15064                 }
15065                 break;
15066             case 'span':
15067                 if (className.indexOf('disabled') < 0) {
15068                     this.viewDate.setUTCDate(1);
15069                     if (className.indexOf('month') > -1) {
15070                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15071                     } else {
15072                         var year = parseInt(html, 10) || 0;
15073                         this.viewDate.setUTCFullYear(year);
15074                         
15075                     }
15076                     this.showMode(-1);
15077                     this.fill();
15078                 }
15079                 break;
15080                 
15081             case 'td':
15082                 //Roo.log(className);
15083                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15084                     var day = parseInt(html, 10) || 1;
15085                     var year = this.viewDate.getUTCFullYear(),
15086                         month = this.viewDate.getUTCMonth();
15087
15088                     if (className.indexOf('old') > -1) {
15089                         if(month === 0 ){
15090                             month = 11;
15091                             year -= 1;
15092                         }else{
15093                             month -= 1;
15094                         }
15095                     } else if (className.indexOf('new') > -1) {
15096                         if (month == 11) {
15097                             month = 0;
15098                             year += 1;
15099                         } else {
15100                             month += 1;
15101                         }
15102                     }
15103                     //Roo.log([year,month,day]);
15104                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15105                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15106 //                    this.fill();
15107                     //Roo.log(this.formatDate(this.date));
15108                     this.setValue(this.formatDate(this.date));
15109                     this.hide();
15110                 }
15111                 break;
15112         }
15113     },
15114     
15115     setStartDate: function(startDate)
15116     {
15117         this.startDate = startDate || -Infinity;
15118         if (this.startDate !== -Infinity) {
15119             this.startDate = this.parseDate(this.startDate);
15120         }
15121         this.update();
15122         this.updateNavArrows();
15123     },
15124
15125     setEndDate: function(endDate)
15126     {
15127         this.endDate = endDate || Infinity;
15128         if (this.endDate !== Infinity) {
15129             this.endDate = this.parseDate(this.endDate);
15130         }
15131         this.update();
15132         this.updateNavArrows();
15133     },
15134     
15135     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15136     {
15137         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15138         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15139             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15140         }
15141         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15142             return parseInt(d, 10);
15143         });
15144         this.update();
15145         this.updateNavArrows();
15146     },
15147     
15148     updateNavArrows: function() 
15149     {
15150         var d = new Date(this.viewDate),
15151         year = d.getUTCFullYear(),
15152         month = d.getUTCMonth();
15153         
15154         Roo.each(this.picker().select('.prev', true).elements, function(v){
15155             v.show();
15156             switch (this.viewMode) {
15157                 case 0:
15158
15159                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15160                         v.hide();
15161                     }
15162                     break;
15163                 case 1:
15164                 case 2:
15165                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15166                         v.hide();
15167                     }
15168                     break;
15169             }
15170         });
15171         
15172         Roo.each(this.picker().select('.next', true).elements, function(v){
15173             v.show();
15174             switch (this.viewMode) {
15175                 case 0:
15176
15177                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15178                         v.hide();
15179                     }
15180                     break;
15181                 case 1:
15182                 case 2:
15183                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15184                         v.hide();
15185                     }
15186                     break;
15187             }
15188         })
15189     },
15190     
15191     moveMonth: function(date, dir)
15192     {
15193         if (!dir) return date;
15194         var new_date = new Date(date.valueOf()),
15195         day = new_date.getUTCDate(),
15196         month = new_date.getUTCMonth(),
15197         mag = Math.abs(dir),
15198         new_month, test;
15199         dir = dir > 0 ? 1 : -1;
15200         if (mag == 1){
15201             test = dir == -1
15202             // If going back one month, make sure month is not current month
15203             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15204             ? function(){
15205                 return new_date.getUTCMonth() == month;
15206             }
15207             // If going forward one month, make sure month is as expected
15208             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15209             : function(){
15210                 return new_date.getUTCMonth() != new_month;
15211             };
15212             new_month = month + dir;
15213             new_date.setUTCMonth(new_month);
15214             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15215             if (new_month < 0 || new_month > 11)
15216                 new_month = (new_month + 12) % 12;
15217         } else {
15218             // For magnitudes >1, move one month at a time...
15219             for (var i=0; i<mag; i++)
15220                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15221                 new_date = this.moveMonth(new_date, dir);
15222             // ...then reset the day, keeping it in the new month
15223             new_month = new_date.getUTCMonth();
15224             new_date.setUTCDate(day);
15225             test = function(){
15226                 return new_month != new_date.getUTCMonth();
15227             };
15228         }
15229         // Common date-resetting loop -- if date is beyond end of month, make it
15230         // end of month
15231         while (test()){
15232             new_date.setUTCDate(--day);
15233             new_date.setUTCMonth(new_month);
15234         }
15235         return new_date;
15236     },
15237
15238     moveYear: function(date, dir)
15239     {
15240         return this.moveMonth(date, dir*12);
15241     },
15242
15243     dateWithinRange: function(date)
15244     {
15245         return date >= this.startDate && date <= this.endDate;
15246     },
15247
15248     
15249     remove: function() 
15250     {
15251         this.picker().remove();
15252     }
15253    
15254 });
15255
15256 Roo.apply(Roo.bootstrap.DateField,  {
15257     
15258     head : {
15259         tag: 'thead',
15260         cn: [
15261         {
15262             tag: 'tr',
15263             cn: [
15264             {
15265                 tag: 'th',
15266                 cls: 'prev',
15267                 html: '<i class="fa fa-arrow-left"/>'
15268             },
15269             {
15270                 tag: 'th',
15271                 cls: 'switch',
15272                 colspan: '5'
15273             },
15274             {
15275                 tag: 'th',
15276                 cls: 'next',
15277                 html: '<i class="fa fa-arrow-right"/>'
15278             }
15279
15280             ]
15281         }
15282         ]
15283     },
15284     
15285     content : {
15286         tag: 'tbody',
15287         cn: [
15288         {
15289             tag: 'tr',
15290             cn: [
15291             {
15292                 tag: 'td',
15293                 colspan: '7'
15294             }
15295             ]
15296         }
15297         ]
15298     },
15299     
15300     footer : {
15301         tag: 'tfoot',
15302         cn: [
15303         {
15304             tag: 'tr',
15305             cn: [
15306             {
15307                 tag: 'th',
15308                 colspan: '7',
15309                 cls: 'today'
15310             }
15311                     
15312             ]
15313         }
15314         ]
15315     },
15316     
15317     dates:{
15318         en: {
15319             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15320             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15321             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15322             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15323             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15324             today: "Today"
15325         }
15326     },
15327     
15328     modes: [
15329     {
15330         clsName: 'days',
15331         navFnc: 'Month',
15332         navStep: 1
15333     },
15334     {
15335         clsName: 'months',
15336         navFnc: 'FullYear',
15337         navStep: 1
15338     },
15339     {
15340         clsName: 'years',
15341         navFnc: 'FullYear',
15342         navStep: 10
15343     }]
15344 });
15345
15346 Roo.apply(Roo.bootstrap.DateField,  {
15347   
15348     template : {
15349         tag: 'div',
15350         cls: 'datepicker dropdown-menu',
15351         cn: [
15352         {
15353             tag: 'div',
15354             cls: 'datepicker-days',
15355             cn: [
15356             {
15357                 tag: 'table',
15358                 cls: 'table-condensed',
15359                 cn:[
15360                 Roo.bootstrap.DateField.head,
15361                 {
15362                     tag: 'tbody'
15363                 },
15364                 Roo.bootstrap.DateField.footer
15365                 ]
15366             }
15367             ]
15368         },
15369         {
15370             tag: 'div',
15371             cls: 'datepicker-months',
15372             cn: [
15373             {
15374                 tag: 'table',
15375                 cls: 'table-condensed',
15376                 cn:[
15377                 Roo.bootstrap.DateField.head,
15378                 Roo.bootstrap.DateField.content,
15379                 Roo.bootstrap.DateField.footer
15380                 ]
15381             }
15382             ]
15383         },
15384         {
15385             tag: 'div',
15386             cls: 'datepicker-years',
15387             cn: [
15388             {
15389                 tag: 'table',
15390                 cls: 'table-condensed',
15391                 cn:[
15392                 Roo.bootstrap.DateField.head,
15393                 Roo.bootstrap.DateField.content,
15394                 Roo.bootstrap.DateField.footer
15395                 ]
15396             }
15397             ]
15398         }
15399         ]
15400     }
15401 });
15402
15403  
15404
15405  /*
15406  * - LGPL
15407  *
15408  * TimeField
15409  * 
15410  */
15411
15412 /**
15413  * @class Roo.bootstrap.TimeField
15414  * @extends Roo.bootstrap.Input
15415  * Bootstrap DateField class
15416  * 
15417  * 
15418  * @constructor
15419  * Create a new TimeField
15420  * @param {Object} config The config object
15421  */
15422
15423 Roo.bootstrap.TimeField = function(config){
15424     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15425     this.addEvents({
15426             /**
15427              * @event show
15428              * Fires when this field show.
15429              * @param {Roo.bootstrap.DateField} this
15430              * @param {Mixed} date The date value
15431              */
15432             show : true,
15433             /**
15434              * @event show
15435              * Fires when this field hide.
15436              * @param {Roo.bootstrap.DateField} this
15437              * @param {Mixed} date The date value
15438              */
15439             hide : true,
15440             /**
15441              * @event select
15442              * Fires when select a date.
15443              * @param {Roo.bootstrap.DateField} this
15444              * @param {Mixed} date The date value
15445              */
15446             select : true
15447         });
15448 };
15449
15450 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15451     
15452     /**
15453      * @cfg {String} format
15454      * The default time format string which can be overriden for localization support.  The format must be
15455      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15456      */
15457     format : "H:i",
15458        
15459     onRender: function(ct, position)
15460     {
15461         
15462         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15463                 
15464         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15465         
15466         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15467         
15468         this.pop = this.picker().select('>.datepicker-time',true).first();
15469         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15470         
15471         this.picker().on('mousedown', this.onMousedown, this);
15472         this.picker().on('click', this.onClick, this);
15473         
15474         this.picker().addClass('datepicker-dropdown');
15475     
15476         this.fillTime();
15477         this.update();
15478             
15479         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15480         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15481         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15482         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15483         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15484         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15485
15486     },
15487     
15488     fireKey: function(e){
15489         if (!this.picker().isVisible()){
15490             if (e.keyCode == 27) // allow escape to hide and re-show picker
15491                 this.show();
15492             return;
15493         }
15494
15495         e.preventDefault();
15496         
15497         switch(e.keyCode){
15498             case 27: // escape
15499                 this.hide();
15500                 break;
15501             case 37: // left
15502             case 39: // right
15503                 this.onTogglePeriod();
15504                 break;
15505             case 38: // up
15506                 this.onIncrementMinutes();
15507                 break;
15508             case 40: // down
15509                 this.onDecrementMinutes();
15510                 break;
15511             case 13: // enter
15512             case 9: // tab
15513                 this.setTime();
15514                 break;
15515         }
15516     },
15517     
15518     onClick: function(e) {
15519         e.stopPropagation();
15520         e.preventDefault();
15521     },
15522     
15523     picker : function()
15524     {
15525         return this.el.select('.datepicker', true).first();
15526     },
15527     
15528     fillTime: function()
15529     {    
15530         var time = this.pop.select('tbody', true).first();
15531         
15532         time.dom.innerHTML = '';
15533         
15534         time.createChild({
15535             tag: 'tr',
15536             cn: [
15537                 {
15538                     tag: 'td',
15539                     cn: [
15540                         {
15541                             tag: 'a',
15542                             href: '#',
15543                             cls: 'btn',
15544                             cn: [
15545                                 {
15546                                     tag: 'span',
15547                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15548                                 }
15549                             ]
15550                         } 
15551                     ]
15552                 },
15553                 {
15554                     tag: 'td',
15555                     cls: 'separator'
15556                 },
15557                 {
15558                     tag: 'td',
15559                     cn: [
15560                         {
15561                             tag: 'a',
15562                             href: '#',
15563                             cls: 'btn',
15564                             cn: [
15565                                 {
15566                                     tag: 'span',
15567                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15568                                 }
15569                             ]
15570                         }
15571                     ]
15572                 },
15573                 {
15574                     tag: 'td',
15575                     cls: 'separator'
15576                 }
15577             ]
15578         });
15579         
15580         time.createChild({
15581             tag: 'tr',
15582             cn: [
15583                 {
15584                     tag: 'td',
15585                     cn: [
15586                         {
15587                             tag: 'span',
15588                             cls: 'timepicker-hour',
15589                             html: '00'
15590                         }  
15591                     ]
15592                 },
15593                 {
15594                     tag: 'td',
15595                     cls: 'separator',
15596                     html: ':'
15597                 },
15598                 {
15599                     tag: 'td',
15600                     cn: [
15601                         {
15602                             tag: 'span',
15603                             cls: 'timepicker-minute',
15604                             html: '00'
15605                         }  
15606                     ]
15607                 },
15608                 {
15609                     tag: 'td',
15610                     cls: 'separator'
15611                 },
15612                 {
15613                     tag: 'td',
15614                     cn: [
15615                         {
15616                             tag: 'button',
15617                             type: 'button',
15618                             cls: 'btn btn-primary period',
15619                             html: 'AM'
15620                             
15621                         }
15622                     ]
15623                 }
15624             ]
15625         });
15626         
15627         time.createChild({
15628             tag: 'tr',
15629             cn: [
15630                 {
15631                     tag: 'td',
15632                     cn: [
15633                         {
15634                             tag: 'a',
15635                             href: '#',
15636                             cls: 'btn',
15637                             cn: [
15638                                 {
15639                                     tag: 'span',
15640                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15641                                 }
15642                             ]
15643                         }
15644                     ]
15645                 },
15646                 {
15647                     tag: 'td',
15648                     cls: 'separator'
15649                 },
15650                 {
15651                     tag: 'td',
15652                     cn: [
15653                         {
15654                             tag: 'a',
15655                             href: '#',
15656                             cls: 'btn',
15657                             cn: [
15658                                 {
15659                                     tag: 'span',
15660                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15661                                 }
15662                             ]
15663                         }
15664                     ]
15665                 },
15666                 {
15667                     tag: 'td',
15668                     cls: 'separator'
15669                 }
15670             ]
15671         });
15672         
15673     },
15674     
15675     update: function()
15676     {
15677         
15678         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15679         
15680         this.fill();
15681     },
15682     
15683     fill: function() 
15684     {
15685         var hours = this.time.getHours();
15686         var minutes = this.time.getMinutes();
15687         var period = 'AM';
15688         
15689         if(hours > 11){
15690             period = 'PM';
15691         }
15692         
15693         if(hours == 0){
15694             hours = 12;
15695         }
15696         
15697         
15698         if(hours > 12){
15699             hours = hours - 12;
15700         }
15701         
15702         if(hours < 10){
15703             hours = '0' + hours;
15704         }
15705         
15706         if(minutes < 10){
15707             minutes = '0' + minutes;
15708         }
15709         
15710         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15711         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15712         this.pop.select('button', true).first().dom.innerHTML = period;
15713         
15714     },
15715     
15716     place: function()
15717     {   
15718         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15719         
15720         var cls = ['bottom'];
15721         
15722         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15723             cls.pop();
15724             cls.push('top');
15725         }
15726         
15727         cls.push('right');
15728         
15729         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15730             cls.pop();
15731             cls.push('left');
15732         }
15733         
15734         this.picker().addClass(cls.join('-'));
15735         
15736         var _this = this;
15737         
15738         Roo.each(cls, function(c){
15739             if(c == 'bottom'){
15740                 _this.picker().setTop(_this.inputEl().getHeight());
15741                 return;
15742             }
15743             if(c == 'top'){
15744                 _this.picker().setTop(0 - _this.picker().getHeight());
15745                 return;
15746             }
15747             
15748             if(c == 'left'){
15749                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15750                 return;
15751             }
15752             if(c == 'right'){
15753                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15754                 return;
15755             }
15756         });
15757         
15758     },
15759   
15760     onFocus : function()
15761     {
15762         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15763         this.show();
15764     },
15765     
15766     onBlur : function()
15767     {
15768         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15769         this.hide();
15770     },
15771     
15772     show : function()
15773     {
15774         this.picker().show();
15775         this.pop.show();
15776         this.update();
15777         this.place();
15778         
15779         this.fireEvent('show', this, this.date);
15780     },
15781     
15782     hide : function()
15783     {
15784         this.picker().hide();
15785         this.pop.hide();
15786         
15787         this.fireEvent('hide', this, this.date);
15788     },
15789     
15790     setTime : function()
15791     {
15792         this.hide();
15793         this.setValue(this.time.format(this.format));
15794         
15795         this.fireEvent('select', this, this.date);
15796         
15797         
15798     },
15799     
15800     onMousedown: function(e){
15801         e.stopPropagation();
15802         e.preventDefault();
15803     },
15804     
15805     onIncrementHours: function()
15806     {
15807         Roo.log('onIncrementHours');
15808         this.time = this.time.add(Date.HOUR, 1);
15809         this.update();
15810         
15811     },
15812     
15813     onDecrementHours: function()
15814     {
15815         Roo.log('onDecrementHours');
15816         this.time = this.time.add(Date.HOUR, -1);
15817         this.update();
15818     },
15819     
15820     onIncrementMinutes: function()
15821     {
15822         Roo.log('onIncrementMinutes');
15823         this.time = this.time.add(Date.MINUTE, 1);
15824         this.update();
15825     },
15826     
15827     onDecrementMinutes: function()
15828     {
15829         Roo.log('onDecrementMinutes');
15830         this.time = this.time.add(Date.MINUTE, -1);
15831         this.update();
15832     },
15833     
15834     onTogglePeriod: function()
15835     {
15836         Roo.log('onTogglePeriod');
15837         this.time = this.time.add(Date.HOUR, 12);
15838         this.update();
15839     }
15840     
15841    
15842 });
15843
15844 Roo.apply(Roo.bootstrap.TimeField,  {
15845     
15846     content : {
15847         tag: 'tbody',
15848         cn: [
15849             {
15850                 tag: 'tr',
15851                 cn: [
15852                 {
15853                     tag: 'td',
15854                     colspan: '7'
15855                 }
15856                 ]
15857             }
15858         ]
15859     },
15860     
15861     footer : {
15862         tag: 'tfoot',
15863         cn: [
15864             {
15865                 tag: 'tr',
15866                 cn: [
15867                 {
15868                     tag: 'th',
15869                     colspan: '7',
15870                     cls: '',
15871                     cn: [
15872                         {
15873                             tag: 'button',
15874                             cls: 'btn btn-info ok',
15875                             html: 'OK'
15876                         }
15877                     ]
15878                 }
15879
15880                 ]
15881             }
15882         ]
15883     }
15884 });
15885
15886 Roo.apply(Roo.bootstrap.TimeField,  {
15887   
15888     template : {
15889         tag: 'div',
15890         cls: 'datepicker dropdown-menu',
15891         cn: [
15892             {
15893                 tag: 'div',
15894                 cls: 'datepicker-time',
15895                 cn: [
15896                 {
15897                     tag: 'table',
15898                     cls: 'table-condensed',
15899                     cn:[
15900                     Roo.bootstrap.TimeField.content,
15901                     Roo.bootstrap.TimeField.footer
15902                     ]
15903                 }
15904                 ]
15905             }
15906         ]
15907     }
15908 });
15909
15910  
15911
15912  /*
15913  * - LGPL
15914  *
15915  * CheckBox
15916  * 
15917  */
15918
15919 /**
15920  * @class Roo.bootstrap.CheckBox
15921  * @extends Roo.bootstrap.Input
15922  * Bootstrap CheckBox class
15923  * 
15924  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15925  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15926  * @cfg {String} boxLabel The text that appears beside the checkbox
15927  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15928  * @cfg {Boolean} checked initnal the element
15929  * 
15930  * 
15931  * @constructor
15932  * Create a new CheckBox
15933  * @param {Object} config The config object
15934  */
15935
15936 Roo.bootstrap.CheckBox = function(config){
15937     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15938    
15939         this.addEvents({
15940             /**
15941             * @event check
15942             * Fires when the element is checked or unchecked.
15943             * @param {Roo.bootstrap.CheckBox} this This input
15944             * @param {Boolean} checked The new checked value
15945             */
15946            check : true
15947         });
15948 };
15949
15950 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15951     
15952     inputType: 'checkbox',
15953     inputValue: 1,
15954     valueOff: 0,
15955     boxLabel: false,
15956     checked: false,
15957     weight : false,
15958     
15959     getAutoCreate : function()
15960     {
15961         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15962         
15963         var id = Roo.id();
15964         
15965         var cfg = {};
15966         
15967         cfg.cls = 'form-group checkbox' //input-group
15968         
15969         
15970         
15971         
15972         var input =  {
15973             tag: 'input',
15974             id : id,
15975             type : this.inputType,
15976             value : (!this.checked) ? this.valueOff : this.inputValue,
15977             cls : 'roo-checkbox', //'form-box',
15978             placeholder : this.placeholder || ''
15979             
15980         };
15981         
15982         if (this.weight) { // Validity check?
15983             cfg.cls += " checkbox-" + this.weight;
15984         }
15985         
15986         if (this.disabled) {
15987             input.disabled=true;
15988         }
15989         
15990         if(this.checked){
15991             input.checked = this.checked;
15992         }
15993         
15994         if (this.name) {
15995             input.name = this.name;
15996         }
15997         
15998         if (this.size) {
15999             input.cls += ' input-' + this.size;
16000         }
16001         
16002         var settings=this;
16003         ['xs','sm','md','lg'].map(function(size){
16004             if (settings[size]) {
16005                 cfg.cls += ' col-' + size + '-' + settings[size];
16006             }
16007         });
16008         
16009        
16010         
16011         var inputblock = input;
16012         
16013         
16014         
16015         
16016         if (this.before || this.after) {
16017             
16018             inputblock = {
16019                 cls : 'input-group',
16020                 cn :  [] 
16021             };
16022             if (this.before) {
16023                 inputblock.cn.push({
16024                     tag :'span',
16025                     cls : 'input-group-addon',
16026                     html : this.before
16027                 });
16028             }
16029             inputblock.cn.push(input);
16030             if (this.after) {
16031                 inputblock.cn.push({
16032                     tag :'span',
16033                     cls : 'input-group-addon',
16034                     html : this.after
16035                 });
16036             }
16037             
16038         };
16039         
16040         if (align ==='left' && this.fieldLabel.length) {
16041                 Roo.log("left and has label");
16042                 cfg.cn = [
16043                     
16044                     {
16045                         tag: 'label',
16046                         'for' :  id,
16047                         cls : 'control-label col-md-' + this.labelWidth,
16048                         html : this.fieldLabel
16049                         
16050                     },
16051                     {
16052                         cls : "col-md-" + (12 - this.labelWidth), 
16053                         cn: [
16054                             inputblock
16055                         ]
16056                     }
16057                     
16058                 ];
16059         } else if ( this.fieldLabel.length) {
16060                 Roo.log(" label");
16061                 cfg.cn = [
16062                    
16063                     {
16064                         tag: this.boxLabel ? 'span' : 'label',
16065                         'for': id,
16066                         cls: 'control-label box-input-label',
16067                         //cls : 'input-group-addon',
16068                         html : this.fieldLabel
16069                         
16070                     },
16071                     
16072                     inputblock
16073                     
16074                 ];
16075
16076         } else {
16077             
16078                 Roo.log(" no label && no align");
16079                 cfg.cn = [  inputblock ] ;
16080                 
16081                 
16082         };
16083          if(this.boxLabel){
16084             cfg.cn.push( {
16085                 tag: 'label',
16086                 'for': id,
16087                 cls: 'box-label',
16088                 html: this.boxLabel
16089                 
16090             });
16091         }
16092         
16093         
16094        
16095         return cfg;
16096         
16097     },
16098     
16099     /**
16100      * return the real input element.
16101      */
16102     inputEl: function ()
16103     {
16104         return this.el.select('input.roo-checkbox',true).first();
16105     },
16106     
16107     label: function()
16108     {
16109         return this.el.select('label.control-label',true).first();
16110     },
16111     
16112     initEvents : function()
16113     {
16114 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16115         
16116         this.inputEl().on('click', this.onClick,  this);
16117         
16118     },
16119     
16120     onClick : function()
16121     {   
16122         this.setChecked(!this.checked);
16123     },
16124     
16125     setChecked : function(state,suppressEvent)
16126     {
16127         this.checked = state;
16128         
16129         this.inputEl().dom.checked = state;
16130         
16131         if(suppressEvent !== true){
16132             this.fireEvent('check', this, state);
16133         }
16134         
16135         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16136         
16137     },
16138     
16139     setValue : function(v,suppressEvent)
16140     {
16141         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16142     }
16143     
16144 });
16145
16146  
16147 /*
16148  * - LGPL
16149  *
16150  * Radio
16151  * 
16152  */
16153
16154 /**
16155  * @class Roo.bootstrap.Radio
16156  * @extends Roo.bootstrap.CheckBox
16157  * Bootstrap Radio class
16158
16159  * @constructor
16160  * Create a new Radio
16161  * @param {Object} config The config object
16162  */
16163
16164 Roo.bootstrap.Radio = function(config){
16165     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16166    
16167 };
16168
16169 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16170     
16171     inputType: 'radio',
16172     inputValue: '',
16173     valueOff: '',
16174     
16175     getAutoCreate : function()
16176     {
16177         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16178         
16179         var id = Roo.id();
16180         
16181         var cfg = {};
16182         
16183         cfg.cls = 'form-group radio' //input-group
16184         
16185         var input =  {
16186             tag: 'input',
16187             id : id,
16188             type : this.inputType,
16189             value : (!this.checked) ? this.valueOff : this.inputValue,
16190             cls : 'roo-radio',
16191             placeholder : this.placeholder || ''
16192             
16193         };
16194           if (this.weight) { // Validity check?
16195             cfg.cls += " radio-" + this.weight;
16196         }
16197         if (this.disabled) {
16198             input.disabled=true;
16199         }
16200         
16201         if(this.checked){
16202             input.checked = this.checked;
16203         }
16204         
16205         if (this.name) {
16206             input.name = this.name;
16207         }
16208         
16209         if (this.size) {
16210             input.cls += ' input-' + this.size;
16211         }
16212         
16213         var settings=this;
16214         ['xs','sm','md','lg'].map(function(size){
16215             if (settings[size]) {
16216                 cfg.cls += ' col-' + size + '-' + settings[size];
16217             }
16218         });
16219         
16220         var inputblock = input;
16221         
16222         if (this.before || this.after) {
16223             
16224             inputblock = {
16225                 cls : 'input-group',
16226                 cn :  [] 
16227             };
16228             if (this.before) {
16229                 inputblock.cn.push({
16230                     tag :'span',
16231                     cls : 'input-group-addon',
16232                     html : this.before
16233                 });
16234             }
16235             inputblock.cn.push(input);
16236             if (this.after) {
16237                 inputblock.cn.push({
16238                     tag :'span',
16239                     cls : 'input-group-addon',
16240                     html : this.after
16241                 });
16242             }
16243             
16244         };
16245         
16246         if (align ==='left' && this.fieldLabel.length) {
16247                 Roo.log("left and has label");
16248                 cfg.cn = [
16249                     
16250                     {
16251                         tag: 'label',
16252                         'for' :  id,
16253                         cls : 'control-label col-md-' + this.labelWidth,
16254                         html : this.fieldLabel
16255                         
16256                     },
16257                     {
16258                         cls : "col-md-" + (12 - this.labelWidth), 
16259                         cn: [
16260                             inputblock
16261                         ]
16262                     }
16263                     
16264                 ];
16265         } else if ( this.fieldLabel.length) {
16266                 Roo.log(" label");
16267                  cfg.cn = [
16268                    
16269                     {
16270                         tag: 'label',
16271                         'for': id,
16272                         cls: 'control-label box-input-label',
16273                         //cls : 'input-group-addon',
16274                         html : this.fieldLabel
16275                         
16276                     },
16277                     
16278                     inputblock
16279                     
16280                 ];
16281
16282         } else {
16283             
16284                    Roo.log(" no label && no align");
16285                 cfg.cn = [
16286                     
16287                         inputblock
16288                     
16289                 ];
16290                 
16291                 
16292         };
16293         
16294         if(this.boxLabel){
16295             cfg.cn.push({
16296                 tag: 'label',
16297                 'for': id,
16298                 cls: 'box-label',
16299                 html: this.boxLabel
16300             })
16301         }
16302         
16303         return cfg;
16304         
16305     },
16306     inputEl: function ()
16307     {
16308         return this.el.select('input.roo-radio',true).first();
16309     },
16310     onClick : function()
16311     {   
16312         this.setChecked(true);
16313     },
16314     
16315     setChecked : function(state,suppressEvent)
16316     {
16317         if(state){
16318             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16319                 v.dom.checked = false;
16320             });
16321         }
16322         
16323         this.checked = state;
16324         this.inputEl().dom.checked = state;
16325         
16326         if(suppressEvent !== true){
16327             this.fireEvent('check', this, state);
16328         }
16329         
16330         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16331         
16332     },
16333     
16334     getGroupValue : function()
16335     {
16336         var value = ''
16337         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16338             if(v.dom.checked == true){
16339                 value = v.dom.value;
16340             }
16341         });
16342         
16343         return value;
16344     },
16345     
16346     /**
16347      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16348      * @return {Mixed} value The field value
16349      */
16350     getValue : function(){
16351         return this.getGroupValue();
16352     }
16353     
16354 });
16355
16356  
16357 //<script type="text/javascript">
16358
16359 /*
16360  * Based  Ext JS Library 1.1.1
16361  * Copyright(c) 2006-2007, Ext JS, LLC.
16362  * LGPL
16363  *
16364  */
16365  
16366 /**
16367  * @class Roo.HtmlEditorCore
16368  * @extends Roo.Component
16369  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16370  *
16371  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16372  */
16373
16374 Roo.HtmlEditorCore = function(config){
16375     
16376     
16377     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16378     
16379     
16380     this.addEvents({
16381         /**
16382          * @event initialize
16383          * Fires when the editor is fully initialized (including the iframe)
16384          * @param {Roo.HtmlEditorCore} this
16385          */
16386         initialize: true,
16387         /**
16388          * @event activate
16389          * Fires when the editor is first receives the focus. Any insertion must wait
16390          * until after this event.
16391          * @param {Roo.HtmlEditorCore} this
16392          */
16393         activate: true,
16394          /**
16395          * @event beforesync
16396          * Fires before the textarea is updated with content from the editor iframe. Return false
16397          * to cancel the sync.
16398          * @param {Roo.HtmlEditorCore} this
16399          * @param {String} html
16400          */
16401         beforesync: true,
16402          /**
16403          * @event beforepush
16404          * Fires before the iframe editor is updated with content from the textarea. Return false
16405          * to cancel the push.
16406          * @param {Roo.HtmlEditorCore} this
16407          * @param {String} html
16408          */
16409         beforepush: true,
16410          /**
16411          * @event sync
16412          * Fires when the textarea is updated with content from the editor iframe.
16413          * @param {Roo.HtmlEditorCore} this
16414          * @param {String} html
16415          */
16416         sync: true,
16417          /**
16418          * @event push
16419          * Fires when the iframe editor is updated with content from the textarea.
16420          * @param {Roo.HtmlEditorCore} this
16421          * @param {String} html
16422          */
16423         push: true,
16424         
16425         /**
16426          * @event editorevent
16427          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16428          * @param {Roo.HtmlEditorCore} this
16429          */
16430         editorevent: true
16431     });
16432     
16433     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16434     
16435     // defaults : white / black...
16436     this.applyBlacklists();
16437     
16438     
16439     
16440 };
16441
16442
16443 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16444
16445
16446      /**
16447      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16448      */
16449     
16450     owner : false,
16451     
16452      /**
16453      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16454      *                        Roo.resizable.
16455      */
16456     resizable : false,
16457      /**
16458      * @cfg {Number} height (in pixels)
16459      */   
16460     height: 300,
16461    /**
16462      * @cfg {Number} width (in pixels)
16463      */   
16464     width: 500,
16465     
16466     /**
16467      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16468      * 
16469      */
16470     stylesheets: false,
16471     
16472     // id of frame..
16473     frameId: false,
16474     
16475     // private properties
16476     validationEvent : false,
16477     deferHeight: true,
16478     initialized : false,
16479     activated : false,
16480     sourceEditMode : false,
16481     onFocus : Roo.emptyFn,
16482     iframePad:3,
16483     hideMode:'offsets',
16484     
16485     clearUp: true,
16486     
16487     // blacklist + whitelisted elements..
16488     black: false,
16489     white: false,
16490      
16491     
16492
16493     /**
16494      * Protected method that will not generally be called directly. It
16495      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16496      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16497      */
16498     getDocMarkup : function(){
16499         // body styles..
16500         var st = '';
16501         Roo.log(this.stylesheets);
16502         
16503         // inherit styels from page...?? 
16504         if (this.stylesheets === false) {
16505             
16506             Roo.get(document.head).select('style').each(function(node) {
16507                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16508             });
16509             
16510             Roo.get(document.head).select('link').each(function(node) { 
16511                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16512             });
16513             
16514         } else if (!this.stylesheets.length) {
16515                 // simple..
16516                 st = '<style type="text/css">' +
16517                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16518                    '</style>';
16519         } else {
16520             Roo.each(this.stylesheets, function(s) {
16521                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16522             });
16523             
16524         }
16525         
16526         st +=  '<style type="text/css">' +
16527             'IMG { cursor: pointer } ' +
16528         '</style>';
16529
16530         
16531         return '<html><head>' + st  +
16532             //<style type="text/css">' +
16533             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16534             //'</style>' +
16535             ' </head><body class="roo-htmleditor-body"></body></html>';
16536     },
16537
16538     // private
16539     onRender : function(ct, position)
16540     {
16541         var _t = this;
16542         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16543         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16544         
16545         
16546         this.el.dom.style.border = '0 none';
16547         this.el.dom.setAttribute('tabIndex', -1);
16548         this.el.addClass('x-hidden hide');
16549         
16550         
16551         
16552         if(Roo.isIE){ // fix IE 1px bogus margin
16553             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16554         }
16555        
16556         
16557         this.frameId = Roo.id();
16558         
16559          
16560         
16561         var iframe = this.owner.wrap.createChild({
16562             tag: 'iframe',
16563             cls: 'form-control', // bootstrap..
16564             id: this.frameId,
16565             name: this.frameId,
16566             frameBorder : 'no',
16567             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16568         }, this.el
16569         );
16570         
16571         
16572         this.iframe = iframe.dom;
16573
16574          this.assignDocWin();
16575         
16576         this.doc.designMode = 'on';
16577        
16578         this.doc.open();
16579         this.doc.write(this.getDocMarkup());
16580         this.doc.close();
16581
16582         
16583         var task = { // must defer to wait for browser to be ready
16584             run : function(){
16585                 //console.log("run task?" + this.doc.readyState);
16586                 this.assignDocWin();
16587                 if(this.doc.body || this.doc.readyState == 'complete'){
16588                     try {
16589                         this.doc.designMode="on";
16590                     } catch (e) {
16591                         return;
16592                     }
16593                     Roo.TaskMgr.stop(task);
16594                     this.initEditor.defer(10, this);
16595                 }
16596             },
16597             interval : 10,
16598             duration: 10000,
16599             scope: this
16600         };
16601         Roo.TaskMgr.start(task);
16602
16603         
16604          
16605     },
16606
16607     // private
16608     onResize : function(w, h)
16609     {
16610          Roo.log('resize: ' +w + ',' + h );
16611         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16612         if(!this.iframe){
16613             return;
16614         }
16615         if(typeof w == 'number'){
16616             
16617             this.iframe.style.width = w + 'px';
16618         }
16619         if(typeof h == 'number'){
16620             
16621             this.iframe.style.height = h + 'px';
16622             if(this.doc){
16623                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16624             }
16625         }
16626         
16627     },
16628
16629     /**
16630      * Toggles the editor between standard and source edit mode.
16631      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16632      */
16633     toggleSourceEdit : function(sourceEditMode){
16634         
16635         this.sourceEditMode = sourceEditMode === true;
16636         
16637         if(this.sourceEditMode){
16638  
16639             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16640             
16641         }else{
16642             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16643             //this.iframe.className = '';
16644             this.deferFocus();
16645         }
16646         //this.setSize(this.owner.wrap.getSize());
16647         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16648     },
16649
16650     
16651   
16652
16653     /**
16654      * Protected method that will not generally be called directly. If you need/want
16655      * custom HTML cleanup, this is the method you should override.
16656      * @param {String} html The HTML to be cleaned
16657      * return {String} The cleaned HTML
16658      */
16659     cleanHtml : function(html){
16660         html = String(html);
16661         if(html.length > 5){
16662             if(Roo.isSafari){ // strip safari nonsense
16663                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16664             }
16665         }
16666         if(html == '&nbsp;'){
16667             html = '';
16668         }
16669         return html;
16670     },
16671
16672     /**
16673      * HTML Editor -> Textarea
16674      * Protected method that will not generally be called directly. Syncs the contents
16675      * of the editor iframe with the textarea.
16676      */
16677     syncValue : function(){
16678         if(this.initialized){
16679             var bd = (this.doc.body || this.doc.documentElement);
16680             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16681             var html = bd.innerHTML;
16682             if(Roo.isSafari){
16683                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16684                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16685                 if(m && m[1]){
16686                     html = '<div style="'+m[0]+'">' + html + '</div>';
16687                 }
16688             }
16689             html = this.cleanHtml(html);
16690             // fix up the special chars.. normaly like back quotes in word...
16691             // however we do not want to do this with chinese..
16692             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16693                 var cc = b.charCodeAt();
16694                 if (
16695                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16696                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16697                     (cc >= 0xf900 && cc < 0xfb00 )
16698                 ) {
16699                         return b;
16700                 }
16701                 return "&#"+cc+";" 
16702             });
16703             if(this.owner.fireEvent('beforesync', this, html) !== false){
16704                 this.el.dom.value = html;
16705                 this.owner.fireEvent('sync', this, html);
16706             }
16707         }
16708     },
16709
16710     /**
16711      * Protected method that will not generally be called directly. Pushes the value of the textarea
16712      * into the iframe editor.
16713      */
16714     pushValue : function(){
16715         if(this.initialized){
16716             var v = this.el.dom.value.trim();
16717             
16718 //            if(v.length < 1){
16719 //                v = '&#160;';
16720 //            }
16721             
16722             if(this.owner.fireEvent('beforepush', this, v) !== false){
16723                 var d = (this.doc.body || this.doc.documentElement);
16724                 d.innerHTML = v;
16725                 this.cleanUpPaste();
16726                 this.el.dom.value = d.innerHTML;
16727                 this.owner.fireEvent('push', this, v);
16728             }
16729         }
16730     },
16731
16732     // private
16733     deferFocus : function(){
16734         this.focus.defer(10, this);
16735     },
16736
16737     // doc'ed in Field
16738     focus : function(){
16739         if(this.win && !this.sourceEditMode){
16740             this.win.focus();
16741         }else{
16742             this.el.focus();
16743         }
16744     },
16745     
16746     assignDocWin: function()
16747     {
16748         var iframe = this.iframe;
16749         
16750          if(Roo.isIE){
16751             this.doc = iframe.contentWindow.document;
16752             this.win = iframe.contentWindow;
16753         } else {
16754 //            if (!Roo.get(this.frameId)) {
16755 //                return;
16756 //            }
16757 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16758 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16759             
16760             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16761                 return;
16762             }
16763             
16764             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16765             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16766         }
16767     },
16768     
16769     // private
16770     initEditor : function(){
16771         //console.log("INIT EDITOR");
16772         this.assignDocWin();
16773         
16774         
16775         
16776         this.doc.designMode="on";
16777         this.doc.open();
16778         this.doc.write(this.getDocMarkup());
16779         this.doc.close();
16780         
16781         var dbody = (this.doc.body || this.doc.documentElement);
16782         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16783         // this copies styles from the containing element into thsi one..
16784         // not sure why we need all of this..
16785         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16786         
16787         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16788         //ss['background-attachment'] = 'fixed'; // w3c
16789         dbody.bgProperties = 'fixed'; // ie
16790         //Roo.DomHelper.applyStyles(dbody, ss);
16791         Roo.EventManager.on(this.doc, {
16792             //'mousedown': this.onEditorEvent,
16793             'mouseup': this.onEditorEvent,
16794             'dblclick': this.onEditorEvent,
16795             'click': this.onEditorEvent,
16796             'keyup': this.onEditorEvent,
16797             buffer:100,
16798             scope: this
16799         });
16800         if(Roo.isGecko){
16801             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16802         }
16803         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16804             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16805         }
16806         this.initialized = true;
16807
16808         this.owner.fireEvent('initialize', this);
16809         this.pushValue();
16810     },
16811
16812     // private
16813     onDestroy : function(){
16814         
16815         
16816         
16817         if(this.rendered){
16818             
16819             //for (var i =0; i < this.toolbars.length;i++) {
16820             //    // fixme - ask toolbars for heights?
16821             //    this.toolbars[i].onDestroy();
16822            // }
16823             
16824             //this.wrap.dom.innerHTML = '';
16825             //this.wrap.remove();
16826         }
16827     },
16828
16829     // private
16830     onFirstFocus : function(){
16831         
16832         this.assignDocWin();
16833         
16834         
16835         this.activated = true;
16836          
16837     
16838         if(Roo.isGecko){ // prevent silly gecko errors
16839             this.win.focus();
16840             var s = this.win.getSelection();
16841             if(!s.focusNode || s.focusNode.nodeType != 3){
16842                 var r = s.getRangeAt(0);
16843                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16844                 r.collapse(true);
16845                 this.deferFocus();
16846             }
16847             try{
16848                 this.execCmd('useCSS', true);
16849                 this.execCmd('styleWithCSS', false);
16850             }catch(e){}
16851         }
16852         this.owner.fireEvent('activate', this);
16853     },
16854
16855     // private
16856     adjustFont: function(btn){
16857         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16858         //if(Roo.isSafari){ // safari
16859         //    adjust *= 2;
16860        // }
16861         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16862         if(Roo.isSafari){ // safari
16863             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16864             v =  (v < 10) ? 10 : v;
16865             v =  (v > 48) ? 48 : v;
16866             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16867             
16868         }
16869         
16870         
16871         v = Math.max(1, v+adjust);
16872         
16873         this.execCmd('FontSize', v  );
16874     },
16875
16876     onEditorEvent : function(e){
16877         this.owner.fireEvent('editorevent', this, e);
16878       //  this.updateToolbar();
16879         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16880     },
16881
16882     insertTag : function(tg)
16883     {
16884         // could be a bit smarter... -> wrap the current selected tRoo..
16885         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16886             
16887             range = this.createRange(this.getSelection());
16888             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16889             wrappingNode.appendChild(range.extractContents());
16890             range.insertNode(wrappingNode);
16891
16892             return;
16893             
16894             
16895             
16896         }
16897         this.execCmd("formatblock",   tg);
16898         
16899     },
16900     
16901     insertText : function(txt)
16902     {
16903         
16904         
16905         var range = this.createRange();
16906         range.deleteContents();
16907                //alert(Sender.getAttribute('label'));
16908                
16909         range.insertNode(this.doc.createTextNode(txt));
16910     } ,
16911     
16912      
16913
16914     /**
16915      * Executes a Midas editor command on the editor document and performs necessary focus and
16916      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16917      * @param {String} cmd The Midas command
16918      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16919      */
16920     relayCmd : function(cmd, value){
16921         this.win.focus();
16922         this.execCmd(cmd, value);
16923         this.owner.fireEvent('editorevent', this);
16924         //this.updateToolbar();
16925         this.owner.deferFocus();
16926     },
16927
16928     /**
16929      * Executes a Midas editor command directly on the editor document.
16930      * For visual commands, you should use {@link #relayCmd} instead.
16931      * <b>This should only be called after the editor is initialized.</b>
16932      * @param {String} cmd The Midas command
16933      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16934      */
16935     execCmd : function(cmd, value){
16936         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16937         this.syncValue();
16938     },
16939  
16940  
16941    
16942     /**
16943      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16944      * to insert tRoo.
16945      * @param {String} text | dom node.. 
16946      */
16947     insertAtCursor : function(text)
16948     {
16949         
16950         
16951         
16952         if(!this.activated){
16953             return;
16954         }
16955         /*
16956         if(Roo.isIE){
16957             this.win.focus();
16958             var r = this.doc.selection.createRange();
16959             if(r){
16960                 r.collapse(true);
16961                 r.pasteHTML(text);
16962                 this.syncValue();
16963                 this.deferFocus();
16964             
16965             }
16966             return;
16967         }
16968         */
16969         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16970             this.win.focus();
16971             
16972             
16973             // from jquery ui (MIT licenced)
16974             var range, node;
16975             var win = this.win;
16976             
16977             if (win.getSelection && win.getSelection().getRangeAt) {
16978                 range = win.getSelection().getRangeAt(0);
16979                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16980                 range.insertNode(node);
16981             } else if (win.document.selection && win.document.selection.createRange) {
16982                 // no firefox support
16983                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16984                 win.document.selection.createRange().pasteHTML(txt);
16985             } else {
16986                 // no firefox support
16987                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16988                 this.execCmd('InsertHTML', txt);
16989             } 
16990             
16991             this.syncValue();
16992             
16993             this.deferFocus();
16994         }
16995     },
16996  // private
16997     mozKeyPress : function(e){
16998         if(e.ctrlKey){
16999             var c = e.getCharCode(), cmd;
17000           
17001             if(c > 0){
17002                 c = String.fromCharCode(c).toLowerCase();
17003                 switch(c){
17004                     case 'b':
17005                         cmd = 'bold';
17006                         break;
17007                     case 'i':
17008                         cmd = 'italic';
17009                         break;
17010                     
17011                     case 'u':
17012                         cmd = 'underline';
17013                         break;
17014                     
17015                     case 'v':
17016                         this.cleanUpPaste.defer(100, this);
17017                         return;
17018                         
17019                 }
17020                 if(cmd){
17021                     this.win.focus();
17022                     this.execCmd(cmd);
17023                     this.deferFocus();
17024                     e.preventDefault();
17025                 }
17026                 
17027             }
17028         }
17029     },
17030
17031     // private
17032     fixKeys : function(){ // load time branching for fastest keydown performance
17033         if(Roo.isIE){
17034             return function(e){
17035                 var k = e.getKey(), r;
17036                 if(k == e.TAB){
17037                     e.stopEvent();
17038                     r = this.doc.selection.createRange();
17039                     if(r){
17040                         r.collapse(true);
17041                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17042                         this.deferFocus();
17043                     }
17044                     return;
17045                 }
17046                 
17047                 if(k == e.ENTER){
17048                     r = this.doc.selection.createRange();
17049                     if(r){
17050                         var target = r.parentElement();
17051                         if(!target || target.tagName.toLowerCase() != 'li'){
17052                             e.stopEvent();
17053                             r.pasteHTML('<br />');
17054                             r.collapse(false);
17055                             r.select();
17056                         }
17057                     }
17058                 }
17059                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17060                     this.cleanUpPaste.defer(100, this);
17061                     return;
17062                 }
17063                 
17064                 
17065             };
17066         }else if(Roo.isOpera){
17067             return function(e){
17068                 var k = e.getKey();
17069                 if(k == e.TAB){
17070                     e.stopEvent();
17071                     this.win.focus();
17072                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17073                     this.deferFocus();
17074                 }
17075                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17076                     this.cleanUpPaste.defer(100, this);
17077                     return;
17078                 }
17079                 
17080             };
17081         }else if(Roo.isSafari){
17082             return function(e){
17083                 var k = e.getKey();
17084                 
17085                 if(k == e.TAB){
17086                     e.stopEvent();
17087                     this.execCmd('InsertText','\t');
17088                     this.deferFocus();
17089                     return;
17090                 }
17091                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17092                     this.cleanUpPaste.defer(100, this);
17093                     return;
17094                 }
17095                 
17096              };
17097         }
17098     }(),
17099     
17100     getAllAncestors: function()
17101     {
17102         var p = this.getSelectedNode();
17103         var a = [];
17104         if (!p) {
17105             a.push(p); // push blank onto stack..
17106             p = this.getParentElement();
17107         }
17108         
17109         
17110         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17111             a.push(p);
17112             p = p.parentNode;
17113         }
17114         a.push(this.doc.body);
17115         return a;
17116     },
17117     lastSel : false,
17118     lastSelNode : false,
17119     
17120     
17121     getSelection : function() 
17122     {
17123         this.assignDocWin();
17124         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17125     },
17126     
17127     getSelectedNode: function() 
17128     {
17129         // this may only work on Gecko!!!
17130         
17131         // should we cache this!!!!
17132         
17133         
17134         
17135          
17136         var range = this.createRange(this.getSelection()).cloneRange();
17137         
17138         if (Roo.isIE) {
17139             var parent = range.parentElement();
17140             while (true) {
17141                 var testRange = range.duplicate();
17142                 testRange.moveToElementText(parent);
17143                 if (testRange.inRange(range)) {
17144                     break;
17145                 }
17146                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17147                     break;
17148                 }
17149                 parent = parent.parentElement;
17150             }
17151             return parent;
17152         }
17153         
17154         // is ancestor a text element.
17155         var ac =  range.commonAncestorContainer;
17156         if (ac.nodeType == 3) {
17157             ac = ac.parentNode;
17158         }
17159         
17160         var ar = ac.childNodes;
17161          
17162         var nodes = [];
17163         var other_nodes = [];
17164         var has_other_nodes = false;
17165         for (var i=0;i<ar.length;i++) {
17166             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17167                 continue;
17168             }
17169             // fullly contained node.
17170             
17171             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17172                 nodes.push(ar[i]);
17173                 continue;
17174             }
17175             
17176             // probably selected..
17177             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17178                 other_nodes.push(ar[i]);
17179                 continue;
17180             }
17181             // outer..
17182             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17183                 continue;
17184             }
17185             
17186             
17187             has_other_nodes = true;
17188         }
17189         if (!nodes.length && other_nodes.length) {
17190             nodes= other_nodes;
17191         }
17192         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17193             return false;
17194         }
17195         
17196         return nodes[0];
17197     },
17198     createRange: function(sel)
17199     {
17200         // this has strange effects when using with 
17201         // top toolbar - not sure if it's a great idea.
17202         //this.editor.contentWindow.focus();
17203         if (typeof sel != "undefined") {
17204             try {
17205                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17206             } catch(e) {
17207                 return this.doc.createRange();
17208             }
17209         } else {
17210             return this.doc.createRange();
17211         }
17212     },
17213     getParentElement: function()
17214     {
17215         
17216         this.assignDocWin();
17217         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17218         
17219         var range = this.createRange(sel);
17220          
17221         try {
17222             var p = range.commonAncestorContainer;
17223             while (p.nodeType == 3) { // text node
17224                 p = p.parentNode;
17225             }
17226             return p;
17227         } catch (e) {
17228             return null;
17229         }
17230     
17231     },
17232     /***
17233      *
17234      * Range intersection.. the hard stuff...
17235      *  '-1' = before
17236      *  '0' = hits..
17237      *  '1' = after.
17238      *         [ -- selected range --- ]
17239      *   [fail]                        [fail]
17240      *
17241      *    basically..
17242      *      if end is before start or  hits it. fail.
17243      *      if start is after end or hits it fail.
17244      *
17245      *   if either hits (but other is outside. - then it's not 
17246      *   
17247      *    
17248      **/
17249     
17250     
17251     // @see http://www.thismuchiknow.co.uk/?p=64.
17252     rangeIntersectsNode : function(range, node)
17253     {
17254         var nodeRange = node.ownerDocument.createRange();
17255         try {
17256             nodeRange.selectNode(node);
17257         } catch (e) {
17258             nodeRange.selectNodeContents(node);
17259         }
17260     
17261         var rangeStartRange = range.cloneRange();
17262         rangeStartRange.collapse(true);
17263     
17264         var rangeEndRange = range.cloneRange();
17265         rangeEndRange.collapse(false);
17266     
17267         var nodeStartRange = nodeRange.cloneRange();
17268         nodeStartRange.collapse(true);
17269     
17270         var nodeEndRange = nodeRange.cloneRange();
17271         nodeEndRange.collapse(false);
17272     
17273         return rangeStartRange.compareBoundaryPoints(
17274                  Range.START_TO_START, nodeEndRange) == -1 &&
17275                rangeEndRange.compareBoundaryPoints(
17276                  Range.START_TO_START, nodeStartRange) == 1;
17277         
17278          
17279     },
17280     rangeCompareNode : function(range, node)
17281     {
17282         var nodeRange = node.ownerDocument.createRange();
17283         try {
17284             nodeRange.selectNode(node);
17285         } catch (e) {
17286             nodeRange.selectNodeContents(node);
17287         }
17288         
17289         
17290         range.collapse(true);
17291     
17292         nodeRange.collapse(true);
17293      
17294         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17295         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17296          
17297         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17298         
17299         var nodeIsBefore   =  ss == 1;
17300         var nodeIsAfter    = ee == -1;
17301         
17302         if (nodeIsBefore && nodeIsAfter)
17303             return 0; // outer
17304         if (!nodeIsBefore && nodeIsAfter)
17305             return 1; //right trailed.
17306         
17307         if (nodeIsBefore && !nodeIsAfter)
17308             return 2;  // left trailed.
17309         // fully contined.
17310         return 3;
17311     },
17312
17313     // private? - in a new class?
17314     cleanUpPaste :  function()
17315     {
17316         // cleans up the whole document..
17317         Roo.log('cleanuppaste');
17318         
17319         this.cleanUpChildren(this.doc.body);
17320         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17321         if (clean != this.doc.body.innerHTML) {
17322             this.doc.body.innerHTML = clean;
17323         }
17324         
17325     },
17326     
17327     cleanWordChars : function(input) {// change the chars to hex code
17328         var he = Roo.HtmlEditorCore;
17329         
17330         var output = input;
17331         Roo.each(he.swapCodes, function(sw) { 
17332             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17333             
17334             output = output.replace(swapper, sw[1]);
17335         });
17336         
17337         return output;
17338     },
17339     
17340     
17341     cleanUpChildren : function (n)
17342     {
17343         if (!n.childNodes.length) {
17344             return;
17345         }
17346         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17347            this.cleanUpChild(n.childNodes[i]);
17348         }
17349     },
17350     
17351     
17352         
17353     
17354     cleanUpChild : function (node)
17355     {
17356         var ed = this;
17357         //console.log(node);
17358         if (node.nodeName == "#text") {
17359             // clean up silly Windows -- stuff?
17360             return; 
17361         }
17362         if (node.nodeName == "#comment") {
17363             node.parentNode.removeChild(node);
17364             // clean up silly Windows -- stuff?
17365             return; 
17366         }
17367         var lcname = node.tagName.toLowerCase();
17368         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17369         // whitelist of tags..
17370         
17371         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17372             // remove node.
17373             node.parentNode.removeChild(node);
17374             return;
17375             
17376         }
17377         
17378         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17379         
17380         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17381         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17382         
17383         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17384         //    remove_keep_children = true;
17385         //}
17386         
17387         if (remove_keep_children) {
17388             this.cleanUpChildren(node);
17389             // inserts everything just before this node...
17390             while (node.childNodes.length) {
17391                 var cn = node.childNodes[0];
17392                 node.removeChild(cn);
17393                 node.parentNode.insertBefore(cn, node);
17394             }
17395             node.parentNode.removeChild(node);
17396             return;
17397         }
17398         
17399         if (!node.attributes || !node.attributes.length) {
17400             this.cleanUpChildren(node);
17401             return;
17402         }
17403         
17404         function cleanAttr(n,v)
17405         {
17406             
17407             if (v.match(/^\./) || v.match(/^\//)) {
17408                 return;
17409             }
17410             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17411                 return;
17412             }
17413             if (v.match(/^#/)) {
17414                 return;
17415             }
17416 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17417             node.removeAttribute(n);
17418             
17419         }
17420         
17421         var cwhite = this.cwhite;
17422         var cblack = this.cblack;
17423             
17424         function cleanStyle(n,v)
17425         {
17426             if (v.match(/expression/)) { //XSS?? should we even bother..
17427                 node.removeAttribute(n);
17428                 return;
17429             }
17430             
17431             var parts = v.split(/;/);
17432             var clean = [];
17433             
17434             Roo.each(parts, function(p) {
17435                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17436                 if (!p.length) {
17437                     return true;
17438                 }
17439                 var l = p.split(':').shift().replace(/\s+/g,'');
17440                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17441                 
17442                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17443 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17444                     //node.removeAttribute(n);
17445                     return true;
17446                 }
17447                 //Roo.log()
17448                 // only allow 'c whitelisted system attributes'
17449                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17450 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17451                     //node.removeAttribute(n);
17452                     return true;
17453                 }
17454                 
17455                 
17456                  
17457                 
17458                 clean.push(p);
17459                 return true;
17460             });
17461             if (clean.length) { 
17462                 node.setAttribute(n, clean.join(';'));
17463             } else {
17464                 node.removeAttribute(n);
17465             }
17466             
17467         }
17468         
17469         
17470         for (var i = node.attributes.length-1; i > -1 ; i--) {
17471             var a = node.attributes[i];
17472             //console.log(a);
17473             
17474             if (a.name.toLowerCase().substr(0,2)=='on')  {
17475                 node.removeAttribute(a.name);
17476                 continue;
17477             }
17478             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17479                 node.removeAttribute(a.name);
17480                 continue;
17481             }
17482             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17483                 cleanAttr(a.name,a.value); // fixme..
17484                 continue;
17485             }
17486             if (a.name == 'style') {
17487                 cleanStyle(a.name,a.value);
17488                 continue;
17489             }
17490             /// clean up MS crap..
17491             // tecnically this should be a list of valid class'es..
17492             
17493             
17494             if (a.name == 'class') {
17495                 if (a.value.match(/^Mso/)) {
17496                     node.className = '';
17497                 }
17498                 
17499                 if (a.value.match(/body/)) {
17500                     node.className = '';
17501                 }
17502                 continue;
17503             }
17504             
17505             // style cleanup!?
17506             // class cleanup?
17507             
17508         }
17509         
17510         
17511         this.cleanUpChildren(node);
17512         
17513         
17514     },
17515     /**
17516      * Clean up MS wordisms...
17517      */
17518     cleanWord : function(node)
17519     {
17520         var _t = this;
17521         var cleanWordChildren = function()
17522         {
17523             if (!node.childNodes.length) {
17524                 return;
17525             }
17526             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17527                _t.cleanWord(node.childNodes[i]);
17528             }
17529         }
17530         
17531         
17532         if (!node) {
17533             this.cleanWord(this.doc.body);
17534             return;
17535         }
17536         if (node.nodeName == "#text") {
17537             // clean up silly Windows -- stuff?
17538             return; 
17539         }
17540         if (node.nodeName == "#comment") {
17541             node.parentNode.removeChild(node);
17542             // clean up silly Windows -- stuff?
17543             return; 
17544         }
17545         
17546         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17547             node.parentNode.removeChild(node);
17548             return;
17549         }
17550         
17551         // remove - but keep children..
17552         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17553             while (node.childNodes.length) {
17554                 var cn = node.childNodes[0];
17555                 node.removeChild(cn);
17556                 node.parentNode.insertBefore(cn, node);
17557             }
17558             node.parentNode.removeChild(node);
17559             cleanWordChildren();
17560             return;
17561         }
17562         // clean styles
17563         if (node.className.length) {
17564             
17565             var cn = node.className.split(/\W+/);
17566             var cna = [];
17567             Roo.each(cn, function(cls) {
17568                 if (cls.match(/Mso[a-zA-Z]+/)) {
17569                     return;
17570                 }
17571                 cna.push(cls);
17572             });
17573             node.className = cna.length ? cna.join(' ') : '';
17574             if (!cna.length) {
17575                 node.removeAttribute("class");
17576             }
17577         }
17578         
17579         if (node.hasAttribute("lang")) {
17580             node.removeAttribute("lang");
17581         }
17582         
17583         if (node.hasAttribute("style")) {
17584             
17585             var styles = node.getAttribute("style").split(";");
17586             var nstyle = [];
17587             Roo.each(styles, function(s) {
17588                 if (!s.match(/:/)) {
17589                     return;
17590                 }
17591                 var kv = s.split(":");
17592                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17593                     return;
17594                 }
17595                 // what ever is left... we allow.
17596                 nstyle.push(s);
17597             });
17598             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17599             if (!nstyle.length) {
17600                 node.removeAttribute('style');
17601             }
17602         }
17603         
17604         cleanWordChildren();
17605         
17606         
17607     },
17608     domToHTML : function(currentElement, depth, nopadtext) {
17609         
17610         depth = depth || 0;
17611         nopadtext = nopadtext || false;
17612     
17613         if (!currentElement) {
17614             return this.domToHTML(this.doc.body);
17615         }
17616         
17617         //Roo.log(currentElement);
17618         var j;
17619         var allText = false;
17620         var nodeName = currentElement.nodeName;
17621         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17622         
17623         if  (nodeName == '#text') {
17624             return currentElement.nodeValue;
17625         }
17626         
17627         
17628         var ret = '';
17629         if (nodeName != 'BODY') {
17630              
17631             var i = 0;
17632             // Prints the node tagName, such as <A>, <IMG>, etc
17633             if (tagName) {
17634                 var attr = [];
17635                 for(i = 0; i < currentElement.attributes.length;i++) {
17636                     // quoting?
17637                     var aname = currentElement.attributes.item(i).name;
17638                     if (!currentElement.attributes.item(i).value.length) {
17639                         continue;
17640                     }
17641                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17642                 }
17643                 
17644                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17645             } 
17646             else {
17647                 
17648                 // eack
17649             }
17650         } else {
17651             tagName = false;
17652         }
17653         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17654             return ret;
17655         }
17656         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17657             nopadtext = true;
17658         }
17659         
17660         
17661         // Traverse the tree
17662         i = 0;
17663         var currentElementChild = currentElement.childNodes.item(i);
17664         var allText = true;
17665         var innerHTML  = '';
17666         lastnode = '';
17667         while (currentElementChild) {
17668             // Formatting code (indent the tree so it looks nice on the screen)
17669             var nopad = nopadtext;
17670             if (lastnode == 'SPAN') {
17671                 nopad  = true;
17672             }
17673             // text
17674             if  (currentElementChild.nodeName == '#text') {
17675                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17676                 if (!nopad && toadd.length > 80) {
17677                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17678                 }
17679                 innerHTML  += toadd;
17680                 
17681                 i++;
17682                 currentElementChild = currentElement.childNodes.item(i);
17683                 lastNode = '';
17684                 continue;
17685             }
17686             allText = false;
17687             
17688             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17689                 
17690             // Recursively traverse the tree structure of the child node
17691             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17692             lastnode = currentElementChild.nodeName;
17693             i++;
17694             currentElementChild=currentElement.childNodes.item(i);
17695         }
17696         
17697         ret += innerHTML;
17698         
17699         if (!allText) {
17700                 // The remaining code is mostly for formatting the tree
17701             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17702         }
17703         
17704         
17705         if (tagName) {
17706             ret+= "</"+tagName+">";
17707         }
17708         return ret;
17709         
17710     },
17711         
17712     applyBlacklists : function()
17713     {
17714         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17715         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17716         
17717         this.white = [];
17718         this.black = [];
17719         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17720             if (b.indexOf(tag) > -1) {
17721                 return;
17722             }
17723             this.white.push(tag);
17724             
17725         }, this);
17726         
17727         Roo.each(w, function(tag) {
17728             if (b.indexOf(tag) > -1) {
17729                 return;
17730             }
17731             if (this.white.indexOf(tag) > -1) {
17732                 return;
17733             }
17734             this.white.push(tag);
17735             
17736         }, this);
17737         
17738         
17739         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17740             if (w.indexOf(tag) > -1) {
17741                 return;
17742             }
17743             this.black.push(tag);
17744             
17745         }, this);
17746         
17747         Roo.each(b, function(tag) {
17748             if (w.indexOf(tag) > -1) {
17749                 return;
17750             }
17751             if (this.black.indexOf(tag) > -1) {
17752                 return;
17753             }
17754             this.black.push(tag);
17755             
17756         }, this);
17757         
17758         
17759         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17760         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17761         
17762         this.cwhite = [];
17763         this.cblack = [];
17764         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17765             if (b.indexOf(tag) > -1) {
17766                 return;
17767             }
17768             this.cwhite.push(tag);
17769             
17770         }, this);
17771         
17772         Roo.each(w, function(tag) {
17773             if (b.indexOf(tag) > -1) {
17774                 return;
17775             }
17776             if (this.cwhite.indexOf(tag) > -1) {
17777                 return;
17778             }
17779             this.cwhite.push(tag);
17780             
17781         }, this);
17782         
17783         
17784         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17785             if (w.indexOf(tag) > -1) {
17786                 return;
17787             }
17788             this.cblack.push(tag);
17789             
17790         }, this);
17791         
17792         Roo.each(b, function(tag) {
17793             if (w.indexOf(tag) > -1) {
17794                 return;
17795             }
17796             if (this.cblack.indexOf(tag) > -1) {
17797                 return;
17798             }
17799             this.cblack.push(tag);
17800             
17801         }, this);
17802     }
17803     
17804     // hide stuff that is not compatible
17805     /**
17806      * @event blur
17807      * @hide
17808      */
17809     /**
17810      * @event change
17811      * @hide
17812      */
17813     /**
17814      * @event focus
17815      * @hide
17816      */
17817     /**
17818      * @event specialkey
17819      * @hide
17820      */
17821     /**
17822      * @cfg {String} fieldClass @hide
17823      */
17824     /**
17825      * @cfg {String} focusClass @hide
17826      */
17827     /**
17828      * @cfg {String} autoCreate @hide
17829      */
17830     /**
17831      * @cfg {String} inputType @hide
17832      */
17833     /**
17834      * @cfg {String} invalidClass @hide
17835      */
17836     /**
17837      * @cfg {String} invalidText @hide
17838      */
17839     /**
17840      * @cfg {String} msgFx @hide
17841      */
17842     /**
17843      * @cfg {String} validateOnBlur @hide
17844      */
17845 });
17846
17847 Roo.HtmlEditorCore.white = [
17848         'area', 'br', 'img', 'input', 'hr', 'wbr',
17849         
17850        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17851        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17852        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17853        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17854        'table',   'ul',         'xmp', 
17855        
17856        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17857       'thead',   'tr', 
17858      
17859       'dir', 'menu', 'ol', 'ul', 'dl',
17860        
17861       'embed',  'object'
17862 ];
17863
17864
17865 Roo.HtmlEditorCore.black = [
17866     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17867         'applet', // 
17868         'base',   'basefont', 'bgsound', 'blink',  'body', 
17869         'frame',  'frameset', 'head',    'html',   'ilayer', 
17870         'iframe', 'layer',  'link',     'meta',    'object',   
17871         'script', 'style' ,'title',  'xml' // clean later..
17872 ];
17873 Roo.HtmlEditorCore.clean = [
17874     'script', 'style', 'title', 'xml'
17875 ];
17876 Roo.HtmlEditorCore.remove = [
17877     'font'
17878 ];
17879 // attributes..
17880
17881 Roo.HtmlEditorCore.ablack = [
17882     'on'
17883 ];
17884     
17885 Roo.HtmlEditorCore.aclean = [ 
17886     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17887 ];
17888
17889 // protocols..
17890 Roo.HtmlEditorCore.pwhite= [
17891         'http',  'https',  'mailto'
17892 ];
17893
17894 // white listed style attributes.
17895 Roo.HtmlEditorCore.cwhite= [
17896       //  'text-align', /// default is to allow most things..
17897       
17898          
17899 //        'font-size'//??
17900 ];
17901
17902 // black listed style attributes.
17903 Roo.HtmlEditorCore.cblack= [
17904       //  'font-size' -- this can be set by the project 
17905 ];
17906
17907
17908 Roo.HtmlEditorCore.swapCodes   =[ 
17909     [    8211, "--" ], 
17910     [    8212, "--" ], 
17911     [    8216,  "'" ],  
17912     [    8217, "'" ],  
17913     [    8220, '"' ],  
17914     [    8221, '"' ],  
17915     [    8226, "*" ],  
17916     [    8230, "..." ]
17917 ]; 
17918
17919     /*
17920  * - LGPL
17921  *
17922  * HtmlEditor
17923  * 
17924  */
17925
17926 /**
17927  * @class Roo.bootstrap.HtmlEditor
17928  * @extends Roo.bootstrap.TextArea
17929  * Bootstrap HtmlEditor class
17930
17931  * @constructor
17932  * Create a new HtmlEditor
17933  * @param {Object} config The config object
17934  */
17935
17936 Roo.bootstrap.HtmlEditor = function(config){
17937     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17938     if (!this.toolbars) {
17939         this.toolbars = [];
17940     }
17941     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17942     this.addEvents({
17943             /**
17944              * @event initialize
17945              * Fires when the editor is fully initialized (including the iframe)
17946              * @param {HtmlEditor} this
17947              */
17948             initialize: true,
17949             /**
17950              * @event activate
17951              * Fires when the editor is first receives the focus. Any insertion must wait
17952              * until after this event.
17953              * @param {HtmlEditor} this
17954              */
17955             activate: true,
17956              /**
17957              * @event beforesync
17958              * Fires before the textarea is updated with content from the editor iframe. Return false
17959              * to cancel the sync.
17960              * @param {HtmlEditor} this
17961              * @param {String} html
17962              */
17963             beforesync: true,
17964              /**
17965              * @event beforepush
17966              * Fires before the iframe editor is updated with content from the textarea. Return false
17967              * to cancel the push.
17968              * @param {HtmlEditor} this
17969              * @param {String} html
17970              */
17971             beforepush: true,
17972              /**
17973              * @event sync
17974              * Fires when the textarea is updated with content from the editor iframe.
17975              * @param {HtmlEditor} this
17976              * @param {String} html
17977              */
17978             sync: true,
17979              /**
17980              * @event push
17981              * Fires when the iframe editor is updated with content from the textarea.
17982              * @param {HtmlEditor} this
17983              * @param {String} html
17984              */
17985             push: true,
17986              /**
17987              * @event editmodechange
17988              * Fires when the editor switches edit modes
17989              * @param {HtmlEditor} this
17990              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17991              */
17992             editmodechange: true,
17993             /**
17994              * @event editorevent
17995              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17996              * @param {HtmlEditor} this
17997              */
17998             editorevent: true,
17999             /**
18000              * @event firstfocus
18001              * Fires when on first focus - needed by toolbars..
18002              * @param {HtmlEditor} this
18003              */
18004             firstfocus: true,
18005             /**
18006              * @event autosave
18007              * Auto save the htmlEditor value as a file into Events
18008              * @param {HtmlEditor} this
18009              */
18010             autosave: true,
18011             /**
18012              * @event savedpreview
18013              * preview the saved version of htmlEditor
18014              * @param {HtmlEditor} this
18015              */
18016             savedpreview: true
18017         });
18018 };
18019
18020
18021 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18022     
18023     
18024       /**
18025      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18026      */
18027     toolbars : false,
18028    
18029      /**
18030      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18031      *                        Roo.resizable.
18032      */
18033     resizable : false,
18034      /**
18035      * @cfg {Number} height (in pixels)
18036      */   
18037     height: 300,
18038    /**
18039      * @cfg {Number} width (in pixels)
18040      */   
18041     width: false,
18042     
18043     /**
18044      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18045      * 
18046      */
18047     stylesheets: false,
18048     
18049     // id of frame..
18050     frameId: false,
18051     
18052     // private properties
18053     validationEvent : false,
18054     deferHeight: true,
18055     initialized : false,
18056     activated : false,
18057     
18058     onFocus : Roo.emptyFn,
18059     iframePad:3,
18060     hideMode:'offsets',
18061     
18062     
18063     tbContainer : false,
18064     
18065     toolbarContainer :function() {
18066         return this.wrap.select('.x-html-editor-tb',true).first();
18067     },
18068
18069     /**
18070      * Protected method that will not generally be called directly. It
18071      * is called when the editor creates its toolbar. Override this method if you need to
18072      * add custom toolbar buttons.
18073      * @param {HtmlEditor} editor
18074      */
18075     createToolbar : function(){
18076         
18077         Roo.log("create toolbars");
18078         
18079         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18080         this.toolbars[0].render(this.toolbarContainer());
18081         
18082         return;
18083         
18084 //        if (!editor.toolbars || !editor.toolbars.length) {
18085 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18086 //        }
18087 //        
18088 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18089 //            editor.toolbars[i] = Roo.factory(
18090 //                    typeof(editor.toolbars[i]) == 'string' ?
18091 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18092 //                Roo.bootstrap.HtmlEditor);
18093 //            editor.toolbars[i].init(editor);
18094 //        }
18095     },
18096
18097      
18098     // private
18099     onRender : function(ct, position)
18100     {
18101        // Roo.log("Call onRender: " + this.xtype);
18102         var _t = this;
18103         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18104       
18105         this.wrap = this.inputEl().wrap({
18106             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18107         });
18108         
18109         this.editorcore.onRender(ct, position);
18110          
18111         if (this.resizable) {
18112             this.resizeEl = new Roo.Resizable(this.wrap, {
18113                 pinned : true,
18114                 wrap: true,
18115                 dynamic : true,
18116                 minHeight : this.height,
18117                 height: this.height,
18118                 handles : this.resizable,
18119                 width: this.width,
18120                 listeners : {
18121                     resize : function(r, w, h) {
18122                         _t.onResize(w,h); // -something
18123                     }
18124                 }
18125             });
18126             
18127         }
18128         this.createToolbar(this);
18129        
18130         
18131         if(!this.width && this.resizable){
18132             this.setSize(this.wrap.getSize());
18133         }
18134         if (this.resizeEl) {
18135             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18136             // should trigger onReize..
18137         }
18138         
18139     },
18140
18141     // private
18142     onResize : function(w, h)
18143     {
18144         Roo.log('resize: ' +w + ',' + h );
18145         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18146         var ew = false;
18147         var eh = false;
18148         
18149         if(this.inputEl() ){
18150             if(typeof w == 'number'){
18151                 var aw = w - this.wrap.getFrameWidth('lr');
18152                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18153                 ew = aw;
18154             }
18155             if(typeof h == 'number'){
18156                  var tbh = -11;  // fixme it needs to tool bar size!
18157                 for (var i =0; i < this.toolbars.length;i++) {
18158                     // fixme - ask toolbars for heights?
18159                     tbh += this.toolbars[i].el.getHeight();
18160                     //if (this.toolbars[i].footer) {
18161                     //    tbh += this.toolbars[i].footer.el.getHeight();
18162                     //}
18163                 }
18164               
18165                 
18166                 
18167                 
18168                 
18169                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18170                 ah -= 5; // knock a few pixes off for look..
18171                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18172                 var eh = ah;
18173             }
18174         }
18175         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18176         this.editorcore.onResize(ew,eh);
18177         
18178     },
18179
18180     /**
18181      * Toggles the editor between standard and source edit mode.
18182      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18183      */
18184     toggleSourceEdit : function(sourceEditMode)
18185     {
18186         this.editorcore.toggleSourceEdit(sourceEditMode);
18187         
18188         if(this.editorcore.sourceEditMode){
18189             Roo.log('editor - showing textarea');
18190             
18191 //            Roo.log('in');
18192 //            Roo.log(this.syncValue());
18193             this.syncValue();
18194             this.inputEl().removeClass(['hide', 'x-hidden']);
18195             this.inputEl().dom.removeAttribute('tabIndex');
18196             this.inputEl().focus();
18197         }else{
18198             Roo.log('editor - hiding textarea');
18199 //            Roo.log('out')
18200 //            Roo.log(this.pushValue()); 
18201             this.pushValue();
18202             
18203             this.inputEl().addClass(['hide', 'x-hidden']);
18204             this.inputEl().dom.setAttribute('tabIndex', -1);
18205             //this.deferFocus();
18206         }
18207          
18208         if(this.resizable){
18209             this.setSize(this.wrap.getSize());
18210         }
18211         
18212         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18213     },
18214  
18215     // private (for BoxComponent)
18216     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18217
18218     // private (for BoxComponent)
18219     getResizeEl : function(){
18220         return this.wrap;
18221     },
18222
18223     // private (for BoxComponent)
18224     getPositionEl : function(){
18225         return this.wrap;
18226     },
18227
18228     // private
18229     initEvents : function(){
18230         this.originalValue = this.getValue();
18231     },
18232
18233 //    /**
18234 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18235 //     * @method
18236 //     */
18237 //    markInvalid : Roo.emptyFn,
18238 //    /**
18239 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18240 //     * @method
18241 //     */
18242 //    clearInvalid : Roo.emptyFn,
18243
18244     setValue : function(v){
18245         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18246         this.editorcore.pushValue();
18247     },
18248
18249      
18250     // private
18251     deferFocus : function(){
18252         this.focus.defer(10, this);
18253     },
18254
18255     // doc'ed in Field
18256     focus : function(){
18257         this.editorcore.focus();
18258         
18259     },
18260       
18261
18262     // private
18263     onDestroy : function(){
18264         
18265         
18266         
18267         if(this.rendered){
18268             
18269             for (var i =0; i < this.toolbars.length;i++) {
18270                 // fixme - ask toolbars for heights?
18271                 this.toolbars[i].onDestroy();
18272             }
18273             
18274             this.wrap.dom.innerHTML = '';
18275             this.wrap.remove();
18276         }
18277     },
18278
18279     // private
18280     onFirstFocus : function(){
18281         //Roo.log("onFirstFocus");
18282         this.editorcore.onFirstFocus();
18283          for (var i =0; i < this.toolbars.length;i++) {
18284             this.toolbars[i].onFirstFocus();
18285         }
18286         
18287     },
18288     
18289     // private
18290     syncValue : function()
18291     {   
18292         this.editorcore.syncValue();
18293     },
18294     
18295     pushValue : function()
18296     {   
18297         this.editorcore.pushValue();
18298     }
18299      
18300     
18301     // hide stuff that is not compatible
18302     /**
18303      * @event blur
18304      * @hide
18305      */
18306     /**
18307      * @event change
18308      * @hide
18309      */
18310     /**
18311      * @event focus
18312      * @hide
18313      */
18314     /**
18315      * @event specialkey
18316      * @hide
18317      */
18318     /**
18319      * @cfg {String} fieldClass @hide
18320      */
18321     /**
18322      * @cfg {String} focusClass @hide
18323      */
18324     /**
18325      * @cfg {String} autoCreate @hide
18326      */
18327     /**
18328      * @cfg {String} inputType @hide
18329      */
18330     /**
18331      * @cfg {String} invalidClass @hide
18332      */
18333     /**
18334      * @cfg {String} invalidText @hide
18335      */
18336     /**
18337      * @cfg {String} msgFx @hide
18338      */
18339     /**
18340      * @cfg {String} validateOnBlur @hide
18341      */
18342 });
18343  
18344     
18345    
18346    
18347    
18348       
18349 Roo.namespace('Roo.bootstrap.htmleditor');
18350 /**
18351  * @class Roo.bootstrap.HtmlEditorToolbar1
18352  * Basic Toolbar
18353  * 
18354  * Usage:
18355  *
18356  new Roo.bootstrap.HtmlEditor({
18357     ....
18358     toolbars : [
18359         new Roo.bootstrap.HtmlEditorToolbar1({
18360             disable : { fonts: 1 , format: 1, ..., ... , ...],
18361             btns : [ .... ]
18362         })
18363     }
18364      
18365  * 
18366  * @cfg {Object} disable List of elements to disable..
18367  * @cfg {Array} btns List of additional buttons.
18368  * 
18369  * 
18370  * NEEDS Extra CSS? 
18371  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18372  */
18373  
18374 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18375 {
18376     
18377     Roo.apply(this, config);
18378     
18379     // default disabled, based on 'good practice'..
18380     this.disable = this.disable || {};
18381     Roo.applyIf(this.disable, {
18382         fontSize : true,
18383         colors : true,
18384         specialElements : true
18385     });
18386     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18387     
18388     this.editor = config.editor;
18389     this.editorcore = config.editor.editorcore;
18390     
18391     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18392     
18393     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18394     // dont call parent... till later.
18395 }
18396 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18397      
18398     bar : true,
18399     
18400     editor : false,
18401     editorcore : false,
18402     
18403     
18404     formats : [
18405         "p" ,  
18406         "h1","h2","h3","h4","h5","h6", 
18407         "pre", "code", 
18408         "abbr", "acronym", "address", "cite", "samp", "var",
18409         'div','span'
18410     ],
18411     
18412     onRender : function(ct, position)
18413     {
18414        // Roo.log("Call onRender: " + this.xtype);
18415         
18416        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18417        Roo.log(this.el);
18418        this.el.dom.style.marginBottom = '0';
18419        var _this = this;
18420        var editorcore = this.editorcore;
18421        var editor= this.editor;
18422        
18423        var children = [];
18424        var btn = function(id,cmd , toggle, handler){
18425        
18426             var  event = toggle ? 'toggle' : 'click';
18427        
18428             var a = {
18429                 size : 'sm',
18430                 xtype: 'Button',
18431                 xns: Roo.bootstrap,
18432                 glyphicon : id,
18433                 cmd : id || cmd,
18434                 enableToggle:toggle !== false,
18435                 //html : 'submit'
18436                 pressed : toggle ? false : null,
18437                 listeners : {}
18438             }
18439             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18440                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18441             }
18442             children.push(a);
18443             return a;
18444        }
18445         
18446         var style = {
18447                 xtype: 'Button',
18448                 size : 'sm',
18449                 xns: Roo.bootstrap,
18450                 glyphicon : 'font',
18451                 //html : 'submit'
18452                 menu : {
18453                     xtype: 'Menu',
18454                     xns: Roo.bootstrap,
18455                     items:  []
18456                 }
18457         };
18458         Roo.each(this.formats, function(f) {
18459             style.menu.items.push({
18460                 xtype :'MenuItem',
18461                 xns: Roo.bootstrap,
18462                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18463                 tagname : f,
18464                 listeners : {
18465                     click : function()
18466                     {
18467                         editorcore.insertTag(this.tagname);
18468                         editor.focus();
18469                     }
18470                 }
18471                 
18472             });
18473         });
18474          children.push(style);   
18475             
18476             
18477         btn('bold',false,true);
18478         btn('italic',false,true);
18479         btn('align-left', 'justifyleft',true);
18480         btn('align-center', 'justifycenter',true);
18481         btn('align-right' , 'justifyright',true);
18482         btn('link', false, false, function(btn) {
18483             //Roo.log("create link?");
18484             var url = prompt(this.createLinkText, this.defaultLinkValue);
18485             if(url && url != 'http:/'+'/'){
18486                 this.editorcore.relayCmd('createlink', url);
18487             }
18488         }),
18489         btn('list','insertunorderedlist',true);
18490         btn('pencil', false,true, function(btn){
18491                 Roo.log(this);
18492                 
18493                 this.toggleSourceEdit(btn.pressed);
18494         });
18495         /*
18496         var cog = {
18497                 xtype: 'Button',
18498                 size : 'sm',
18499                 xns: Roo.bootstrap,
18500                 glyphicon : 'cog',
18501                 //html : 'submit'
18502                 menu : {
18503                     xtype: 'Menu',
18504                     xns: Roo.bootstrap,
18505                     items:  []
18506                 }
18507         };
18508         
18509         cog.menu.items.push({
18510             xtype :'MenuItem',
18511             xns: Roo.bootstrap,
18512             html : Clean styles,
18513             tagname : f,
18514             listeners : {
18515                 click : function()
18516                 {
18517                     editorcore.insertTag(this.tagname);
18518                     editor.focus();
18519                 }
18520             }
18521             
18522         });
18523        */
18524         
18525          
18526        this.xtype = 'NavSimplebar';
18527         
18528         for(var i=0;i< children.length;i++) {
18529             
18530             this.buttons.add(this.addxtypeChild(children[i]));
18531             
18532         }
18533         
18534         editor.on('editorevent', this.updateToolbar, this);
18535     },
18536     onBtnClick : function(id)
18537     {
18538        this.editorcore.relayCmd(id);
18539        this.editorcore.focus();
18540     },
18541     
18542     /**
18543      * Protected method that will not generally be called directly. It triggers
18544      * a toolbar update by reading the markup state of the current selection in the editor.
18545      */
18546     updateToolbar: function(){
18547
18548         if(!this.editorcore.activated){
18549             this.editor.onFirstFocus(); // is this neeed?
18550             return;
18551         }
18552
18553         var btns = this.buttons; 
18554         var doc = this.editorcore.doc;
18555         btns.get('bold').setActive(doc.queryCommandState('bold'));
18556         btns.get('italic').setActive(doc.queryCommandState('italic'));
18557         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18558         
18559         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18560         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18561         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18562         
18563         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18564         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18565          /*
18566         
18567         var ans = this.editorcore.getAllAncestors();
18568         if (this.formatCombo) {
18569             
18570             
18571             var store = this.formatCombo.store;
18572             this.formatCombo.setValue("");
18573             for (var i =0; i < ans.length;i++) {
18574                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18575                     // select it..
18576                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18577                     break;
18578                 }
18579             }
18580         }
18581         
18582         
18583         
18584         // hides menus... - so this cant be on a menu...
18585         Roo.bootstrap.MenuMgr.hideAll();
18586         */
18587         Roo.bootstrap.MenuMgr.hideAll();
18588         //this.editorsyncValue();
18589     },
18590     onFirstFocus: function() {
18591         this.buttons.each(function(item){
18592            item.enable();
18593         });
18594     },
18595     toggleSourceEdit : function(sourceEditMode){
18596         
18597           
18598         if(sourceEditMode){
18599             Roo.log("disabling buttons");
18600            this.buttons.each( function(item){
18601                 if(item.cmd != 'pencil'){
18602                     item.disable();
18603                 }
18604             });
18605           
18606         }else{
18607             Roo.log("enabling buttons");
18608             if(this.editorcore.initialized){
18609                 this.buttons.each( function(item){
18610                     item.enable();
18611                 });
18612             }
18613             
18614         }
18615         Roo.log("calling toggole on editor");
18616         // tell the editor that it's been pressed..
18617         this.editor.toggleSourceEdit(sourceEditMode);
18618        
18619     }
18620 });
18621
18622
18623
18624
18625
18626 /**
18627  * @class Roo.bootstrap.Table.AbstractSelectionModel
18628  * @extends Roo.util.Observable
18629  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18630  * implemented by descendant classes.  This class should not be directly instantiated.
18631  * @constructor
18632  */
18633 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18634     this.locked = false;
18635     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18636 };
18637
18638
18639 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18640     /** @ignore Called by the grid automatically. Do not call directly. */
18641     init : function(grid){
18642         this.grid = grid;
18643         this.initEvents();
18644     },
18645
18646     /**
18647      * Locks the selections.
18648      */
18649     lock : function(){
18650         this.locked = true;
18651     },
18652
18653     /**
18654      * Unlocks the selections.
18655      */
18656     unlock : function(){
18657         this.locked = false;
18658     },
18659
18660     /**
18661      * Returns true if the selections are locked.
18662      * @return {Boolean}
18663      */
18664     isLocked : function(){
18665         return this.locked;
18666     }
18667 });
18668 /**
18669  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18670  * @class Roo.bootstrap.Table.RowSelectionModel
18671  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18672  * It supports multiple selections and keyboard selection/navigation. 
18673  * @constructor
18674  * @param {Object} config
18675  */
18676
18677 Roo.bootstrap.Table.RowSelectionModel = function(config){
18678     Roo.apply(this, config);
18679     this.selections = new Roo.util.MixedCollection(false, function(o){
18680         return o.id;
18681     });
18682
18683     this.last = false;
18684     this.lastActive = false;
18685
18686     this.addEvents({
18687         /**
18688              * @event selectionchange
18689              * Fires when the selection changes
18690              * @param {SelectionModel} this
18691              */
18692             "selectionchange" : true,
18693         /**
18694              * @event afterselectionchange
18695              * Fires after the selection changes (eg. by key press or clicking)
18696              * @param {SelectionModel} this
18697              */
18698             "afterselectionchange" : true,
18699         /**
18700              * @event beforerowselect
18701              * Fires when a row is selected being selected, return false to cancel.
18702              * @param {SelectionModel} this
18703              * @param {Number} rowIndex The selected index
18704              * @param {Boolean} keepExisting False if other selections will be cleared
18705              */
18706             "beforerowselect" : true,
18707         /**
18708              * @event rowselect
18709              * Fires when a row is selected.
18710              * @param {SelectionModel} this
18711              * @param {Number} rowIndex The selected index
18712              * @param {Roo.data.Record} r The record
18713              */
18714             "rowselect" : true,
18715         /**
18716              * @event rowdeselect
18717              * Fires when a row is deselected.
18718              * @param {SelectionModel} this
18719              * @param {Number} rowIndex The selected index
18720              */
18721         "rowdeselect" : true
18722     });
18723     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18724     this.locked = false;
18725 };
18726
18727 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18728     /**
18729      * @cfg {Boolean} singleSelect
18730      * True to allow selection of only one row at a time (defaults to false)
18731      */
18732     singleSelect : false,
18733
18734     // private
18735     initEvents : function(){
18736
18737         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18738             this.grid.on("mousedown", this.handleMouseDown, this);
18739         }else{ // allow click to work like normal
18740             this.grid.on("rowclick", this.handleDragableRowClick, this);
18741         }
18742
18743         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18744             "up" : function(e){
18745                 if(!e.shiftKey){
18746                     this.selectPrevious(e.shiftKey);
18747                 }else if(this.last !== false && this.lastActive !== false){
18748                     var last = this.last;
18749                     this.selectRange(this.last,  this.lastActive-1);
18750                     this.grid.getView().focusRow(this.lastActive);
18751                     if(last !== false){
18752                         this.last = last;
18753                     }
18754                 }else{
18755                     this.selectFirstRow();
18756                 }
18757                 this.fireEvent("afterselectionchange", this);
18758             },
18759             "down" : function(e){
18760                 if(!e.shiftKey){
18761                     this.selectNext(e.shiftKey);
18762                 }else if(this.last !== false && this.lastActive !== false){
18763                     var last = this.last;
18764                     this.selectRange(this.last,  this.lastActive+1);
18765                     this.grid.getView().focusRow(this.lastActive);
18766                     if(last !== false){
18767                         this.last = last;
18768                     }
18769                 }else{
18770                     this.selectFirstRow();
18771                 }
18772                 this.fireEvent("afterselectionchange", this);
18773             },
18774             scope: this
18775         });
18776
18777         var view = this.grid.view;
18778         view.on("refresh", this.onRefresh, this);
18779         view.on("rowupdated", this.onRowUpdated, this);
18780         view.on("rowremoved", this.onRemove, this);
18781     },
18782
18783     // private
18784     onRefresh : function(){
18785         var ds = this.grid.dataSource, i, v = this.grid.view;
18786         var s = this.selections;
18787         s.each(function(r){
18788             if((i = ds.indexOfId(r.id)) != -1){
18789                 v.onRowSelect(i);
18790             }else{
18791                 s.remove(r);
18792             }
18793         });
18794     },
18795
18796     // private
18797     onRemove : function(v, index, r){
18798         this.selections.remove(r);
18799     },
18800
18801     // private
18802     onRowUpdated : function(v, index, r){
18803         if(this.isSelected(r)){
18804             v.onRowSelect(index);
18805         }
18806     },
18807
18808     /**
18809      * Select records.
18810      * @param {Array} records The records to select
18811      * @param {Boolean} keepExisting (optional) True to keep existing selections
18812      */
18813     selectRecords : function(records, keepExisting){
18814         if(!keepExisting){
18815             this.clearSelections();
18816         }
18817         var ds = this.grid.dataSource;
18818         for(var i = 0, len = records.length; i < len; i++){
18819             this.selectRow(ds.indexOf(records[i]), true);
18820         }
18821     },
18822
18823     /**
18824      * Gets the number of selected rows.
18825      * @return {Number}
18826      */
18827     getCount : function(){
18828         return this.selections.length;
18829     },
18830
18831     /**
18832      * Selects the first row in the grid.
18833      */
18834     selectFirstRow : function(){
18835         this.selectRow(0);
18836     },
18837
18838     /**
18839      * Select the last row.
18840      * @param {Boolean} keepExisting (optional) True to keep existing selections
18841      */
18842     selectLastRow : function(keepExisting){
18843         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18844     },
18845
18846     /**
18847      * Selects the row immediately following the last selected row.
18848      * @param {Boolean} keepExisting (optional) True to keep existing selections
18849      */
18850     selectNext : function(keepExisting){
18851         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18852             this.selectRow(this.last+1, keepExisting);
18853             this.grid.getView().focusRow(this.last);
18854         }
18855     },
18856
18857     /**
18858      * Selects the row that precedes the last selected row.
18859      * @param {Boolean} keepExisting (optional) True to keep existing selections
18860      */
18861     selectPrevious : function(keepExisting){
18862         if(this.last){
18863             this.selectRow(this.last-1, keepExisting);
18864             this.grid.getView().focusRow(this.last);
18865         }
18866     },
18867
18868     /**
18869      * Returns the selected records
18870      * @return {Array} Array of selected records
18871      */
18872     getSelections : function(){
18873         return [].concat(this.selections.items);
18874     },
18875
18876     /**
18877      * Returns the first selected record.
18878      * @return {Record}
18879      */
18880     getSelected : function(){
18881         return this.selections.itemAt(0);
18882     },
18883
18884
18885     /**
18886      * Clears all selections.
18887      */
18888     clearSelections : function(fast){
18889         if(this.locked) return;
18890         if(fast !== true){
18891             var ds = this.grid.dataSource;
18892             var s = this.selections;
18893             s.each(function(r){
18894                 this.deselectRow(ds.indexOfId(r.id));
18895             }, this);
18896             s.clear();
18897         }else{
18898             this.selections.clear();
18899         }
18900         this.last = false;
18901     },
18902
18903
18904     /**
18905      * Selects all rows.
18906      */
18907     selectAll : function(){
18908         if(this.locked) return;
18909         this.selections.clear();
18910         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18911             this.selectRow(i, true);
18912         }
18913     },
18914
18915     /**
18916      * Returns True if there is a selection.
18917      * @return {Boolean}
18918      */
18919     hasSelection : function(){
18920         return this.selections.length > 0;
18921     },
18922
18923     /**
18924      * Returns True if the specified row is selected.
18925      * @param {Number/Record} record The record or index of the record to check
18926      * @return {Boolean}
18927      */
18928     isSelected : function(index){
18929         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18930         return (r && this.selections.key(r.id) ? true : false);
18931     },
18932
18933     /**
18934      * Returns True if the specified record id is selected.
18935      * @param {String} id The id of record to check
18936      * @return {Boolean}
18937      */
18938     isIdSelected : function(id){
18939         return (this.selections.key(id) ? true : false);
18940     },
18941
18942     // private
18943     handleMouseDown : function(e, t){
18944         var view = this.grid.getView(), rowIndex;
18945         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18946             return;
18947         };
18948         if(e.shiftKey && this.last !== false){
18949             var last = this.last;
18950             this.selectRange(last, rowIndex, e.ctrlKey);
18951             this.last = last; // reset the last
18952             view.focusRow(rowIndex);
18953         }else{
18954             var isSelected = this.isSelected(rowIndex);
18955             if(e.button !== 0 && isSelected){
18956                 view.focusRow(rowIndex);
18957             }else if(e.ctrlKey && isSelected){
18958                 this.deselectRow(rowIndex);
18959             }else if(!isSelected){
18960                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18961                 view.focusRow(rowIndex);
18962             }
18963         }
18964         this.fireEvent("afterselectionchange", this);
18965     },
18966     // private
18967     handleDragableRowClick :  function(grid, rowIndex, e) 
18968     {
18969         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18970             this.selectRow(rowIndex, false);
18971             grid.view.focusRow(rowIndex);
18972              this.fireEvent("afterselectionchange", this);
18973         }
18974     },
18975     
18976     /**
18977      * Selects multiple rows.
18978      * @param {Array} rows Array of the indexes of the row to select
18979      * @param {Boolean} keepExisting (optional) True to keep existing selections
18980      */
18981     selectRows : function(rows, keepExisting){
18982         if(!keepExisting){
18983             this.clearSelections();
18984         }
18985         for(var i = 0, len = rows.length; i < len; i++){
18986             this.selectRow(rows[i], true);
18987         }
18988     },
18989
18990     /**
18991      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18992      * @param {Number} startRow The index of the first row in the range
18993      * @param {Number} endRow The index of the last row in the range
18994      * @param {Boolean} keepExisting (optional) True to retain existing selections
18995      */
18996     selectRange : function(startRow, endRow, keepExisting){
18997         if(this.locked) return;
18998         if(!keepExisting){
18999             this.clearSelections();
19000         }
19001         if(startRow <= endRow){
19002             for(var i = startRow; i <= endRow; i++){
19003                 this.selectRow(i, true);
19004             }
19005         }else{
19006             for(var i = startRow; i >= endRow; i--){
19007                 this.selectRow(i, true);
19008             }
19009         }
19010     },
19011
19012     /**
19013      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19014      * @param {Number} startRow The index of the first row in the range
19015      * @param {Number} endRow The index of the last row in the range
19016      */
19017     deselectRange : function(startRow, endRow, preventViewNotify){
19018         if(this.locked) return;
19019         for(var i = startRow; i <= endRow; i++){
19020             this.deselectRow(i, preventViewNotify);
19021         }
19022     },
19023
19024     /**
19025      * Selects a row.
19026      * @param {Number} row The index of the row to select
19027      * @param {Boolean} keepExisting (optional) True to keep existing selections
19028      */
19029     selectRow : function(index, keepExisting, preventViewNotify){
19030         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19031         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19032             if(!keepExisting || this.singleSelect){
19033                 this.clearSelections();
19034             }
19035             var r = this.grid.dataSource.getAt(index);
19036             this.selections.add(r);
19037             this.last = this.lastActive = index;
19038             if(!preventViewNotify){
19039                 this.grid.getView().onRowSelect(index);
19040             }
19041             this.fireEvent("rowselect", this, index, r);
19042             this.fireEvent("selectionchange", this);
19043         }
19044     },
19045
19046     /**
19047      * Deselects a row.
19048      * @param {Number} row The index of the row to deselect
19049      */
19050     deselectRow : function(index, preventViewNotify){
19051         if(this.locked) return;
19052         if(this.last == index){
19053             this.last = false;
19054         }
19055         if(this.lastActive == index){
19056             this.lastActive = false;
19057         }
19058         var r = this.grid.dataSource.getAt(index);
19059         this.selections.remove(r);
19060         if(!preventViewNotify){
19061             this.grid.getView().onRowDeselect(index);
19062         }
19063         this.fireEvent("rowdeselect", this, index);
19064         this.fireEvent("selectionchange", this);
19065     },
19066
19067     // private
19068     restoreLast : function(){
19069         if(this._last){
19070             this.last = this._last;
19071         }
19072     },
19073
19074     // private
19075     acceptsNav : function(row, col, cm){
19076         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19077     },
19078
19079     // private
19080     onEditorKey : function(field, e){
19081         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19082         if(k == e.TAB){
19083             e.stopEvent();
19084             ed.completeEdit();
19085             if(e.shiftKey){
19086                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19087             }else{
19088                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19089             }
19090         }else if(k == e.ENTER && !e.ctrlKey){
19091             e.stopEvent();
19092             ed.completeEdit();
19093             if(e.shiftKey){
19094                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19095             }else{
19096                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19097             }
19098         }else if(k == e.ESC){
19099             ed.cancelEdit();
19100         }
19101         if(newCell){
19102             g.startEditing(newCell[0], newCell[1]);
19103         }
19104     }
19105 });/*
19106  * Based on:
19107  * Ext JS Library 1.1.1
19108  * Copyright(c) 2006-2007, Ext JS, LLC.
19109  *
19110  * Originally Released Under LGPL - original licence link has changed is not relivant.
19111  *
19112  * Fork - LGPL
19113  * <script type="text/javascript">
19114  */
19115  
19116 /**
19117  * @class Roo.bootstrap.PagingToolbar
19118  * @extends Roo.Row
19119  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19120  * @constructor
19121  * Create a new PagingToolbar
19122  * @param {Object} config The config object
19123  */
19124 Roo.bootstrap.PagingToolbar = function(config)
19125 {
19126     // old args format still supported... - xtype is prefered..
19127         // created from xtype...
19128     var ds = config.dataSource;
19129     this.toolbarItems = [];
19130     if (config.items) {
19131         this.toolbarItems = config.items;
19132 //        config.items = [];
19133     }
19134     
19135     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19136     this.ds = ds;
19137     this.cursor = 0;
19138     if (ds) { 
19139         this.bind(ds);
19140     }
19141     
19142     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19143     
19144 };
19145
19146 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19147     /**
19148      * @cfg {Roo.data.Store} dataSource
19149      * The underlying data store providing the paged data
19150      */
19151     /**
19152      * @cfg {String/HTMLElement/Element} container
19153      * container The id or element that will contain the toolbar
19154      */
19155     /**
19156      * @cfg {Boolean} displayInfo
19157      * True to display the displayMsg (defaults to false)
19158      */
19159     /**
19160      * @cfg {Number} pageSize
19161      * The number of records to display per page (defaults to 20)
19162      */
19163     pageSize: 20,
19164     /**
19165      * @cfg {String} displayMsg
19166      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19167      */
19168     displayMsg : 'Displaying {0} - {1} of {2}',
19169     /**
19170      * @cfg {String} emptyMsg
19171      * The message to display when no records are found (defaults to "No data to display")
19172      */
19173     emptyMsg : 'No data to display',
19174     /**
19175      * Customizable piece of the default paging text (defaults to "Page")
19176      * @type String
19177      */
19178     beforePageText : "Page",
19179     /**
19180      * Customizable piece of the default paging text (defaults to "of %0")
19181      * @type String
19182      */
19183     afterPageText : "of {0}",
19184     /**
19185      * Customizable piece of the default paging text (defaults to "First Page")
19186      * @type String
19187      */
19188     firstText : "First Page",
19189     /**
19190      * Customizable piece of the default paging text (defaults to "Previous Page")
19191      * @type String
19192      */
19193     prevText : "Previous Page",
19194     /**
19195      * Customizable piece of the default paging text (defaults to "Next Page")
19196      * @type String
19197      */
19198     nextText : "Next Page",
19199     /**
19200      * Customizable piece of the default paging text (defaults to "Last Page")
19201      * @type String
19202      */
19203     lastText : "Last Page",
19204     /**
19205      * Customizable piece of the default paging text (defaults to "Refresh")
19206      * @type String
19207      */
19208     refreshText : "Refresh",
19209
19210     buttons : false,
19211     // private
19212     onRender : function(ct, position) 
19213     {
19214         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19215         this.navgroup.parentId = this.id;
19216         this.navgroup.onRender(this.el, null);
19217         // add the buttons to the navgroup
19218         
19219         if(this.displayInfo){
19220             Roo.log(this.el.select('ul.navbar-nav',true).first());
19221             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19222             this.displayEl = this.el.select('.x-paging-info', true).first();
19223 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19224 //            this.displayEl = navel.el.select('span',true).first();
19225         }
19226         
19227         var _this = this;
19228         
19229         if(this.buttons){
19230             Roo.each(_this.buttons, function(e){
19231                Roo.factory(e).onRender(_this.el, null);
19232             });
19233         }
19234             
19235         Roo.each(_this.toolbarItems, function(e) {
19236             _this.navgroup.addItem(e);
19237         });
19238         
19239         this.first = this.navgroup.addItem({
19240             tooltip: this.firstText,
19241             cls: "prev",
19242             icon : 'fa fa-backward',
19243             disabled: true,
19244             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19245         });
19246         
19247         this.prev =  this.navgroup.addItem({
19248             tooltip: this.prevText,
19249             cls: "prev",
19250             icon : 'fa fa-step-backward',
19251             disabled: true,
19252             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19253         });
19254     //this.addSeparator();
19255         
19256         
19257         var field = this.navgroup.addItem( {
19258             tagtype : 'span',
19259             cls : 'x-paging-position',
19260             
19261             html : this.beforePageText  +
19262                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19263                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19264          } ); //?? escaped?
19265         
19266         this.field = field.el.select('input', true).first();
19267         this.field.on("keydown", this.onPagingKeydown, this);
19268         this.field.on("focus", function(){this.dom.select();});
19269     
19270     
19271         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19272         //this.field.setHeight(18);
19273         //this.addSeparator();
19274         this.next = this.navgroup.addItem({
19275             tooltip: this.nextText,
19276             cls: "next",
19277             html : ' <i class="fa fa-step-forward">',
19278             disabled: true,
19279             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19280         });
19281         this.last = this.navgroup.addItem({
19282             tooltip: this.lastText,
19283             icon : 'fa fa-forward',
19284             cls: "next",
19285             disabled: true,
19286             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19287         });
19288     //this.addSeparator();
19289         this.loading = this.navgroup.addItem({
19290             tooltip: this.refreshText,
19291             icon: 'fa fa-refresh',
19292             
19293             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19294         });
19295
19296     },
19297
19298     // private
19299     updateInfo : function(){
19300         if(this.displayEl){
19301             var count = this.ds.getCount();
19302             var msg = count == 0 ?
19303                 this.emptyMsg :
19304                 String.format(
19305                     this.displayMsg,
19306                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19307                 );
19308             this.displayEl.update(msg);
19309         }
19310     },
19311
19312     // private
19313     onLoad : function(ds, r, o){
19314        this.cursor = o.params ? o.params.start : 0;
19315        var d = this.getPageData(),
19316             ap = d.activePage,
19317             ps = d.pages;
19318         
19319        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19320        this.field.dom.value = ap;
19321        this.first.setDisabled(ap == 1);
19322        this.prev.setDisabled(ap == 1);
19323        this.next.setDisabled(ap == ps);
19324        this.last.setDisabled(ap == ps);
19325        this.loading.enable();
19326        this.updateInfo();
19327     },
19328
19329     // private
19330     getPageData : function(){
19331         var total = this.ds.getTotalCount();
19332         return {
19333             total : total,
19334             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19335             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19336         };
19337     },
19338
19339     // private
19340     onLoadError : function(){
19341         this.loading.enable();
19342     },
19343
19344     // private
19345     onPagingKeydown : function(e){
19346         var k = e.getKey();
19347         var d = this.getPageData();
19348         if(k == e.RETURN){
19349             var v = this.field.dom.value, pageNum;
19350             if(!v || isNaN(pageNum = parseInt(v, 10))){
19351                 this.field.dom.value = d.activePage;
19352                 return;
19353             }
19354             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19355             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19356             e.stopEvent();
19357         }
19358         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))
19359         {
19360           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19361           this.field.dom.value = pageNum;
19362           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19363           e.stopEvent();
19364         }
19365         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19366         {
19367           var v = this.field.dom.value, pageNum; 
19368           var increment = (e.shiftKey) ? 10 : 1;
19369           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19370             increment *= -1;
19371           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19372             this.field.dom.value = d.activePage;
19373             return;
19374           }
19375           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19376           {
19377             this.field.dom.value = parseInt(v, 10) + increment;
19378             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19379             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19380           }
19381           e.stopEvent();
19382         }
19383     },
19384
19385     // private
19386     beforeLoad : function(){
19387         if(this.loading){
19388             this.loading.disable();
19389         }
19390     },
19391
19392     // private
19393     onClick : function(which){
19394         var ds = this.ds;
19395         if (!ds) {
19396             return;
19397         }
19398         switch(which){
19399             case "first":
19400                 ds.load({params:{start: 0, limit: this.pageSize}});
19401             break;
19402             case "prev":
19403                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19404             break;
19405             case "next":
19406                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19407             break;
19408             case "last":
19409                 var total = ds.getTotalCount();
19410                 var extra = total % this.pageSize;
19411                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19412                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19413             break;
19414             case "refresh":
19415                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19416             break;
19417         }
19418     },
19419
19420     /**
19421      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19422      * @param {Roo.data.Store} store The data store to unbind
19423      */
19424     unbind : function(ds){
19425         ds.un("beforeload", this.beforeLoad, this);
19426         ds.un("load", this.onLoad, this);
19427         ds.un("loadexception", this.onLoadError, this);
19428         ds.un("remove", this.updateInfo, this);
19429         ds.un("add", this.updateInfo, this);
19430         this.ds = undefined;
19431     },
19432
19433     /**
19434      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19435      * @param {Roo.data.Store} store The data store to bind
19436      */
19437     bind : function(ds){
19438         ds.on("beforeload", this.beforeLoad, this);
19439         ds.on("load", this.onLoad, this);
19440         ds.on("loadexception", this.onLoadError, this);
19441         ds.on("remove", this.updateInfo, this);
19442         ds.on("add", this.updateInfo, this);
19443         this.ds = ds;
19444     }
19445 });/*
19446  * - LGPL
19447  *
19448  * element
19449  * 
19450  */
19451
19452 /**
19453  * @class Roo.bootstrap.MessageBar
19454  * @extends Roo.bootstrap.Component
19455  * Bootstrap MessageBar class
19456  * @cfg {String} html contents of the MessageBar
19457  * @cfg {String} weight (info | success | warning | danger) default info
19458  * @cfg {String} beforeClass insert the bar before the given class
19459  * @cfg {Boolean} closable (true | false) default false
19460  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19461  * 
19462  * @constructor
19463  * Create a new Element
19464  * @param {Object} config The config object
19465  */
19466
19467 Roo.bootstrap.MessageBar = function(config){
19468     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19469 };
19470
19471 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19472     
19473     html: '',
19474     weight: 'info',
19475     closable: false,
19476     fixed: false,
19477     beforeClass: 'bootstrap-sticky-wrap',
19478     
19479     getAutoCreate : function(){
19480         
19481         var cfg = {
19482             tag: 'div',
19483             cls: 'alert alert-dismissable alert-' + this.weight,
19484             cn: [
19485                 {
19486                     tag: 'span',
19487                     cls: 'message',
19488                     html: this.html || ''
19489                 }
19490             ]
19491         }
19492         
19493         if(this.fixed){
19494             cfg.cls += ' alert-messages-fixed';
19495         }
19496         
19497         if(this.closable){
19498             cfg.cn.push({
19499                 tag: 'button',
19500                 cls: 'close',
19501                 html: 'x'
19502             });
19503         }
19504         
19505         return cfg;
19506     },
19507     
19508     onRender : function(ct, position)
19509     {
19510         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19511         
19512         if(!this.el){
19513             var cfg = Roo.apply({},  this.getAutoCreate());
19514             cfg.id = Roo.id();
19515             
19516             if (this.cls) {
19517                 cfg.cls += ' ' + this.cls;
19518             }
19519             if (this.style) {
19520                 cfg.style = this.style;
19521             }
19522             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19523             
19524             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19525         }
19526         
19527         this.el.select('>button.close').on('click', this.hide, this);
19528         
19529     },
19530     
19531     show : function()
19532     {
19533         if (!this.rendered) {
19534             this.render();
19535         }
19536         
19537         this.el.show();
19538         
19539         this.fireEvent('show', this);
19540         
19541     },
19542     
19543     hide : function()
19544     {
19545         if (!this.rendered) {
19546             this.render();
19547         }
19548         
19549         this.el.hide();
19550         
19551         this.fireEvent('hide', this);
19552     },
19553     
19554     update : function()
19555     {
19556 //        var e = this.el.dom.firstChild;
19557 //        
19558 //        if(this.closable){
19559 //            e = e.nextSibling;
19560 //        }
19561 //        
19562 //        e.data = this.html || '';
19563
19564         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19565     }
19566    
19567 });
19568
19569  
19570
19571      /*
19572  * - LGPL
19573  *
19574  * Graph
19575  * 
19576  */
19577
19578
19579 /**
19580  * @class Roo.bootstrap.Graph
19581  * @extends Roo.bootstrap.Component
19582  * Bootstrap Graph class
19583 > Prameters
19584  -sm {number} sm 4
19585  -md {number} md 5
19586  @cfg {String} graphtype  bar | vbar | pie
19587  @cfg {number} g_x coodinator | centre x (pie)
19588  @cfg {number} g_y coodinator | centre y (pie)
19589  @cfg {number} g_r radius (pie)
19590  @cfg {number} g_height height of the chart (respected by all elements in the set)
19591  @cfg {number} g_width width of the chart (respected by all elements in the set)
19592  @cfg {Object} title The title of the chart
19593     
19594  -{Array}  values
19595  -opts (object) options for the chart 
19596      o {
19597      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19598      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19599      o vgutter (number)
19600      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.
19601      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19602      o to
19603      o stretch (boolean)
19604      o }
19605  -opts (object) options for the pie
19606      o{
19607      o cut
19608      o startAngle (number)
19609      o endAngle (number)
19610      } 
19611  *
19612  * @constructor
19613  * Create a new Input
19614  * @param {Object} config The config object
19615  */
19616
19617 Roo.bootstrap.Graph = function(config){
19618     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19619     
19620     this.addEvents({
19621         // img events
19622         /**
19623          * @event click
19624          * The img click event for the img.
19625          * @param {Roo.EventObject} e
19626          */
19627         "click" : true
19628     });
19629 };
19630
19631 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19632     
19633     sm: 4,
19634     md: 5,
19635     graphtype: 'bar',
19636     g_height: 250,
19637     g_width: 400,
19638     g_x: 50,
19639     g_y: 50,
19640     g_r: 30,
19641     opts:{
19642         //g_colors: this.colors,
19643         g_type: 'soft',
19644         g_gutter: '20%'
19645
19646     },
19647     title : false,
19648
19649     getAutoCreate : function(){
19650         
19651         var cfg = {
19652             tag: 'div',
19653             html : null
19654         }
19655         
19656         
19657         return  cfg;
19658     },
19659
19660     onRender : function(ct,position){
19661         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19662         this.raphael = Raphael(this.el.dom);
19663         
19664                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19665                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19666                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19667                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19668                 /*
19669                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19670                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19671                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19672                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19673                 
19674                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19675                 r.barchart(330, 10, 300, 220, data1);
19676                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19677                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19678                 */
19679                 
19680                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19681                 // r.barchart(30, 30, 560, 250,  xdata, {
19682                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19683                 //     axis : "0 0 1 1",
19684                 //     axisxlabels :  xdata
19685                 //     //yvalues : cols,
19686                    
19687                 // });
19688 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19689 //        
19690 //        this.load(null,xdata,{
19691 //                axis : "0 0 1 1",
19692 //                axisxlabels :  xdata
19693 //                });
19694
19695     },
19696
19697     load : function(graphtype,xdata,opts){
19698         this.raphael.clear();
19699         if(!graphtype) {
19700             graphtype = this.graphtype;
19701         }
19702         if(!opts){
19703             opts = this.opts;
19704         }
19705         var r = this.raphael,
19706             fin = function () {
19707                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19708             },
19709             fout = function () {
19710                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19711             },
19712             pfin = function() {
19713                 this.sector.stop();
19714                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19715
19716                 if (this.label) {
19717                     this.label[0].stop();
19718                     this.label[0].attr({ r: 7.5 });
19719                     this.label[1].attr({ "font-weight": 800 });
19720                 }
19721             },
19722             pfout = function() {
19723                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19724
19725                 if (this.label) {
19726                     this.label[0].animate({ r: 5 }, 500, "bounce");
19727                     this.label[1].attr({ "font-weight": 400 });
19728                 }
19729             };
19730
19731         switch(graphtype){
19732             case 'bar':
19733                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19734                 break;
19735             case 'hbar':
19736                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19737                 break;
19738             case 'pie':
19739 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19740 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19741 //            
19742                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19743                 
19744                 break;
19745
19746         }
19747         
19748         if(this.title){
19749             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19750         }
19751         
19752     },
19753     
19754     setTitle: function(o)
19755     {
19756         this.title = o;
19757     },
19758     
19759     initEvents: function() {
19760         
19761         if(!this.href){
19762             this.el.on('click', this.onClick, this);
19763         }
19764     },
19765     
19766     onClick : function(e)
19767     {
19768         Roo.log('img onclick');
19769         this.fireEvent('click', this, e);
19770     }
19771    
19772 });
19773
19774  
19775 /*
19776  * - LGPL
19777  *
19778  * numberBox
19779  * 
19780  */
19781 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19782
19783 /**
19784  * @class Roo.bootstrap.dash.NumberBox
19785  * @extends Roo.bootstrap.Component
19786  * Bootstrap NumberBox class
19787  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19788  * @cfg {String} headline Box headline
19789  * @cfg {String} content Box content
19790  * @cfg {String} icon Box icon
19791  * @cfg {String} footer Footer text
19792  * @cfg {String} fhref Footer href
19793  * 
19794  * @constructor
19795  * Create a new NumberBox
19796  * @param {Object} config The config object
19797  */
19798
19799
19800 Roo.bootstrap.dash.NumberBox = function(config){
19801     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19802     
19803 };
19804
19805 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19806     
19807     bgcolor : 'aqua',
19808     headline : '',
19809     content : '',
19810     icon : '',
19811     footer : '',
19812     fhref : '',
19813     ficon : '',
19814     
19815     getAutoCreate : function(){
19816         
19817         var cfg = {
19818             tag : 'div',
19819             cls : 'small-box bg-' + this.bgcolor,
19820             cn : [
19821                 {
19822                     tag : 'div',
19823                     cls : 'inner',
19824                     cn :[
19825                         {
19826                             tag : 'h3',
19827                             cls : 'roo-headline',
19828                             html : this.headline
19829                         },
19830                         {
19831                             tag : 'p',
19832                             cls : 'roo-content',
19833                             html : this.content
19834                         }
19835                     ]
19836                 }
19837             ]
19838         }
19839         
19840         if(this.icon){
19841             cfg.cn.push({
19842                 tag : 'div',
19843                 cls : 'icon',
19844                 cn :[
19845                     {
19846                         tag : 'i',
19847                         cls : 'ion ' + this.icon
19848                     }
19849                 ]
19850             });
19851         }
19852         
19853         if(this.footer){
19854             var footer = {
19855                 tag : 'a',
19856                 cls : 'small-box-footer',
19857                 href : this.fhref || '#',
19858                 html : this.footer
19859             };
19860             
19861             cfg.cn.push(footer);
19862             
19863         }
19864         
19865         return  cfg;
19866     },
19867
19868     onRender : function(ct,position){
19869         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19870
19871
19872        
19873                 
19874     },
19875
19876     setHeadline: function (value)
19877     {
19878         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19879     },
19880     
19881     setFooter: function (value, href)
19882     {
19883         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19884         
19885         if(href){
19886             this.el.select('a.small-box-footer',true).first().attr('href', href);
19887         }
19888         
19889     },
19890
19891     setContent: function (value)
19892     {
19893         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19894     },
19895
19896     initEvents: function() 
19897     {   
19898         
19899     }
19900     
19901 });
19902
19903  
19904 /*
19905  * - LGPL
19906  *
19907  * TabBox
19908  * 
19909  */
19910 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19911
19912 /**
19913  * @class Roo.bootstrap.dash.TabBox
19914  * @extends Roo.bootstrap.Component
19915  * Bootstrap TabBox class
19916  * @cfg {String} title Title of the TabBox
19917  * @cfg {String} icon Icon of the TabBox
19918  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19919  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19920  * 
19921  * @constructor
19922  * Create a new TabBox
19923  * @param {Object} config The config object
19924  */
19925
19926
19927 Roo.bootstrap.dash.TabBox = function(config){
19928     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19929     this.addEvents({
19930         // raw events
19931         /**
19932          * @event addpane
19933          * When a pane is added
19934          * @param {Roo.bootstrap.dash.TabPane} pane
19935          */
19936         "addpane" : true,
19937         /**
19938          * @event activatepane
19939          * When a pane is activated
19940          * @param {Roo.bootstrap.dash.TabPane} pane
19941          */
19942         "activatepane" : true
19943         
19944          
19945     });
19946     
19947     this.panes = [];
19948 };
19949
19950 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19951
19952     title : '',
19953     icon : false,
19954     showtabs : true,
19955     tabScrollable : false,
19956     
19957     getChildContainer : function()
19958     {
19959         return this.el.select('.tab-content', true).first();
19960     },
19961     
19962     getAutoCreate : function(){
19963         
19964         var header = {
19965             tag: 'li',
19966             cls: 'pull-left header',
19967             html: this.title,
19968             cn : []
19969         };
19970         
19971         if(this.icon){
19972             header.cn.push({
19973                 tag: 'i',
19974                 cls: 'fa ' + this.icon
19975             });
19976         }
19977         
19978         var h = {
19979             tag: 'ul',
19980             cls: 'nav nav-tabs pull-right',
19981             cn: [
19982                 header
19983             ]
19984         };
19985         
19986         if(this.tabScrollable){
19987             h = {
19988                 tag: 'div',
19989                 cls: 'tab-header',
19990                 cn: [
19991                     {
19992                         tag: 'ul',
19993                         cls: 'nav nav-tabs pull-right',
19994                         cn: [
19995                             header
19996                         ]
19997                     }
19998                 ]
19999             }
20000         }
20001         
20002         var cfg = {
20003             tag: 'div',
20004             cls: 'nav-tabs-custom',
20005             cn: [
20006                 h,
20007                 {
20008                     tag: 'div',
20009                     cls: 'tab-content no-padding',
20010                     cn: []
20011                 }
20012             ]
20013         }
20014
20015         return  cfg;
20016     },
20017     initEvents : function()
20018     {
20019         //Roo.log('add add pane handler');
20020         this.on('addpane', this.onAddPane, this);
20021     },
20022      /**
20023      * Updates the box title
20024      * @param {String} html to set the title to.
20025      */
20026     setTitle : function(value)
20027     {
20028         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20029     },
20030     onAddPane : function(pane)
20031     {
20032         this.panes.push(pane);
20033         //Roo.log('addpane');
20034         //Roo.log(pane);
20035         // tabs are rendere left to right..
20036         if(!this.showtabs){
20037             return;
20038         }
20039         
20040         var ctr = this.el.select('.nav-tabs', true).first();
20041          
20042          
20043         var existing = ctr.select('.nav-tab',true);
20044         var qty = existing.getCount();;
20045         
20046         
20047         var tab = ctr.createChild({
20048             tag : 'li',
20049             cls : 'nav-tab' + (qty ? '' : ' active'),
20050             cn : [
20051                 {
20052                     tag : 'a',
20053                     href:'#',
20054                     html : pane.title
20055                 }
20056             ]
20057         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20058         pane.tab = tab;
20059         
20060         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20061         if (!qty) {
20062             pane.el.addClass('active');
20063         }
20064         
20065                 
20066     },
20067     onTabClick : function(ev,un,ob,pane)
20068     {
20069         //Roo.log('tab - prev default');
20070         ev.preventDefault();
20071         
20072         
20073         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20074         pane.tab.addClass('active');
20075         //Roo.log(pane.title);
20076         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20077         // technically we should have a deactivate event.. but maybe add later.
20078         // and it should not de-activate the selected tab...
20079         this.fireEvent('activatepane', pane);
20080         pane.el.addClass('active');
20081         pane.fireEvent('activate');
20082         
20083         
20084     },
20085     
20086     getActivePane : function()
20087     {
20088         var r = false;
20089         Roo.each(this.panes, function(p) {
20090             if(p.el.hasClass('active')){
20091                 r = p;
20092                 return false;
20093             }
20094             
20095             return;
20096         });
20097         
20098         return r;
20099     }
20100     
20101     
20102 });
20103
20104  
20105 /*
20106  * - LGPL
20107  *
20108  * Tab pane
20109  * 
20110  */
20111 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20112 /**
20113  * @class Roo.bootstrap.TabPane
20114  * @extends Roo.bootstrap.Component
20115  * Bootstrap TabPane class
20116  * @cfg {Boolean} active (false | true) Default false
20117  * @cfg {String} title title of panel
20118
20119  * 
20120  * @constructor
20121  * Create a new TabPane
20122  * @param {Object} config The config object
20123  */
20124
20125 Roo.bootstrap.dash.TabPane = function(config){
20126     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20127     
20128     this.addEvents({
20129         // raw events
20130         /**
20131          * @event activate
20132          * When a pane is activated
20133          * @param {Roo.bootstrap.dash.TabPane} pane
20134          */
20135         "activate" : true
20136          
20137     });
20138 };
20139
20140 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20141     
20142     active : false,
20143     title : '',
20144     
20145     // the tabBox that this is attached to.
20146     tab : false,
20147      
20148     getAutoCreate : function() 
20149     {
20150         var cfg = {
20151             tag: 'div',
20152             cls: 'tab-pane'
20153         }
20154         
20155         if(this.active){
20156             cfg.cls += ' active';
20157         }
20158         
20159         return cfg;
20160     },
20161     initEvents  : function()
20162     {
20163         //Roo.log('trigger add pane handler');
20164         this.parent().fireEvent('addpane', this)
20165     },
20166     
20167      /**
20168      * Updates the tab title 
20169      * @param {String} html to set the title to.
20170      */
20171     setTitle: function(str)
20172     {
20173         if (!this.tab) {
20174             return;
20175         }
20176         this.title = str;
20177         this.tab.select('a', true).first().dom.innerHTML = str;
20178         
20179     }
20180     
20181     
20182     
20183 });
20184
20185  
20186
20187
20188  /*
20189  * - LGPL
20190  *
20191  * menu
20192  * 
20193  */
20194 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20195
20196 /**
20197  * @class Roo.bootstrap.menu.Menu
20198  * @extends Roo.bootstrap.Component
20199  * Bootstrap Menu class - container for Menu
20200  * @cfg {String} html Text of the menu
20201  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20202  * @cfg {String} icon Font awesome icon
20203  * @cfg {String} pos Menu align to (top | bottom) default bottom
20204  * 
20205  * 
20206  * @constructor
20207  * Create a new Menu
20208  * @param {Object} config The config object
20209  */
20210
20211
20212 Roo.bootstrap.menu.Menu = function(config){
20213     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20214     
20215     this.addEvents({
20216         /**
20217          * @event beforeshow
20218          * Fires before this menu is displayed
20219          * @param {Roo.bootstrap.menu.Menu} this
20220          */
20221         beforeshow : true,
20222         /**
20223          * @event beforehide
20224          * Fires before this menu is hidden
20225          * @param {Roo.bootstrap.menu.Menu} this
20226          */
20227         beforehide : true,
20228         /**
20229          * @event show
20230          * Fires after this menu is displayed
20231          * @param {Roo.bootstrap.menu.Menu} this
20232          */
20233         show : true,
20234         /**
20235          * @event hide
20236          * Fires after this menu is hidden
20237          * @param {Roo.bootstrap.menu.Menu} this
20238          */
20239         hide : true,
20240         /**
20241          * @event click
20242          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20243          * @param {Roo.bootstrap.menu.Menu} this
20244          * @param {Roo.EventObject} e
20245          */
20246         click : true
20247     });
20248     
20249 };
20250
20251 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20252     
20253     submenu : false,
20254     html : '',
20255     weight : 'default',
20256     icon : false,
20257     pos : 'bottom',
20258     
20259     
20260     getChildContainer : function() {
20261         if(this.isSubMenu){
20262             return this.el;
20263         }
20264         
20265         return this.el.select('ul.dropdown-menu', true).first();  
20266     },
20267     
20268     getAutoCreate : function()
20269     {
20270         var text = [
20271             {
20272                 tag : 'span',
20273                 cls : 'roo-menu-text',
20274                 html : this.html
20275             }
20276         ];
20277         
20278         if(this.icon){
20279             text.unshift({
20280                 tag : 'i',
20281                 cls : 'fa ' + this.icon
20282             })
20283         }
20284         
20285         
20286         var cfg = {
20287             tag : 'div',
20288             cls : 'btn-group',
20289             cn : [
20290                 {
20291                     tag : 'button',
20292                     cls : 'dropdown-button btn btn-' + this.weight,
20293                     cn : text
20294                 },
20295                 {
20296                     tag : 'button',
20297                     cls : 'dropdown-toggle btn btn-' + this.weight,
20298                     cn : [
20299                         {
20300                             tag : 'span',
20301                             cls : 'caret'
20302                         }
20303                     ]
20304                 },
20305                 {
20306                     tag : 'ul',
20307                     cls : 'dropdown-menu'
20308                 }
20309             ]
20310             
20311         };
20312         
20313         if(this.pos == 'top'){
20314             cfg.cls += ' dropup';
20315         }
20316         
20317         if(this.isSubMenu){
20318             cfg = {
20319                 tag : 'ul',
20320                 cls : 'dropdown-menu'
20321             }
20322         }
20323         
20324         return cfg;
20325     },
20326     
20327     onRender : function(ct, position)
20328     {
20329         this.isSubMenu = ct.hasClass('dropdown-submenu');
20330         
20331         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20332     },
20333     
20334     initEvents : function() 
20335     {
20336         if(this.isSubMenu){
20337             return;
20338         }
20339         
20340         this.hidden = true;
20341         
20342         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20343         this.triggerEl.on('click', this.onTriggerPress, this);
20344         
20345         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20346         this.buttonEl.on('click', this.onClick, this);
20347         
20348     },
20349     
20350     list : function()
20351     {
20352         if(this.isSubMenu){
20353             return this.el;
20354         }
20355         
20356         return this.el.select('ul.dropdown-menu', true).first();
20357     },
20358     
20359     onClick : function(e)
20360     {
20361         this.fireEvent("click", this, e);
20362     },
20363     
20364     onTriggerPress  : function(e)
20365     {   
20366         if (this.isVisible()) {
20367             this.hide();
20368         } else {
20369             this.show();
20370         }
20371     },
20372     
20373     isVisible : function(){
20374         return !this.hidden;
20375     },
20376     
20377     show : function()
20378     {
20379         this.fireEvent("beforeshow", this);
20380         
20381         this.hidden = false;
20382         this.el.addClass('open');
20383         
20384         Roo.get(document).on("mouseup", this.onMouseUp, this);
20385         
20386         this.fireEvent("show", this);
20387         
20388         
20389     },
20390     
20391     hide : function()
20392     {
20393         this.fireEvent("beforehide", this);
20394         
20395         this.hidden = true;
20396         this.el.removeClass('open');
20397         
20398         Roo.get(document).un("mouseup", this.onMouseUp);
20399         
20400         this.fireEvent("hide", this);
20401     },
20402     
20403     onMouseUp : function()
20404     {
20405         this.hide();
20406     }
20407     
20408 });
20409
20410  
20411  /*
20412  * - LGPL
20413  *
20414  * menu item
20415  * 
20416  */
20417 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20418
20419 /**
20420  * @class Roo.bootstrap.menu.Item
20421  * @extends Roo.bootstrap.Component
20422  * Bootstrap MenuItem class
20423  * @cfg {Boolean} submenu (true | false) default false
20424  * @cfg {String} html text of the item
20425  * @cfg {String} href the link
20426  * @cfg {Boolean} disable (true | false) default false
20427  * @cfg {Boolean} preventDefault (true | false) default true
20428  * @cfg {String} icon Font awesome icon
20429  * @cfg {String} pos Submenu align to (left | right) default right 
20430  * 
20431  * 
20432  * @constructor
20433  * Create a new Item
20434  * @param {Object} config The config object
20435  */
20436
20437
20438 Roo.bootstrap.menu.Item = function(config){
20439     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20440     this.addEvents({
20441         /**
20442          * @event mouseover
20443          * Fires when the mouse is hovering over this menu
20444          * @param {Roo.bootstrap.menu.Item} this
20445          * @param {Roo.EventObject} e
20446          */
20447         mouseover : true,
20448         /**
20449          * @event mouseout
20450          * Fires when the mouse exits this menu
20451          * @param {Roo.bootstrap.menu.Item} this
20452          * @param {Roo.EventObject} e
20453          */
20454         mouseout : true,
20455         // raw events
20456         /**
20457          * @event click
20458          * The raw click event for the entire grid.
20459          * @param {Roo.EventObject} e
20460          */
20461         click : true
20462     });
20463 };
20464
20465 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20466     
20467     submenu : false,
20468     href : '',
20469     html : '',
20470     preventDefault: true,
20471     disable : false,
20472     icon : false,
20473     pos : 'right',
20474     
20475     getAutoCreate : function()
20476     {
20477         var text = [
20478             {
20479                 tag : 'span',
20480                 cls : 'roo-menu-item-text',
20481                 html : this.html
20482             }
20483         ];
20484         
20485         if(this.icon){
20486             text.unshift({
20487                 tag : 'i',
20488                 cls : 'fa ' + this.icon
20489             })
20490         }
20491         
20492         var cfg = {
20493             tag : 'li',
20494             cn : [
20495                 {
20496                     tag : 'a',
20497                     href : this.href || '#',
20498                     cn : text
20499                 }
20500             ]
20501         };
20502         
20503         if(this.disable){
20504             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20505         }
20506         
20507         if(this.submenu){
20508             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20509             
20510             if(this.pos == 'left'){
20511                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20512             }
20513         }
20514         
20515         return cfg;
20516     },
20517     
20518     initEvents : function() 
20519     {
20520         this.el.on('mouseover', this.onMouseOver, this);
20521         this.el.on('mouseout', this.onMouseOut, this);
20522         
20523         this.el.select('a', true).first().on('click', this.onClick, this);
20524         
20525     },
20526     
20527     onClick : function(e)
20528     {
20529         if(this.preventDefault){
20530             e.preventDefault();
20531         }
20532         
20533         this.fireEvent("click", this, e);
20534     },
20535     
20536     onMouseOver : function(e)
20537     {
20538         if(this.submenu && this.pos == 'left'){
20539             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20540         }
20541         
20542         this.fireEvent("mouseover", this, e);
20543     },
20544     
20545     onMouseOut : function(e)
20546     {
20547         this.fireEvent("mouseout", this, e);
20548     }
20549 });
20550
20551  
20552
20553  /*
20554  * - LGPL
20555  *
20556  * menu separator
20557  * 
20558  */
20559 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20560
20561 /**
20562  * @class Roo.bootstrap.menu.Separator
20563  * @extends Roo.bootstrap.Component
20564  * Bootstrap Separator class
20565  * 
20566  * @constructor
20567  * Create a new Separator
20568  * @param {Object} config The config object
20569  */
20570
20571
20572 Roo.bootstrap.menu.Separator = function(config){
20573     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20574 };
20575
20576 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20577     
20578     getAutoCreate : function(){
20579         var cfg = {
20580             tag : 'li',
20581             cls: 'divider'
20582         };
20583         
20584         return cfg;
20585     }
20586    
20587 });
20588
20589  
20590
20591