Roo/bootstrap/Form.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     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = typeof(cntr == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         var cn = this;
183         cntr = typeof(cntr == 'undefined' ) ? 'getChildContainer' : cntr;
184         
185         
186         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
187                     (typeof(tree['flexy:foreach']) != 'undefined');
188           
189         
190         
191         
192         // render the element if it's not BODY.
193         if (tree.xtype != 'Body') {
194            
195             cn = Roo.factory(tree);
196            
197             cn.parentType = this.xtype; //??
198             cn.parentId = this.id;
199             
200             var build_from_html =  Roo.XComponent.build_from_html;
201             
202             
203             // does the container contain child eleemnts with 'xtype' attributes.
204             // that match this xtype..
205             // note - when we render we create these as well..
206             // so we should check to see if body has xtype set.
207             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
208                
209                 var self_cntr_el = Roo.get(this[cntr]());
210                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
211                 
212                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
213                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
214                   
215                   
216                   
217                     cn.el = echild;
218                   //  Roo.log("GOT");
219                     //echild.dom.removeAttribute('xtype');
220                 } else {
221                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
222                    
223                 }
224             }
225            
226             
227                
228             // if object has flexy:if - then it may or may not be rendered.
229             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
230                 // skip a flexy if element.
231                 Roo.log('skipping render');
232              } else {
233                  
234                 // actually if flexy:foreach is found, we really want to create 
235                 // multiple copies here...
236                 
237                 cn.render(this[cntr]());
238              }
239             // then add the element..
240         }
241         
242         
243         // handle the kids..
244         
245         var nitems = [];
246         if (typeof (tree.menu) != 'undefined') {
247             tree.menu.parentType = cn.xtype;
248             tree.menu.triggerEl = cn.el;
249             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
250             
251         }
252         
253         if (!tree.items || !tree.items.length) {
254             cn.items = nitems;
255             return cn;
256         }
257         var items = tree.items;
258         delete tree.items;
259         
260         //Roo.log(items.length);
261             // add the items..
262         for(var i =0;i < items.length;i++) {
263             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
264         }
265         
266         cn.items = nitems;
267         
268         return cn;
269     }
270     
271     
272     
273     
274 });
275
276  /*
277  * - LGPL
278  *
279  * page container.
280  * 
281  */ 
282 Roo.bootstrap.Body = function(config){
283     Roo.bootstrap.Body.superclass.constructor.call(this, config);
284     this.el = Roo.get(document.body);
285 };
286
287 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
288       
289         autoCreate : {
290         cls: 'container'
291     },
292     onRender : function(ct, position){
293         
294         
295         //this.el.addClass([this.fieldClass, this.cls]);
296         
297     }
298     
299     
300  
301    
302 });
303
304  /*
305  * - LGPL
306  *
307  * button group
308  * 
309  */
310
311
312 /**
313  * @class Roo.bootstrap.ButtonGroup
314  * @extends Roo.bootstrap.Component
315  * Bootstrap ButtonGroup class
316  * @cfg {String} size lg | sm | xs (default empty normal)
317  * @cfg {String} align vertical | justified  (default none)
318  * @cfg {String} direction up | down (default down)
319  * @cfg {Boolean} toolbar false | true
320  * @cfg {Boolean} btn true | false
321  * 
322  * 
323  * @constructor
324  * Create a new Input
325  * @param {Object} config The config object
326  */
327
328 Roo.bootstrap.ButtonGroup = function(config){
329     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
330 };
331
332 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
333     
334     size: '',
335     align: '',
336     direction: '',
337     toolbar: false,
338     btn: true,
339
340     getAutoCreate : function(){
341         var cfg = {
342             cls: 'btn-group',
343             html : null
344         }
345         
346         cfg.html = this.html || cfg.html;
347         
348         if (this.toolbar) {
349             cfg = {
350                 cls: 'btn-toolbar',
351                 html: null
352             }
353             
354             return cfg;
355         }
356         
357         if (['vertical','justified'].indexOf(this.align)!==-1) {
358             cfg.cls = 'btn-group-' + this.align;
359             
360             if (this.align == 'justified') {
361                 console.log(this.items);
362             }
363         }
364         
365         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
366             cfg.cls += ' btn-group-' + this.size;
367         }
368         
369         if (this.direction == 'up') {
370             cfg.cls += ' dropup' ;
371         }
372         
373         return cfg;
374     }
375    
376 });
377
378  /*
379  * - LGPL
380  *
381  * button
382  * 
383  */
384
385 /**
386  * @class Roo.bootstrap.Button
387  * @extends Roo.bootstrap.Component
388  * Bootstrap Button class
389  * @cfg {String} html The button content
390  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
391  * @cfg {String} size empty | lg | sm | xs
392  * @cfg {String} tag empty | a | input | submit
393  * @cfg {String} href empty or href
394  * @cfg {Boolean} disabled false | true
395  * @cfg {Boolean} isClose false | true
396  * @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
397  * @cfg {String} badge text for badge
398  * @cfg {String} theme default (or empty) | glow
399  * @cfg {Boolean} inverse false | true
400  * @cfg {Boolean} toggle false | true
401  * @cfg {String} ontext text for on toggle state
402  * @cfg {String} offtext text for off toggle state
403  * @cfg {Boolean} defaulton true | false
404  * @cfg {Boolean} preventDefault (true | false) default true
405  * @cfg {Boolean} removeClass true | false remove the standard class..
406  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
407  * 
408  * @constructor
409  * Create a new button
410  * @param {Object} config The config object
411  */
412
413
414 Roo.bootstrap.Button = function(config){
415     Roo.bootstrap.Button.superclass.constructor.call(this, config);
416     this.addEvents({
417         // raw events
418         /**
419          * @event click
420          * The raw click event for the entire grid.
421          * @param {Roo.EventObject} e
422          */
423         "click" : true
424     });
425 };
426
427 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
428     html: false,
429     active: false,
430     weight: '',
431     size: '',
432     tag: 'button',
433     href: '',
434     disabled: false,
435     isClose: false,
436     glyphicon: '',
437     badge: '',
438     theme: 'default',
439     inverse: false,
440     
441     toggle: false,
442     ontext: 'ON',
443     offtext: 'OFF',
444     defaulton: true,
445     preventDefault: true,
446     removeClass: false,
447     name: false,
448     target: false,
449     
450     getAutoCreate : function(){
451         
452         var cfg = {
453             tag : 'button',
454             cls : 'roo-button',
455             html: ''
456         };
457         
458         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
459             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
460             this.tag = 'button';
461         } else {
462             cfg.tag = this.tag;
463         }
464         cfg.html = this.html || cfg.html;
465         
466         if (this.toggle===true) {
467             cfg={
468                 tag: 'div',
469                 cls: 'slider-frame roo-button',
470                 cn: [
471                     {
472                         tag: 'span',
473                         'data-on-text':'ON',
474                         'data-off-text':'OFF',
475                         cls: 'slider-button',
476                         html: this.offtext
477                     }
478                 ]
479             };
480             
481             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
482                 cfg.cls += ' '+this.weight;
483             }
484             
485             return cfg;
486         }
487         
488         if (this.isClose) {
489             cfg.cls += ' close';
490             
491             cfg["aria-hidden"] = true;
492             
493             cfg.html = "&times;";
494             
495             return cfg;
496         }
497         
498          
499         if (this.theme==='default') {
500             cfg.cls = 'btn roo-button';
501             
502             if (this.parentType != 'Navbar') {
503                 this.weight = this.weight.length ?  this.weight : 'default';
504             }
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 
507                 cfg.cls += ' btn-' + this.weight;
508             }
509         } else if (this.theme==='glow') {
510             
511             cfg.tag = 'a';
512             cfg.cls = 'btn-glow roo-button';
513             
514             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
515                 
516                 cfg.cls += ' ' + this.weight;
517             }
518         }
519    
520         
521         if (this.inverse) {
522             this.cls += ' inverse';
523         }
524         
525         
526         if (this.active) {
527             cfg.cls += ' active';
528         }
529         
530         if (this.disabled) {
531             cfg.disabled = 'disabled';
532         }
533         
534         if (this.items) {
535             Roo.log('changing to ul' );
536             cfg.tag = 'ul';
537             this.glyphicon = 'caret';
538         }
539         
540         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
541          
542         //gsRoo.log(this.parentType);
543         if (this.parentType === 'Navbar') {
544             cfg.tag = 'li';
545             
546             cfg.cls = '';
547             cfg.cn =  [{
548                 tag : 'a',
549                 cls : 'roo-button',
550                 html : this.html,
551                 href : this.href || '#'
552             }];
553             if (this.menu) {
554                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
555                 cfg.cls += ' dropdown';
556             }   
557             
558             delete cfg.html;
559             
560         } 
561         
562         if (this.glyphicon) {
563             cfg.html = ' ' + cfg.html;
564             
565             cfg.cn = [
566                 {
567                     tag: 'span',
568                     cls: 'glyphicon glyphicon-' + this.glyphicon
569                 }
570             ];
571         }
572         
573         if (this.badge) {
574             cfg.html += ' ';
575             
576             cfg.tag = 'a';
577             
578 //            cfg.cls='btn roo-button';
579             
580             cfg.href=this.href;
581             
582             var value = cfg.html;
583             
584             if(this.glyphicon){
585                 value = {
586                             tag: 'span',
587                             cls: 'glyphicon glyphicon-' + this.glyphicon,
588                             html: this.html
589                         };
590                 
591             }
592             
593             cfg.cn = [
594                 value,
595                 {
596                     tag: 'span',
597                     cls: 'badge',
598                     html: this.badge
599                 }
600             ];
601             
602             cfg.html='';
603         }
604         
605         if (this.menu) {
606             cfg.cls += ' dropdown';
607             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
608         }
609         
610         if (cfg.tag !== 'a' && this.href !== '') {
611             throw "Tag must be a to set href.";
612         } else if (this.href.length > 0) {
613             cfg.href = this.href;
614         }
615         
616         if(this.removeClass){
617             cfg.cls = '';
618         }
619         
620         if(this.target){
621             cfg.target = this.target;
622         }
623         
624         return cfg;
625     },
626     initEvents: function() {
627        // Roo.log('init events?');
628 //        Roo.log(this.el.dom);
629        if (this.el.hasClass('roo-button')) {
630             this.el.on('click', this.onClick, this);
631        } else {
632             this.el.select('.roo-button').on('click', this.onClick, this);
633        }
634        
635        
636         
637     },
638     onClick : function(e)
639     {
640         Roo.log('button on click ');
641         if(this.preventDefault){
642             e.preventDefault();
643         }
644         
645         this.fireEvent('click', this, e);
646     }
647     
648     
649 });
650
651  /*
652  * - LGPL
653  *
654  * column
655  * 
656  */
657
658 /**
659  * @class Roo.bootstrap.Column
660  * @extends Roo.bootstrap.Component
661  * Bootstrap Column class
662  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
663  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
664  * @cfg {Number} md colspan out of 12 for computer-sized screens
665  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
666  * @cfg {String} html content of column.
667  * 
668  * @constructor
669  * Create a new Column
670  * @param {Object} config The config object
671  */
672
673 Roo.bootstrap.Column = function(config){
674     Roo.bootstrap.Column.superclass.constructor.call(this, config);
675 };
676
677 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
678     
679     xs: null,
680     sm: null,
681     md: null,
682     lg: null,
683     html: '',
684     offset: 0,
685     
686     getAutoCreate : function(){
687         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
688         
689         cfg = {
690             tag: 'div',
691             cls: 'column'
692         };
693         
694         var settings=this;
695         ['xs','sm','md','lg'].map(function(size){
696             if (settings[size]) {
697                 cfg.cls += ' col-' + size + '-' + settings[size];
698             }
699         });
700         if (this.html.length) {
701             cfg.html = this.html;
702         }
703         
704         return cfg;
705     }
706    
707 });
708
709  
710
711  /*
712  * - LGPL
713  *
714  * page container.
715  * 
716  */
717
718
719 /**
720  * @class Roo.bootstrap.Container
721  * @extends Roo.bootstrap.Component
722  * Bootstrap Container class
723  * @cfg {Boolean} jumbotron is it a jumbotron element
724  * @cfg {String} html content of element
725  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
726  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
727  * @cfg {String} header content of header (for panel)
728  * @cfg {String} footer content of footer (for panel)
729  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
730  *     
731  * @constructor
732  * Create a new Container
733  * @param {Object} config The config object
734  */
735
736 Roo.bootstrap.Container = function(config){
737     Roo.bootstrap.Container.superclass.constructor.call(this, config);
738 };
739
740 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
741     
742     jumbotron : false,
743     well: '',
744     panel : '',
745     header: '',
746     footer : '',
747     sticky: '',
748   
749      
750     getChildContainer : function() {
751         
752         if(!this.el){
753             return false;
754         }
755         
756         if (this.panel.length) {
757             return this.el.select('.panel-body',true).first();
758         }
759         
760         return this.el;
761     },
762     
763     
764     getAutoCreate : function(){
765         
766         var cfg = {
767             html : '',
768             cls : ''
769         };
770         if (this.jumbotron) {
771             cfg.cls = 'jumbotron';
772         }
773         if (this.cls) {
774             cfg.cls = this.cls + '';
775         }
776         
777         if (this.sticky.length) {
778             
779             var bd = Roo.get(document.body);
780             if (!bd.hasClass('bootstrap-sticky')) {
781                 bd.addClass('bootstrap-sticky');
782                 Roo.select('html',true).setStyle('height', '100%');
783             }
784              
785             cfg.cls += 'bootstrap-sticky-' + this.sticky;
786         }
787         
788         
789         if (this.well.length) {
790             switch (this.well) {
791                 case 'lg':
792                 case 'sm':
793                     cfg.cls +=' well well-' +this.well;
794                     break;
795                 default:
796                     cfg.cls +=' well';
797                     break;
798             }
799         }
800         
801         var body = cfg;
802         
803         if (this.panel.length) {
804             cfg.cls += ' panel panel-' + this.panel;
805             cfg.cn = [];
806             if (this.header.length) {
807                 cfg.cn.push({
808                     
809                     cls : 'panel-heading',
810                     cn : [{
811                         tag: 'h3',
812                         cls : 'panel-title',
813                         html : this.header
814                     }]
815                     
816                 });
817             }
818             body = false;
819             cfg.cn.push({
820                 cls : 'panel-body',
821                 html : this.html
822             });
823             
824             
825             if (this.footer.length) {
826                 cfg.cn.push({
827                     cls : 'panel-footer',
828                     html : this.footer
829                     
830                 });
831             }
832             
833         }
834         if (body) {
835             body.html = this.html || cfg.html;
836         }
837         if (!cfg.cls.length) {
838             cfg.cls =  'container';
839         }
840         
841         return cfg;
842     }
843    
844 });
845
846  /*
847  * - LGPL
848  *
849  * image
850  * 
851  */
852
853
854 /**
855  * @class Roo.bootstrap.Img
856  * @extends Roo.bootstrap.Component
857  * Bootstrap Img class
858  * @cfg {Boolean} imgResponsive false | true
859  * @cfg {String} border rounded | circle | thumbnail
860  * @cfg {String} src image source
861  * @cfg {String} alt image alternative text
862  * @cfg {String} href a tag href
863  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
864  * 
865  * @constructor
866  * Create a new Input
867  * @param {Object} config The config object
868  */
869
870 Roo.bootstrap.Img = function(config){
871     Roo.bootstrap.Img.superclass.constructor.call(this, config);
872     
873     this.addEvents({
874         // img events
875         /**
876          * @event click
877          * The img click event for the img.
878          * @param {Roo.EventObject} e
879          */
880         "click" : true
881     });
882 };
883
884 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
885     
886     imgResponsive: true,
887     border: '',
888     src: '',
889     href: false,
890     target: false,
891
892     getAutoCreate : function(){
893         
894         var cfg = {
895             tag: 'img',
896             cls: 'img-responsive',
897             html : null
898         }
899         
900         cfg.html = this.html || cfg.html;
901         
902         cfg.src = this.src || cfg.src;
903         
904         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
905             cfg.cls += ' img-' + this.border;
906         }
907         
908         if(this.alt){
909             cfg.alt = this.alt;
910         }
911         
912         if(this.href){
913             var a = {
914                 tag: 'a',
915                 href: this.href,
916                 cn: [
917                     cfg
918                 ]
919             }
920             
921             if(this.target){
922                 a.target = this.target;
923             }
924             
925         }
926         
927         
928         return (this.href) ? a : cfg;
929     },
930     
931     initEvents: function() {
932         
933         if(!this.href){
934             this.el.on('click', this.onClick, this);
935         }
936     },
937     
938     onClick : function(e)
939     {
940         Roo.log('img onclick');
941         this.fireEvent('click', this, e);
942     }
943    
944 });
945
946  /*
947  * - LGPL
948  *
949  * header
950  * 
951  */
952
953 /**
954  * @class Roo.bootstrap.Header
955  * @extends Roo.bootstrap.Component
956  * Bootstrap Header class
957  * @cfg {String} html content of header
958  * @cfg {Number} level (1|2|3|4|5|6) default 1
959  * 
960  * @constructor
961  * Create a new Header
962  * @param {Object} config The config object
963  */
964
965
966 Roo.bootstrap.Header  = function(config){
967     Roo.bootstrap.Header.superclass.constructor.call(this, config);
968 };
969
970 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
971     
972     //href : false,
973     html : false,
974     level : 1,
975     
976     
977     
978     getAutoCreate : function(){
979         
980         var cfg = {
981             tag: 'h' + (1 *this.level),
982             html: this.html || 'fill in html'
983         } ;
984         
985         return cfg;
986     }
987    
988 });
989
990  
991
992  /*
993  * - LGPL
994  *
995  * menu
996  * 
997  */
998
999 /**
1000  * @class Roo.bootstrap.Menu
1001  * @extends Roo.bootstrap.Component
1002  * Bootstrap Menu class - container for MenuItems
1003  * @cfg {String} type type of menu
1004  * 
1005  * @constructor
1006  * Create a new Menu
1007  * @param {Object} config The config object
1008  */
1009
1010
1011 Roo.bootstrap.Menu = function(config){
1012     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1013 };
1014
1015 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1016     
1017    /// html : false,
1018     //align : '',
1019     triggerEl : false,
1020     type: false,
1021     
1022     
1023     getChildContainer : function() {
1024         return this.el;  
1025     },
1026     
1027     getAutoCreate : function(){
1028          
1029         //if (['right'].indexOf(this.align)!==-1) {
1030         //    cfg.cn[1].cls += ' pull-right'
1031         //}
1032         var cfg = {
1033             tag : 'ul',
1034             cls : 'dropdown-menu' 
1035             
1036         }
1037         
1038         if (this.type==='submenu') {
1039             cfg.cls='submenu active'
1040         }
1041         
1042         return cfg;
1043     },
1044     initEvents : function() {
1045        // Roo.log("ADD event");
1046        // Roo.log(this.triggerEl.dom);
1047         this.triggerEl.on('click', this.toggle, this);
1048         this.triggerEl.addClass('dropdown-toggle');
1049         
1050     },
1051     toggle  : function(e)
1052     {
1053         //Roo.log(e.getTarget());
1054        // Roo.log(this.triggerEl.dom);
1055         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1056             return;
1057         }
1058         var isActive = this.triggerEl.hasClass('open');
1059         // if disabled.. ingore
1060         this.clearMenus(e);
1061         //if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
1062          // if mobile we use a backdrop because click events don't delegate
1063         // $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
1064         // }
1065  
1066        //var relatedTarget = { relatedTarget: this }
1067        //$parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
1068  
1069        //if (e.isDefaultPrevented()) return;
1070         
1071        this.triggerEl[isActive ? 'removeClass' : 'addClass']('open');
1072        
1073        //  .trigger('shown.bs.dropdown', relatedTarget)
1074  
1075        this.triggerEl.focus();
1076 //       Roo.log(e);
1077        e.preventDefault(); 
1078         
1079         
1080     },
1081     clearMenus : function()
1082     {
1083         //$(backdrop).remove()
1084         Roo.select('.dropdown-toggle',true).each(function(aa) {
1085             if (!aa.hasClass('open')) {
1086                 return;
1087             }
1088             // triger close...
1089             aa.removeClass('open');
1090           //var parent = getParent($(this))
1091           //var relatedTarget = { relatedTarget: this }
1092           
1093            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1094           //if (e.isDefaultPrevented()) return
1095            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1096         })
1097     }
1098     
1099    
1100 });
1101
1102  
1103
1104  /*
1105  * - LGPL
1106  *
1107  * menu item
1108  * 
1109  */
1110
1111
1112 /**
1113  * @class Roo.bootstrap.MenuItem
1114  * @extends Roo.bootstrap.Component
1115  * Bootstrap MenuItem class
1116  * @cfg {String} html the menu label
1117  * @cfg {String} href the link
1118  * @cfg {Boolean} preventDefault (true | false) default true
1119  * 
1120  * 
1121  * @constructor
1122  * Create a new MenuItem
1123  * @param {Object} config The config object
1124  */
1125
1126
1127 Roo.bootstrap.MenuItem = function(config){
1128     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1129     this.addEvents({
1130         // raw events
1131         /**
1132          * @event click
1133          * The raw click event for the entire grid.
1134          * @param {Roo.EventObject} e
1135          */
1136         "click" : true
1137     });
1138 };
1139
1140 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1141     
1142     href : false,
1143     html : false,
1144     preventDefault: true,
1145     
1146     getAutoCreate : function(){
1147         var cfg= {
1148             tag: 'li',
1149             cn: [
1150                 {
1151                     tag : 'a',
1152                     href : '#',
1153                     html : 'Link'
1154                 }
1155             ]
1156         };
1157         
1158         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1159         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1160         return cfg;
1161     },
1162     
1163     initEvents: function() {
1164         
1165         this.el.on('click', this.onClick, this);
1166         
1167     },
1168     onClick : function(e)
1169     {
1170         Roo.log('item on click ');
1171         if(this.preventDefault){
1172             e.preventDefault();
1173         }
1174         
1175         this.fireEvent('click', this, e);
1176     }
1177    
1178 });
1179
1180  
1181
1182  /*
1183  * - LGPL
1184  *
1185  * menu separator
1186  * 
1187  */
1188
1189
1190 /**
1191  * @class Roo.bootstrap.MenuSeparator
1192  * @extends Roo.bootstrap.Component
1193  * Bootstrap MenuSeparator class
1194  * 
1195  * @constructor
1196  * Create a new MenuItem
1197  * @param {Object} config The config object
1198  */
1199
1200
1201 Roo.bootstrap.MenuSeparator = function(config){
1202     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1203 };
1204
1205 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1206     
1207     getAutoCreate : function(){
1208         var cfg = {
1209             cls: 'divider',
1210             tag : 'li'
1211         };
1212         
1213         return cfg;
1214     }
1215    
1216 });
1217
1218  
1219
1220  
1221 /*
1222 <div class="modal fade">
1223   <div class="modal-dialog">
1224     <div class="modal-content">
1225       <div class="modal-header">
1226         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1227         <h4 class="modal-title">Modal title</h4>
1228       </div>
1229       <div class="modal-body">
1230         <p>One fine body&hellip;</p>
1231       </div>
1232       <div class="modal-footer">
1233         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1234         <button type="button" class="btn btn-primary">Save changes</button>
1235       </div>
1236     </div><!-- /.modal-content -->
1237   </div><!-- /.modal-dialog -->
1238 </div><!-- /.modal -->
1239 */
1240 /*
1241  * - LGPL
1242  *
1243  * page contgainer.
1244  * 
1245  */
1246
1247 /**
1248  * @class Roo.bootstrap.Modal
1249  * @extends Roo.bootstrap.Component
1250  * Bootstrap Modal class
1251  * @cfg {String} title Title of dialog
1252  * @cfg {Array} buttons Array of buttons or standard button set..
1253  * 
1254  * @constructor
1255  * Create a new Modal Dialog
1256  * @param {Object} config The config object
1257  */
1258
1259 Roo.bootstrap.Modal = function(config){
1260     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1261     this.addEvents({
1262         // raw events
1263         /**
1264          * @event btnclick
1265          * The raw btnclick event for the button
1266          * @param {Roo.EventObject} e
1267          */
1268         "btnclick" : true
1269     });
1270 };
1271
1272 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1273     
1274     title : 'test dialog',
1275    
1276     buttons : false,
1277     
1278     onRender : function(ct, position)
1279     {
1280         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1281      
1282         if(!this.el){
1283             var cfg = Roo.apply({},  this.getAutoCreate());
1284             cfg.id = Roo.id();
1285             //if(!cfg.name){
1286             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1287             //}
1288             //if (!cfg.name.length) {
1289             //    delete cfg.name;
1290            // }
1291             if (this.cls) {
1292                 cfg.cls += ' ' + this.cls;
1293             }
1294             if (this.style) {
1295                 cfg.style = this.style;
1296             }
1297             this.el = Roo.get(document.body).createChild(cfg, position);
1298         }
1299         //var type = this.el.dom.type;
1300         
1301         if(this.tabIndex !== undefined){
1302             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1303         }
1304         
1305         
1306         
1307         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1308         this.maskEl.enableDisplayMode("block");
1309         this.maskEl.hide();
1310         //this.el.addClass("x-dlg-modal");
1311     
1312         if (this.buttons) {
1313             Roo.each(this.buttons, function(bb) {
1314                 b = Roo.apply({}, bb);
1315                 b.xns = b.xns || Roo.bootstrap;
1316                 b.xtype = b.xtype || 'Button';
1317                 if (typeof(b.listeners) == 'undefined') {
1318                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1319                 }
1320                 
1321                 var btn = Roo.factory(b);
1322                 
1323                 btn.onRender(this.el.select('.modal-footer').first());
1324                 
1325             },this);
1326         }
1327         // render the children.
1328         var nitems = [];
1329         
1330         if(typeof(this.items) != 'undefined'){
1331             var items = this.items;
1332             delete this.items;
1333
1334             for(var i =0;i < items.length;i++) {
1335                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1336             }
1337         }
1338         
1339         this.items = nitems;
1340         this.initEvents();
1341         //this.el.addClass([this.fieldClass, this.cls]);
1342         
1343     },
1344     getAutoCreate : function(){
1345         
1346         
1347         var bdy = {
1348                 cls : 'modal-body',
1349                 html : this.html || ''
1350         };
1351         
1352          
1353         return modal = {
1354             cls: "modal fade",
1355             cn : [
1356                 {
1357                     cls: "modal-dialog",
1358                     cn : [
1359                         {
1360                             cls : "modal-content",
1361                             cn : [
1362                                 {
1363                                     cls : 'modal-header',
1364                                     cn : [
1365                                         {
1366                                             tag: 'button',
1367                                             cls : 'close',
1368                                             html : '&times'
1369                                         },
1370                                         {
1371                                             tag: 'h4',
1372                                             cls : 'modal-title',
1373                                             html : this.title
1374                                         }
1375                                     
1376                                     ]
1377                                 },
1378                                 bdy,
1379                                 {
1380                                     cls : 'modal-footer' 
1381                                 }
1382                                 
1383                                 
1384                             ]
1385                             
1386                         }
1387                     ]
1388                         
1389                 }
1390             ]
1391             
1392             
1393         };
1394           
1395     },
1396     getChildContainer : function() {
1397          
1398          return this.el.select('.modal-body',true).first();
1399         
1400     },
1401     getButtonContainer : function() {
1402          return this.el.select('.modal-footer',true).first();
1403         
1404     },
1405     initEvents : function()
1406     {
1407         this.el.select('.modal-header .close').on('click', this.hide, this);
1408 //        
1409 //        this.addxtype(this);
1410     },
1411     show : function() {
1412         
1413         if (!this.rendered) {
1414             this.render();
1415         }
1416        
1417         this.el.addClass('on');
1418         this.el.removeClass('fade');
1419         this.el.setStyle('display', 'block');
1420         Roo.get(document.body).addClass("x-body-masked");
1421         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1422         this.maskEl.show();
1423         this.el.setStyle('zIndex', '10001');
1424         this.fireEvent('show', this);
1425         
1426         
1427     },
1428     hide : function()
1429     {
1430         Roo.log('Modal hide?!');
1431         this.maskEl.hide();
1432         Roo.get(document.body).removeClass("x-body-masked");
1433         this.el.removeClass('on');
1434         this.el.addClass('fade');
1435         this.el.setStyle('display', 'none');
1436         this.fireEvent('hide', this);
1437     },
1438     onButtonClick: function(btn,e)
1439     {
1440         //Roo.log([a,b,c]);
1441         this.fireEvent('btnclick', btn.name, e);
1442     }
1443 });
1444
1445
1446 Roo.apply(Roo.bootstrap.Modal,  {
1447     /**
1448          * Button config that displays a single OK button
1449          * @type Object
1450          */
1451         OK :  [{
1452             name : 'ok',
1453             weight : 'primary',
1454             html : 'OK'
1455         }], 
1456         /**
1457          * Button config that displays Yes and No buttons
1458          * @type Object
1459          */
1460         YESNO : [
1461             {
1462                 name  : 'no',
1463                 html : 'No'
1464             },
1465             {
1466                 name  :'yes',
1467                 weight : 'primary',
1468                 html : 'Yes'
1469             }
1470         ],
1471         
1472         /**
1473          * Button config that displays OK and Cancel buttons
1474          * @type Object
1475          */
1476         OKCANCEL : [
1477             {
1478                name : 'cancel',
1479                 html : 'Cancel'
1480             },
1481             {
1482                 name : 'ok',
1483                 weight : 'primary',
1484                 html : 'OK'
1485             }
1486         ],
1487         /**
1488          * Button config that displays Yes, No and Cancel buttons
1489          * @type Object
1490          */
1491         YESNOCANCEL : [
1492             {
1493                 name : 'yes',
1494                 weight : 'primary',
1495                 html : 'Yes'
1496             },
1497             {
1498                 name : 'no',
1499                 html : 'No'
1500             },
1501             {
1502                 name : 'cancel',
1503                 html : 'Cancel'
1504             }
1505         ]
1506 });
1507  /*
1508  * - LGPL
1509  *
1510  * navbar
1511  * 
1512  */
1513
1514 /**
1515  * @class Roo.bootstrap.Navbar
1516  * @extends Roo.bootstrap.Component
1517  * Bootstrap Navbar class
1518  * @cfg {Boolean} sidebar has side bar
1519  * @cfg {Boolean} bar is a bar?
1520  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
1521  * @cfg {String} brand what is brand
1522  * @cfg {Boolean} inverse is inverted color
1523  * @cfg {String} type (nav | pills | tabs)
1524  * @cfg {Boolean} arrangement stacked | justified
1525  * @cfg {String} align (left | right) alignment
1526  * @cfg {String} brand_href href of the brand
1527  * @cfg {Boolean} main (true|false) main nav bar? default false
1528  *
1529  * 
1530  * @constructor
1531  * Create a new Navbar
1532  * @param {Object} config The config object
1533  */
1534
1535
1536 Roo.bootstrap.Navbar = function(config){
1537     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
1538 };
1539
1540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
1541     
1542     sidebar: false,
1543     
1544     bar: false,
1545     brand: '',
1546     inverse: false,
1547     position: '',
1548     align : false,
1549     type: 'nav',
1550     arrangement: '',
1551     brand_href: false,
1552     main : false,
1553     
1554     getAutoCreate : function(){
1555         var cfg = {
1556             cls : 'navbar'
1557         };
1558         
1559         if (this.sidebar === true) {
1560             cfg = {
1561                 tag: 'div',
1562                 cls: 'sidebar-nav'
1563             };
1564             return cfg;
1565         }
1566         
1567         if (this.bar === true) {
1568             cfg = {
1569                 tag: 'nav',
1570                 cls: 'navbar',
1571                 role: 'navigation',
1572                 cn: [
1573                     {
1574                         tag: 'div',
1575                         cls: 'navbar-header',
1576                         cn: [
1577                             {
1578                             tag: 'button',
1579                             type: 'button',
1580                             cls: 'navbar-toggle',
1581                             'data-toggle': 'collapse',
1582                             cn: [
1583                                 {
1584                                     tag: 'span',
1585                                     cls: 'sr-only',
1586                                     html: 'Toggle navigation'
1587                                 },
1588                                 {
1589                                     tag: 'span',
1590                                     cls: 'icon-bar'
1591                                 },
1592                                 {
1593                                     tag: 'span',
1594                                     cls: 'icon-bar'
1595                                 },
1596                                 {
1597                                     tag: 'span',
1598                                     cls: 'icon-bar'
1599                                 }
1600                             ]
1601                             }
1602                         ]
1603                     },
1604                     {
1605                     tag: 'div',
1606                     cls: 'collapse navbar-collapse'
1607                     }
1608                 ]
1609             };
1610             
1611             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
1612             
1613             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
1614                 cfg.cls += ' navbar-' + this.position;
1615                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
1616             }
1617             
1618             if (this.brand !== '') {
1619                 cfg.cn[0].cn.push({
1620                     tag: 'a',
1621                     href: this.brand_href ? this.brand_href : '#',
1622                     cls: 'navbar-brand',
1623                     cn: [
1624                     this.brand
1625                     ]
1626                 });
1627             }
1628             
1629             if(this.main){
1630                 cfg.cls += ' main-nav';
1631             }
1632             
1633             
1634             return cfg;
1635         
1636         } else if (this.bar === false) {
1637             
1638         } else {
1639             Roo.log('Property \'bar\' in of Navbar must be either true or false')
1640         }
1641         
1642         cfg.cn = [
1643             {
1644                 cls: 'nav',
1645                 tag : 'ul'
1646             }
1647         ];
1648         
1649         if (['tabs','pills'].indexOf(this.type)!==-1) {
1650             cfg.cn[0].cls += ' nav-' + this.type
1651         } else {
1652             if (this.type!=='nav') {
1653             Roo.log('nav type must be nav/tabs/pills')
1654             }
1655             cfg.cn[0].cls += ' navbar-nav'
1656         }
1657         
1658         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
1659             cfg.cn[0].cls += ' nav-' + this.arrangement;
1660         }
1661         
1662         if (this.align === 'right') {
1663             cfg.cn[0].cls += ' navbar-right';
1664         }
1665         if (this.inverse) {
1666             cfg.cls += ' navbar-inverse';
1667             
1668         }
1669         
1670         
1671         return cfg;
1672     },
1673     
1674     initEvents :function ()
1675     {
1676         //Roo.log(this.el.select('.navbar-toggle',true));
1677         this.el.select('.navbar-toggle',true).on('click', function() {
1678            // Roo.log('click');
1679             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
1680         }, this);
1681     },
1682     
1683     
1684     getChildContainer : function()
1685     {
1686         if (this.bar === true) {
1687             return this.el.select('.collapse',true).first();
1688         }
1689         console.log(this);
1690         return this.el;
1691     }
1692    
1693 });
1694
1695  
1696
1697  /*
1698  * - LGPL
1699  *
1700  * nav group
1701  * 
1702  */
1703
1704 /**
1705  * @class Roo.bootstrap.NavGroup
1706  * @extends Roo.bootstrap.Component
1707  * Bootstrap NavGroup class
1708  * @cfg {String} align left | right
1709  * @cfg {Boolean} inverse false | true
1710  * @cfg {String} type (nav|pills|tab) default nav
1711  * 
1712  * @constructor
1713  * Create a new nav group
1714  * @param {Object} config The config object
1715  */
1716
1717 Roo.bootstrap.NavGroup = function(config){
1718     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
1719 };
1720
1721 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
1722     
1723     align: '',
1724     inverse: false,
1725     form: false,
1726     type: 'nav',
1727     
1728     getAutoCreate : function(){
1729         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
1730         
1731         cfg = {
1732             tag : 'ul',
1733             cls: 'nav' 
1734         }
1735         
1736         if (['tabs','pills'].indexOf(this.type)!==-1) {
1737             cfg.cls += ' nav-' + this.type
1738         } else {
1739             if (this.type!=='nav') {
1740                 Roo.log('nav type must be nav/tabs/pills')
1741             }
1742             cfg.cls += ' navbar-nav'
1743         }
1744         
1745         if (this.parent().sidebar === true) {
1746             cfg = {
1747                 tag: 'ul',
1748                 cls: 'dashboard-menu'
1749             }
1750             
1751             return cfg;
1752         }
1753         
1754         if (this.form === true) {
1755             cfg = {
1756                 tag: 'form',
1757                 cls: 'navbar-form'
1758             }
1759             
1760             if (this.align === 'right') {
1761                 cfg.cls += ' navbar-right';
1762             } else {
1763                 cfg.cls += ' navbar-left';
1764             }
1765         }
1766         
1767         if (this.align === 'right') {
1768             cfg.cls += ' navbar-right';
1769         }
1770         
1771         if (this.inverse) {
1772             cfg.cls += ' navbar-inverse';
1773             
1774         }
1775         
1776         
1777         return cfg;
1778     }
1779    
1780 });
1781
1782  
1783
1784  /*
1785  * - LGPL
1786  *
1787  * row
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Navbar.Item
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Navbar.Button class
1795  * @cfg {String} href  link to
1796  * @cfg {String} html content of button
1797  * @cfg {String} badge text inside badge
1798  * @cfg {String} glyphicon name of glyphicon
1799  * @cfg {String} icon name of font awesome icon
1800  * @cfg {Boolena} active Is item active
1801  * @cfg {Boolean} preventDefault (true | false) default false
1802   
1803  * @constructor
1804  * Create a new Navbar Button
1805  * @param {Object} config The config object
1806  */
1807 Roo.bootstrap.Navbar.Item = function(config){
1808     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
1809     this.addEvents({
1810         // raw events
1811         /**
1812          * @event click
1813          * The raw click event for the entire grid.
1814          * @param {Roo.EventObject} e
1815          */
1816         "click" : true
1817     });
1818 };
1819
1820 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
1821     
1822     href: false,
1823     html: '',
1824     badge: '',
1825     icon: false,
1826     glyphicon: false,
1827     icon: false,
1828     active: false,
1829     preventDefault : false,
1830     
1831     getAutoCreate : function(){
1832         
1833         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
1834         
1835         if (this.parent().parent().sidebar === true) {
1836             cfg = {
1837                 tag: 'li',
1838                 cls: '',
1839                 cn: [
1840                     {
1841                         tag: 'p',
1842                         cls: ''
1843                     }
1844                 ]
1845             }
1846             
1847             if (this.html) {
1848                 cfg.cn[0].html = this.html;
1849             }
1850             
1851             if (this.active) {
1852                 this.cls += ' active';
1853             }
1854             
1855             if (this.menu) {
1856                 cfg.cn[0].cls += ' dropdown-toggle';
1857                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
1858             }
1859             
1860             if (this.href) {
1861                 cfg.cn[0].tag = 'a',
1862                 cfg.cn[0].href = this.href;
1863             }
1864             
1865             if (this.glyphicon) {
1866                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
1867             }
1868             
1869             if (this.icon) {
1870                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
1871             }
1872             
1873             return cfg;
1874         }
1875         
1876         cfg = {
1877             tag: 'li',
1878             cls: 'nav-item'
1879         }
1880         
1881         if (this.active) {
1882             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
1883         }
1884             
1885         cfg.cn = [
1886             {
1887                 tag: 'p',
1888                 html: 'Text'
1889             }
1890         ];
1891         
1892         if (this.glyphicon) {
1893             if(cfg.html){cfg.html = ' ' + this.html};
1894             cfg.cn=[
1895                 {
1896                     tag: 'span',
1897                     cls: 'glyphicon glyphicon-' + this.glyphicon
1898                 }
1899             ];
1900         }
1901         
1902         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1903         
1904         if (this.menu) {
1905             cfg.cn[0].tag='a';
1906             cfg.cn[0].href='#';
1907             cfg.cn[0].html += " <span class='caret'></span>";
1908         //}else if (!this.href) {
1909         //    cfg.cn[0].tag='p';
1910         //    cfg.cn[0].cls='navbar-text';
1911         } else {
1912             cfg.cn[0].tag='a';
1913             cfg.cn[0].href=this.href||'#';
1914             cfg.cn[0].html=this.html;
1915         }
1916         
1917         if (this.badge !== '') {
1918             
1919             cfg.cn[0].cn=[
1920                 cfg.cn[0].html + ' ',
1921                 {
1922                     tag: 'span',
1923                     cls: 'badge',
1924                     html: this.badge
1925                 }
1926             ];
1927             cfg.cn[0].html=''
1928         }
1929          
1930         if (this.icon) {
1931             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
1932         }
1933         
1934         return cfg;
1935     },
1936     initEvents: function() {
1937        // Roo.log('init events?');
1938        // Roo.log(this.el.dom);
1939         this.el.select('a',true).on('click', this.onClick, this);
1940     },
1941     
1942     onClick : function(e)
1943     {
1944         if(this.preventDefault){
1945             e.preventDefault();
1946         }
1947         
1948         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
1949             this.onTabsClick(e);
1950         } 
1951         
1952         this.fireEvent('click', this, e);
1953     },
1954     
1955     onTabsClick : function(e)
1956     {
1957         Roo.each(this.parent().el.select('.active',true).elements, function(v){
1958             v.removeClass('active');
1959         })
1960
1961         this.el.addClass('active');
1962
1963         if(this.href && this.href.substring(0,1) == '#'){
1964             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
1965
1966             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
1967                 v.removeClass('active');
1968             });
1969
1970             tab.addClass('active');
1971         }
1972     }
1973    
1974 });
1975  
1976
1977  /*
1978  * - LGPL
1979  *
1980  * row
1981  * 
1982  */
1983
1984 /**
1985  * @class Roo.bootstrap.Row
1986  * @extends Roo.bootstrap.Component
1987  * Bootstrap Row class (contains columns...)
1988  * 
1989  * @constructor
1990  * Create a new Row
1991  * @param {Object} config The config object
1992  */
1993
1994 Roo.bootstrap.Row = function(config){
1995     Roo.bootstrap.Row.superclass.constructor.call(this, config);
1996 };
1997
1998 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
1999     
2000     getAutoCreate : function(){
2001        return {
2002             cls: 'row clearfix'
2003        };
2004     }
2005     
2006     
2007 });
2008
2009  
2010
2011  /*
2012  * - LGPL
2013  *
2014  * element
2015  * 
2016  */
2017
2018 /**
2019  * @class Roo.bootstrap.Element
2020  * @extends Roo.bootstrap.Component
2021  * Bootstrap Element class
2022  * @cfg {String} html contents of the element
2023  * @cfg {String} tag tag of the element
2024  * @cfg {String} cls class of the element
2025  * 
2026  * @constructor
2027  * Create a new Element
2028  * @param {Object} config The config object
2029  */
2030
2031 Roo.bootstrap.Element = function(config){
2032     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2033 };
2034
2035 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2036     
2037     tag: 'div',
2038     cls: '',
2039     html: '',
2040      
2041     
2042     getAutoCreate : function(){
2043         
2044         var cfg = {
2045             tag: this.tag,
2046             cls: this.cls,
2047             html: this.html
2048         }
2049         
2050         
2051         
2052         return cfg;
2053     }
2054    
2055 });
2056
2057  
2058
2059  /*
2060  * - LGPL
2061  *
2062  * pagination
2063  * 
2064  */
2065
2066 /**
2067  * @class Roo.bootstrap.Pagination
2068  * @extends Roo.bootstrap.Component
2069  * Bootstrap Pagination class
2070  * @cfg {String} size xs | sm | md | lg
2071  * @cfg {Boolean} inverse false | true
2072  * 
2073  * @constructor
2074  * Create a new Pagination
2075  * @param {Object} config The config object
2076  */
2077
2078 Roo.bootstrap.Pagination = function(config){
2079     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2080 };
2081
2082 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2083     
2084     cls: false,
2085     size: false,
2086     inverse: false,
2087     
2088     getAutoCreate : function(){
2089         var cfg = {
2090             tag: 'ul',
2091                 cls: 'pagination'
2092         };
2093         if (this.inverse) {
2094             cfg.cls += ' inverse';
2095         }
2096         if (this.html) {
2097             cfg.html=this.html;
2098         }
2099         if (this.cls) {
2100             cfg.cls += " " + this.cls;
2101         }
2102         return cfg;
2103     }
2104    
2105 });
2106
2107  
2108
2109  /*
2110  * - LGPL
2111  *
2112  * Pagination item
2113  * 
2114  */
2115
2116
2117 /**
2118  * @class Roo.bootstrap.PaginationItem
2119  * @extends Roo.bootstrap.Component
2120  * Bootstrap PaginationItem class
2121  * @cfg {String} html text
2122  * @cfg {String} href the link
2123  * @cfg {Boolean} preventDefault (true | false) default true
2124  * @cfg {Boolean} active (true | false) default false
2125  * 
2126  * 
2127  * @constructor
2128  * Create a new PaginationItem
2129  * @param {Object} config The config object
2130  */
2131
2132
2133 Roo.bootstrap.PaginationItem = function(config){
2134     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2135     this.addEvents({
2136         // raw events
2137         /**
2138          * @event click
2139          * The raw click event for the entire grid.
2140          * @param {Roo.EventObject} e
2141          */
2142         "click" : true
2143     });
2144 };
2145
2146 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2147     
2148     href : false,
2149     html : false,
2150     preventDefault: true,
2151     active : false,
2152     cls : false,
2153     
2154     getAutoCreate : function(){
2155         var cfg= {
2156             tag: 'li',
2157             cn: [
2158                 {
2159                     tag : 'a',
2160                     href : this.href ? this.href : '#',
2161                     html : this.html ? this.html : ''
2162                 }
2163             ]
2164         };
2165         
2166         if(this.cls){
2167             cfg.cls = this.cls;
2168         }
2169         
2170         if(this.active){
2171             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2172         }
2173         
2174         return cfg;
2175     },
2176     
2177     initEvents: function() {
2178         
2179         this.el.on('click', this.onClick, this);
2180         
2181     },
2182     onClick : function(e)
2183     {
2184         Roo.log('PaginationItem on click ');
2185         if(this.preventDefault){
2186             e.preventDefault();
2187         }
2188         
2189         this.fireEvent('click', this, e);
2190     }
2191    
2192 });
2193
2194  
2195
2196  /*
2197  * - LGPL
2198  *
2199  * slider
2200  * 
2201  */
2202
2203
2204 /**
2205  * @class Roo.bootstrap.Slider
2206  * @extends Roo.bootstrap.Component
2207  * Bootstrap Slider class
2208  *    
2209  * @constructor
2210  * Create a new Slider
2211  * @param {Object} config The config object
2212  */
2213
2214 Roo.bootstrap.Slider = function(config){
2215     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2216 };
2217
2218 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2219     
2220     getAutoCreate : function(){
2221         
2222         var cfg = {
2223             tag: 'div',
2224             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2225             cn: [
2226                 {
2227                     tag: 'a',
2228                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2229                 }
2230             ]
2231         }
2232         
2233         return cfg;
2234     }
2235    
2236 });
2237
2238  /*
2239  * - LGPL
2240  *
2241  * table
2242  * 
2243  */
2244
2245 /**
2246  * @class Roo.bootstrap.Table
2247  * @extends Roo.bootstrap.Component
2248  * Bootstrap Table class
2249  * @cfg {String} cls table class
2250  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2251  * @cfg {String} bgcolor Specifies the background color for a table
2252  * @cfg {Number} border Specifies whether the table cells should have borders or not
2253  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2254  * @cfg {Number} cellspacing Specifies the space between cells
2255  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2256  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2257  * @cfg {String} sortable Specifies that the table should be sortable
2258  * @cfg {String} summary Specifies a summary of the content of a table
2259  * @cfg {Number} width Specifies the width of a table
2260  * 
2261  * @cfg {boolean} striped Should the rows be alternative striped
2262  * @cfg {boolean} bordered Add borders to the table
2263  * @cfg {boolean} hover Add hover highlighting
2264  * @cfg {boolean} condensed Format condensed
2265  * @cfg {boolean} responsive Format condensed
2266  *
2267  
2268  
2269  * 
2270  * @constructor
2271  * Create a new Table
2272  * @param {Object} config The config object
2273  */
2274
2275 Roo.bootstrap.Table = function(config){
2276     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2277     
2278     if (this.sm) {
2279         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2280         this.sm = this.selModel;
2281         this.sm.xmodule = this.xmodule || false;
2282     }
2283     if (this.cm && typeof(this.cm.config) == 'undefined') {
2284         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2285         this.cm = this.colModel;
2286         this.cm.xmodule = this.xmodule || false;
2287     }
2288     if (this.store) {
2289         this.store= Roo.factory(this.store, Roo.data);
2290         this.ds = this.store;
2291         this.ds.xmodule = this.xmodule || false;
2292          
2293     }
2294 };
2295
2296 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2297     
2298     cls: false,
2299     align: false,
2300     bgcolor: false,
2301     border: false,
2302     cellpadding: false,
2303     cellspacing: false,
2304     frame: false,
2305     rules: false,
2306     sortable: false,
2307     summary: false,
2308     width: false,
2309     striped : false,
2310     bordered: false,
2311     hover:  false,
2312     condensed : false,
2313     responsive : false,
2314     sm : false,
2315     cm : false,
2316     store : false,
2317     
2318     getAutoCreate : function(){
2319         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2320         
2321         cfg = {
2322             tag: 'table',
2323             cls : 'table'
2324         }
2325             
2326         if (this.striped) {
2327             cfg.cls += ' table-striped';
2328         }
2329         if (this.hover) {
2330             cfg.cls += ' table-hover';
2331         }
2332         if (this.bordered) {
2333             cfg.cls += ' table-bordered';
2334         }
2335         if (this.condensed) {
2336             cfg.cls += ' table-condensed';
2337         }
2338         if (this.responsive) {
2339             cfg.cls += ' table-responsive';
2340         }
2341         
2342           
2343         
2344         
2345         if (this.cls) {
2346             cfg.cls+=  ' ' +this.cls;
2347         }
2348         
2349         // this lot should be simplifed...
2350         
2351         if (this.align) {
2352             cfg.align=this.align;
2353         }
2354         if (this.bgcolor) {
2355             cfg.bgcolor=this.bgcolor;
2356         }
2357         if (this.border) {
2358             cfg.border=this.border;
2359         }
2360         if (this.cellpadding) {
2361             cfg.cellpadding=this.cellpadding;
2362         }
2363         if (this.cellspacing) {
2364             cfg.cellspacing=this.cellspacing;
2365         }
2366         if (this.frame) {
2367             cfg.frame=this.frame;
2368         }
2369         if (this.rules) {
2370             cfg.rules=this.rules;
2371         }
2372         if (this.sortable) {
2373             cfg.sortable=this.sortable;
2374         }
2375         if (this.summary) {
2376             cfg.summary=this.summary;
2377         }
2378         if (this.width) {
2379             cfg.width=this.width;
2380         }
2381         
2382         if(this.store && this.cm && this.sm){
2383             cfg.cn = this.initTableGrid();
2384         }
2385         
2386         return cfg;
2387     },
2388     
2389     initTableGrid : function()
2390     {
2391         var cfg = {};
2392         
2393 //        cfg.push({
2394 //            tag: 'thead',
2395 //            
2396 //        })
2397     },
2398     
2399     initEvents : function()
2400     {   
2401         if(!this.store && !this.cm && !this.sm){
2402             return;
2403         }
2404         
2405         Roo.log('initEvents!!!!');
2406         Roo.log(this.sm);
2407         
2408         var cm = this.cm;
2409         var colCount = cm.getColumnCount();
2410         
2411         var header = this.renderHeaders();
2412         
2413     },
2414     
2415     renderHeaders : function()
2416     {
2417         
2418     }
2419    
2420 });
2421
2422  
2423
2424  /*
2425  * - LGPL
2426  *
2427  * table cell
2428  * 
2429  */
2430
2431 /**
2432  * @class Roo.bootstrap.TableCell
2433  * @extends Roo.bootstrap.Component
2434  * Bootstrap TableCell class
2435  * @cfg {String} html cell contain text
2436  * @cfg {String} cls cell class
2437  * @cfg {String} tag cell tag (td|th) default td
2438  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
2439  * @cfg {String} align Aligns the content in a cell
2440  * @cfg {String} axis Categorizes cells
2441  * @cfg {String} bgcolor Specifies the background color of a cell
2442  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
2443  * @cfg {Number} colspan Specifies the number of columns a cell should span
2444  * @cfg {String} headers Specifies one or more header cells a cell is related to
2445  * @cfg {Number} height Sets the height of a cell
2446  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
2447  * @cfg {Number} rowspan Sets the number of rows a cell should span
2448  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
2449  * @cfg {String} valign Vertical aligns the content in a cell
2450  * @cfg {Number} width Specifies the width of a cell
2451  * 
2452  * @constructor
2453  * Create a new TableCell
2454  * @param {Object} config The config object
2455  */
2456
2457 Roo.bootstrap.TableCell = function(config){
2458     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
2459 };
2460
2461 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
2462     
2463     html: false,
2464     cls: false,
2465     tag: false,
2466     abbr: false,
2467     align: false,
2468     axis: false,
2469     bgcolor: false,
2470     charoff: false,
2471     colspan: false,
2472     headers: false,
2473     height: false,
2474     nowrap: false,
2475     rowspan: false,
2476     scope: false,
2477     valign: false,
2478     width: false,
2479     
2480     
2481     getAutoCreate : function(){
2482         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
2483         
2484         cfg = {
2485             tag: 'td'
2486         }
2487         
2488         if(this.tag){
2489             cfg.tag = this.tag;
2490         }
2491         
2492         if (this.html) {
2493             cfg.html=this.html
2494         }
2495         if (this.cls) {
2496             cfg.cls=this.cls
2497         }
2498         if (this.abbr) {
2499             cfg.abbr=this.abbr
2500         }
2501         if (this.align) {
2502             cfg.align=this.align
2503         }
2504         if (this.axis) {
2505             cfg.axis=this.axis
2506         }
2507         if (this.bgcolor) {
2508             cfg.bgcolor=this.bgcolor
2509         }
2510         if (this.charoff) {
2511             cfg.charoff=this.charoff
2512         }
2513         if (this.colspan) {
2514             cfg.colspan=this.colspan
2515         }
2516         if (this.headers) {
2517             cfg.headers=this.headers
2518         }
2519         if (this.height) {
2520             cfg.height=this.height
2521         }
2522         if (this.nowrap) {
2523             cfg.nowrap=this.nowrap
2524         }
2525         if (this.rowspan) {
2526             cfg.rowspan=this.rowspan
2527         }
2528         if (this.scope) {
2529             cfg.scope=this.scope
2530         }
2531         if (this.valign) {
2532             cfg.valign=this.valign
2533         }
2534         if (this.width) {
2535             cfg.width=this.width
2536         }
2537         
2538         
2539         return cfg;
2540     }
2541    
2542 });
2543
2544  
2545
2546  /*
2547  * - LGPL
2548  *
2549  * table row
2550  * 
2551  */
2552
2553 /**
2554  * @class Roo.bootstrap.TableRow
2555  * @extends Roo.bootstrap.Component
2556  * Bootstrap TableRow class
2557  * @cfg {String} cls row class
2558  * @cfg {String} align Aligns the content in a table row
2559  * @cfg {String} bgcolor Specifies a background color for a table row
2560  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
2561  * @cfg {String} valign Vertical aligns the content in a table row
2562  * 
2563  * @constructor
2564  * Create a new TableRow
2565  * @param {Object} config The config object
2566  */
2567
2568 Roo.bootstrap.TableRow = function(config){
2569     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
2570 };
2571
2572 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
2573     
2574     cls: false,
2575     align: false,
2576     bgcolor: false,
2577     charoff: false,
2578     valign: false,
2579     
2580     getAutoCreate : function(){
2581         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
2582         
2583         cfg = {
2584             tag: 'tr'
2585         }
2586             
2587         if(this.cls){
2588             cfg.cls = this.cls;
2589         }
2590         if(this.align){
2591             cfg.align = this.align;
2592         }
2593         if(this.bgcolor){
2594             cfg.bgcolor = this.bgcolor;
2595         }
2596         if(this.charoff){
2597             cfg.charoff = this.charoff;
2598         }
2599         if(this.valign){
2600             cfg.valign = this.valign;
2601         }
2602         
2603         return cfg;
2604     }
2605    
2606 });
2607
2608  
2609
2610  /*
2611  * - LGPL
2612  *
2613  * table body
2614  * 
2615  */
2616
2617 /**
2618  * @class Roo.bootstrap.TableBody
2619  * @extends Roo.bootstrap.Component
2620  * Bootstrap TableBody class
2621  * @cfg {String} cls element class
2622  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
2623  * @cfg {String} align Aligns the content inside the element
2624  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
2625  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
2626  * 
2627  * @constructor
2628  * Create a new TableBody
2629  * @param {Object} config The config object
2630  */
2631
2632 Roo.bootstrap.TableBody = function(config){
2633     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
2634 };
2635
2636 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
2637     
2638     cls: false,
2639     tag: false,
2640     align: false,
2641     charoff: false,
2642     valign: false,
2643     
2644     getAutoCreate : function(){
2645         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
2646         
2647         cfg = {
2648             tag: 'tbody'
2649         }
2650             
2651         if (this.cls) {
2652             cfg.cls=this.cls
2653         }
2654         if(this.tag){
2655             cfg.tag = this.tag;
2656         }
2657         
2658         if(this.align){
2659             cfg.align = this.align;
2660         }
2661         if(this.charoff){
2662             cfg.charoff = this.charoff;
2663         }
2664         if(this.valign){
2665             cfg.valign = this.valign;
2666         }
2667         
2668         return cfg;
2669     }
2670     
2671     
2672 //    initEvents : function()
2673 //    {
2674 //        
2675 //        if(!this.store){
2676 //            return;
2677 //        }
2678 //        
2679 //        this.store = Roo.factory(this.store, Roo.data);
2680 //        this.store.on('load', this.onLoad, this);
2681 //        
2682 //        this.store.load();
2683 //        
2684 //    },
2685 //    
2686 //    onLoad: function () 
2687 //    {   
2688 //        this.fireEvent('load', this);
2689 //    }
2690 //    
2691 //   
2692 });
2693
2694  
2695
2696  /*
2697  * Based on:
2698  * Ext JS Library 1.1.1
2699  * Copyright(c) 2006-2007, Ext JS, LLC.
2700  *
2701  * Originally Released Under LGPL - original licence link has changed is not relivant.
2702  *
2703  * Fork - LGPL
2704  * <script type="text/javascript">
2705  */
2706
2707 // as we use this in bootstrap.
2708 Roo.namespace('Roo.form');
2709  /**
2710  * @class Roo.form.Action
2711  * Internal Class used to handle form actions
2712  * @constructor
2713  * @param {Roo.form.BasicForm} el The form element or its id
2714  * @param {Object} config Configuration options
2715  */
2716
2717  
2718  
2719 // define the action interface
2720 Roo.form.Action = function(form, options){
2721     this.form = form;
2722     this.options = options || {};
2723 };
2724 /**
2725  * Client Validation Failed
2726  * @const 
2727  */
2728 Roo.form.Action.CLIENT_INVALID = 'client';
2729 /**
2730  * Server Validation Failed
2731  * @const 
2732  */
2733 Roo.form.Action.SERVER_INVALID = 'server';
2734  /**
2735  * Connect to Server Failed
2736  * @const 
2737  */
2738 Roo.form.Action.CONNECT_FAILURE = 'connect';
2739 /**
2740  * Reading Data from Server Failed
2741  * @const 
2742  */
2743 Roo.form.Action.LOAD_FAILURE = 'load';
2744
2745 Roo.form.Action.prototype = {
2746     type : 'default',
2747     failureType : undefined,
2748     response : undefined,
2749     result : undefined,
2750
2751     // interface method
2752     run : function(options){
2753
2754     },
2755
2756     // interface method
2757     success : function(response){
2758
2759     },
2760
2761     // interface method
2762     handleResponse : function(response){
2763
2764     },
2765
2766     // default connection failure
2767     failure : function(response){
2768         
2769         this.response = response;
2770         this.failureType = Roo.form.Action.CONNECT_FAILURE;
2771         this.form.afterAction(this, false);
2772     },
2773
2774     processResponse : function(response){
2775         this.response = response;
2776         if(!response.responseText){
2777             return true;
2778         }
2779         this.result = this.handleResponse(response);
2780         return this.result;
2781     },
2782
2783     // utility functions used internally
2784     getUrl : function(appendParams){
2785         var url = this.options.url || this.form.url || this.form.el.dom.action;
2786         if(appendParams){
2787             var p = this.getParams();
2788             if(p){
2789                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
2790             }
2791         }
2792         return url;
2793     },
2794
2795     getMethod : function(){
2796         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
2797     },
2798
2799     getParams : function(){
2800         var bp = this.form.baseParams;
2801         var p = this.options.params;
2802         if(p){
2803             if(typeof p == "object"){
2804                 p = Roo.urlEncode(Roo.applyIf(p, bp));
2805             }else if(typeof p == 'string' && bp){
2806                 p += '&' + Roo.urlEncode(bp);
2807             }
2808         }else if(bp){
2809             p = Roo.urlEncode(bp);
2810         }
2811         return p;
2812     },
2813
2814     createCallback : function(){
2815         return {
2816             success: this.success,
2817             failure: this.failure,
2818             scope: this,
2819             timeout: (this.form.timeout*1000),
2820             upload: this.form.fileUpload ? this.success : undefined
2821         };
2822     }
2823 };
2824
2825 Roo.form.Action.Submit = function(form, options){
2826     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
2827 };
2828
2829 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
2830     type : 'submit',
2831
2832     haveProgress : false,
2833     uploadComplete : false,
2834     
2835     // uploadProgress indicator.
2836     uploadProgress : function()
2837     {
2838         if (!this.form.progressUrl) {
2839             return;
2840         }
2841         
2842         if (!this.haveProgress) {
2843             Roo.MessageBox.progress("Uploading", "Uploading");
2844         }
2845         if (this.uploadComplete) {
2846            Roo.MessageBox.hide();
2847            return;
2848         }
2849         
2850         this.haveProgress = true;
2851    
2852         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
2853         
2854         var c = new Roo.data.Connection();
2855         c.request({
2856             url : this.form.progressUrl,
2857             params: {
2858                 id : uid
2859             },
2860             method: 'GET',
2861             success : function(req){
2862                //console.log(data);
2863                 var rdata = false;
2864                 var edata;
2865                 try  {
2866                    rdata = Roo.decode(req.responseText)
2867                 } catch (e) {
2868                     Roo.log("Invalid data from server..");
2869                     Roo.log(edata);
2870                     return;
2871                 }
2872                 if (!rdata || !rdata.success) {
2873                     Roo.log(rdata);
2874                     Roo.MessageBox.alert(Roo.encode(rdata));
2875                     return;
2876                 }
2877                 var data = rdata.data;
2878                 
2879                 if (this.uploadComplete) {
2880                    Roo.MessageBox.hide();
2881                    return;
2882                 }
2883                    
2884                 if (data){
2885                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
2886                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
2887                     );
2888                 }
2889                 this.uploadProgress.defer(2000,this);
2890             },
2891        
2892             failure: function(data) {
2893                 Roo.log('progress url failed ');
2894                 Roo.log(data);
2895             },
2896             scope : this
2897         });
2898            
2899     },
2900     
2901     
2902     run : function()
2903     {
2904         // run get Values on the form, so it syncs any secondary forms.
2905         this.form.getValues();
2906         
2907         var o = this.options;
2908         var method = this.getMethod();
2909         var isPost = method == 'POST';
2910         if(o.clientValidation === false || this.form.isValid()){
2911             
2912             if (this.form.progressUrl) {
2913                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
2914                     (new Date() * 1) + '' + Math.random());
2915                     
2916             } 
2917             
2918             
2919             Roo.Ajax.request(Roo.apply(this.createCallback(), {
2920                 form:this.form.el.dom,
2921                 url:this.getUrl(!isPost),
2922                 method: method,
2923                 params:isPost ? this.getParams() : null,
2924                 isUpload: this.form.fileUpload
2925             }));
2926             
2927             this.uploadProgress();
2928
2929         }else if (o.clientValidation !== false){ // client validation failed
2930             this.failureType = Roo.form.Action.CLIENT_INVALID;
2931             this.form.afterAction(this, false);
2932         }
2933     },
2934
2935     success : function(response)
2936     {
2937         this.uploadComplete= true;
2938         if (this.haveProgress) {
2939             Roo.MessageBox.hide();
2940         }
2941         
2942         
2943         var result = this.processResponse(response);
2944         if(result === true || result.success){
2945             this.form.afterAction(this, true);
2946             return;
2947         }
2948         if(result.errors){
2949             this.form.markInvalid(result.errors);
2950             this.failureType = Roo.form.Action.SERVER_INVALID;
2951         }
2952         this.form.afterAction(this, false);
2953     },
2954     failure : function(response)
2955     {
2956         this.uploadComplete= true;
2957         if (this.haveProgress) {
2958             Roo.MessageBox.hide();
2959         }
2960         
2961         this.response = response;
2962         this.failureType = Roo.form.Action.CONNECT_FAILURE;
2963         this.form.afterAction(this, false);
2964     },
2965     
2966     handleResponse : function(response){
2967         if(this.form.errorReader){
2968             var rs = this.form.errorReader.read(response);
2969             var errors = [];
2970             if(rs.records){
2971                 for(var i = 0, len = rs.records.length; i < len; i++) {
2972                     var r = rs.records[i];
2973                     errors[i] = r.data;
2974                 }
2975             }
2976             if(errors.length < 1){
2977                 errors = null;
2978             }
2979             return {
2980                 success : rs.success,
2981                 errors : errors
2982             };
2983         }
2984         var ret = false;
2985         try {
2986             ret = Roo.decode(response.responseText);
2987         } catch (e) {
2988             ret = {
2989                 success: false,
2990                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
2991                 errors : []
2992             };
2993         }
2994         return ret;
2995         
2996     }
2997 });
2998
2999
3000 Roo.form.Action.Load = function(form, options){
3001     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3002     this.reader = this.form.reader;
3003 };
3004
3005 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3006     type : 'load',
3007
3008     run : function(){
3009         
3010         Roo.Ajax.request(Roo.apply(
3011                 this.createCallback(), {
3012                     method:this.getMethod(),
3013                     url:this.getUrl(false),
3014                     params:this.getParams()
3015         }));
3016     },
3017
3018     success : function(response){
3019         
3020         var result = this.processResponse(response);
3021         if(result === true || !result.success || !result.data){
3022             this.failureType = Roo.form.Action.LOAD_FAILURE;
3023             this.form.afterAction(this, false);
3024             return;
3025         }
3026         this.form.clearInvalid();
3027         this.form.setValues(result.data);
3028         this.form.afterAction(this, true);
3029     },
3030
3031     handleResponse : function(response){
3032         if(this.form.reader){
3033             var rs = this.form.reader.read(response);
3034             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3035             return {
3036                 success : rs.success,
3037                 data : data
3038             };
3039         }
3040         return Roo.decode(response.responseText);
3041     }
3042 });
3043
3044 Roo.form.Action.ACTION_TYPES = {
3045     'load' : Roo.form.Action.Load,
3046     'submit' : Roo.form.Action.Submit
3047 };/*
3048  * - LGPL
3049  *
3050  * form
3051  * 
3052  */
3053
3054 /**
3055  * @class Roo.bootstrap.Form
3056  * @extends Roo.bootstrap.Component
3057  * Bootstrap Form class
3058  * @cfg {String} method  GET | POST (default POST)
3059  * @cfg {String} labelAlign top | left (default top)
3060   * @cfg {String} align left  | right - for navbars
3061
3062  * 
3063  * @constructor
3064  * Create a new Form
3065  * @param {Object} config The config object
3066  */
3067
3068
3069 Roo.bootstrap.Form = function(config){
3070     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3071     this.addEvents({
3072         /**
3073          * @event clientvalidation
3074          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3075          * @param {Form} this
3076          * @param {Boolean} valid true if the form has passed client-side validation
3077          */
3078         clientvalidation: true,
3079         /**
3080          * @event beforeaction
3081          * Fires before any action is performed. Return false to cancel the action.
3082          * @param {Form} this
3083          * @param {Action} action The action to be performed
3084          */
3085         beforeaction: true,
3086         /**
3087          * @event actionfailed
3088          * Fires when an action fails.
3089          * @param {Form} this
3090          * @param {Action} action The action that failed
3091          */
3092         actionfailed : true,
3093         /**
3094          * @event actioncomplete
3095          * Fires when an action is completed.
3096          * @param {Form} this
3097          * @param {Action} action The action that completed
3098          */
3099         actioncomplete : true
3100     });
3101     
3102 };
3103
3104 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3105       
3106      /**
3107      * @cfg {String} method
3108      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3109      */
3110     method : 'POST',
3111     /**
3112      * @cfg {String} url
3113      * The URL to use for form actions if one isn't supplied in the action options.
3114      */
3115     /**
3116      * @cfg {Boolean} fileUpload
3117      * Set to true if this form is a file upload.
3118      */
3119      
3120     /**
3121      * @cfg {Object} baseParams
3122      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3123      */
3124       
3125     /**
3126      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3127      */
3128     timeout: 30,
3129     /**
3130      * @cfg {Sting} align (left|right) for navbar forms
3131      */
3132     align : 'left',
3133
3134     // private
3135     activeAction : null,
3136  
3137     /**
3138      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3139      * element by passing it or its id or mask the form itself by passing in true.
3140      * @type Mixed
3141      */
3142     waitMsgTarget : false,
3143     
3144      
3145     
3146     /**
3147      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3148      * element by passing it or its id or mask the form itself by passing in true.
3149      * @type Mixed
3150      */
3151     
3152     getAutoCreate : function(){
3153         
3154         var cfg = {
3155             tag: 'form',
3156             method : this.method || 'POST',
3157             id : this.id || Roo.id(),
3158             cls : ''
3159         }
3160         if (this.parent().xtype.match(/^Nav/)) {
3161             cfg.cls = 'navbar-form navbar-' + this.align;
3162             
3163         }
3164         
3165         if (this.labelAlign == 'left' ) {
3166             cfg.cls += ' form-horizontal';
3167         }
3168         
3169         
3170         return cfg;
3171     },
3172     initEvents : function()
3173     {
3174         this.el.on('submit', this.onSubmit, this);
3175         
3176         
3177     },
3178     // private
3179     onSubmit : function(e){
3180         e.stopEvent();
3181     },
3182     
3183      /**
3184      * Returns true if client-side validation on the form is successful.
3185      * @return Boolean
3186      */
3187     isValid : function(){
3188         var items = this.getItems();
3189         var valid = true;
3190         items.each(function(f){
3191            if(!f.validate()){
3192                valid = false;
3193                
3194            }
3195         });
3196         return valid;
3197     },
3198     /**
3199      * Returns true if any fields in this form have changed since their original load.
3200      * @return Boolean
3201      */
3202     isDirty : function(){
3203         var dirty = false;
3204         var items = this.getItems();
3205         items.each(function(f){
3206            if(f.isDirty()){
3207                dirty = true;
3208                return false;
3209            }
3210            return true;
3211         });
3212         return dirty;
3213     },
3214      /**
3215      * Performs a predefined action (submit or load) or custom actions you define on this form.
3216      * @param {String} actionName The name of the action type
3217      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3218      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3219      * accept other config options):
3220      * <pre>
3221 Property          Type             Description
3222 ----------------  ---------------  ----------------------------------------------------------------------------------
3223 url               String           The url for the action (defaults to the form's url)
3224 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3225 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3226 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3227                                    validate the form on the client (defaults to false)
3228      * </pre>
3229      * @return {BasicForm} this
3230      */
3231     doAction : function(action, options){
3232         if(typeof action == 'string'){
3233             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3234         }
3235         Roo.log(action);
3236         if(this.fireEvent('beforeaction', this, action) !== false){
3237             this.beforeAction(action);
3238             action.run.defer(100, action);
3239         }
3240         return this;
3241     },
3242     
3243     // private
3244     beforeAction : function(action){
3245         var o = action.options;
3246         
3247         // not really supported yet.. ??
3248         
3249         //if(this.waitMsgTarget === true){
3250             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3251         //}else if(this.waitMsgTarget){
3252         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3253         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3254         //}else {
3255         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3256        // }
3257          
3258     },
3259
3260     // private
3261     afterAction : function(action, success){
3262         this.activeAction = null;
3263         var o = action.options;
3264         
3265         //if(this.waitMsgTarget === true){
3266             this.el.unmask();
3267         //}else if(this.waitMsgTarget){
3268         //    this.waitMsgTarget.unmask();
3269         //}else{
3270         //    Roo.MessageBox.updateProgress(1);
3271         //    Roo.MessageBox.hide();
3272        // }
3273         // 
3274         if(success){
3275             if(o.reset){
3276                 this.reset();
3277             }
3278             Roo.callback(o.success, o.scope, [this, action]);
3279             this.fireEvent('actioncomplete', this, action);
3280             
3281         }else{
3282             
3283             // failure condition..
3284             // we have a scenario where updates need confirming.
3285             // eg. if a locking scenario exists..
3286             // we look for { errors : { needs_confirm : true }} in the response.
3287             if (
3288                 (typeof(action.result) != 'undefined')  &&
3289                 (typeof(action.result.errors) != 'undefined')  &&
3290                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3291            ){
3292                 var _t = this;
3293                 Roo.log("not supported yet");
3294                  /*
3295                 
3296                 Roo.MessageBox.confirm(
3297                     "Change requires confirmation",
3298                     action.result.errorMsg,
3299                     function(r) {
3300                         if (r != 'yes') {
3301                             return;
3302                         }
3303                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3304                     }
3305                     
3306                 );
3307                 */
3308                 
3309                 
3310                 return;
3311             }
3312             
3313             Roo.callback(o.failure, o.scope, [this, action]);
3314             // show an error message if no failed handler is set..
3315             if (!this.hasListener('actionfailed')) {
3316                 Roo.log("need to add dialog support");
3317                 /*
3318                 Roo.MessageBox.alert("Error",
3319                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
3320                         action.result.errorMsg :
3321                         "Saving Failed, please check your entries or try again"
3322                 );
3323                 */
3324             }
3325             
3326             this.fireEvent('actionfailed', this, action);
3327         }
3328         
3329     },
3330     /**
3331      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
3332      * @param {String} id The value to search for
3333      * @return Field
3334      */
3335     findField : function(id){
3336         var items = this.getItems();
3337         var field = items.get(id);
3338         if(!field){
3339              items.each(function(f){
3340                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
3341                     field = f;
3342                     return false;
3343                 }
3344                 return true;
3345             });
3346         }
3347         return field || null;
3348     },
3349      /**
3350      * Mark fields in this form invalid in bulk.
3351      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
3352      * @return {BasicForm} this
3353      */
3354     markInvalid : function(errors){
3355         if(errors instanceof Array){
3356             for(var i = 0, len = errors.length; i < len; i++){
3357                 var fieldError = errors[i];
3358                 var f = this.findField(fieldError.id);
3359                 if(f){
3360                     f.markInvalid(fieldError.msg);
3361                 }
3362             }
3363         }else{
3364             var field, id;
3365             for(id in errors){
3366                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
3367                     field.markInvalid(errors[id]);
3368                 }
3369             }
3370         }
3371         //Roo.each(this.childForms || [], function (f) {
3372         //    f.markInvalid(errors);
3373         //});
3374         
3375         return this;
3376     },
3377
3378     /**
3379      * Set values for fields in this form in bulk.
3380      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
3381      * @return {BasicForm} this
3382      */
3383     setValues : function(values){
3384         if(values instanceof Array){ // array of objects
3385             for(var i = 0, len = values.length; i < len; i++){
3386                 var v = values[i];
3387                 var f = this.findField(v.id);
3388                 if(f){
3389                     f.setValue(v.value);
3390                     if(this.trackResetOnLoad){
3391                         f.originalValue = f.getValue();
3392                     }
3393                 }
3394             }
3395         }else{ // object hash
3396             var field, id;
3397             for(id in values){
3398                 if(typeof values[id] != 'function' && (field = this.findField(id))){
3399                     
3400                     if (field.setFromData && 
3401                         field.valueField && 
3402                         field.displayField &&
3403                         // combos' with local stores can 
3404                         // be queried via setValue()
3405                         // to set their value..
3406                         (field.store && !field.store.isLocal)
3407                         ) {
3408                         // it's a combo
3409                         var sd = { };
3410                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
3411                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
3412                         field.setFromData(sd);
3413                         
3414                     } else {
3415                         field.setValue(values[id]);
3416                     }
3417                     
3418                     
3419                     if(this.trackResetOnLoad){
3420                         field.originalValue = field.getValue();
3421                     }
3422                 }
3423             }
3424         }
3425          
3426         //Roo.each(this.childForms || [], function (f) {
3427         //    f.setValues(values);
3428         //});
3429                 
3430         return this;
3431     },
3432
3433     /**
3434      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
3435      * they are returned as an array.
3436      * @param {Boolean} asString
3437      * @return {Object}
3438      */
3439     getValues : function(asString){
3440         //if (this.childForms) {
3441             // copy values from the child forms
3442         //    Roo.each(this.childForms, function (f) {
3443         //        this.setValues(f.getValues());
3444         //    }, this);
3445         //}
3446         
3447         
3448         
3449         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
3450         if(asString === true){
3451             return fs;
3452         }
3453         return Roo.urlDecode(fs);
3454     },
3455     
3456     /**
3457      * Returns the fields in this form as an object with key/value pairs. 
3458      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
3459      * @return {Object}
3460      */
3461     getFieldValues : function(with_hidden)
3462     {
3463         var items = this.getItems();
3464         var ret = {};
3465         items.each(function(f){
3466             if (!f.getName()) {
3467                 return;
3468             }
3469             var v = f.getValue();
3470             if (f.inputType =='radio') {
3471                 if (typeof(ret[f.getName()]) == 'undefined') {
3472                     ret[f.getName()] = ''; // empty..
3473                 }
3474                 
3475                 if (!f.el.dom.checked) {
3476                     return;
3477                     
3478                 }
3479                 v = f.el.dom.value;
3480                 
3481             }
3482             
3483             // not sure if this supported any more..
3484             if ((typeof(v) == 'object') && f.getRawValue) {
3485                 v = f.getRawValue() ; // dates..
3486             }
3487             // combo boxes where name != hiddenName...
3488             if (f.name != f.getName()) {
3489                 ret[f.name] = f.getRawValue();
3490             }
3491             ret[f.getName()] = v;
3492         });
3493         
3494         return ret;
3495     },
3496
3497     /**
3498      * Clears all invalid messages in this form.
3499      * @return {BasicForm} this
3500      */
3501     clearInvalid : function(){
3502         var items = this.getItems();
3503         
3504         items.each(function(f){
3505            f.clearInvalid();
3506         });
3507         
3508         
3509         
3510         return this;
3511     },
3512
3513     /**
3514      * Resets this form.
3515      * @return {BasicForm} this
3516      */
3517     reset : function(){
3518         var items = this.getItems();
3519         items.each(function(f){
3520             f.reset();
3521         });
3522         
3523         Roo.each(this.childForms || [], function (f) {
3524             f.reset();
3525         });
3526        
3527         
3528         return this;
3529     },
3530     getItems : function()
3531     {
3532         var r=new Roo.util.MixedCollection(false, function(o){
3533             return o.id || (o.id = Roo.id());
3534         });
3535         var iter = function(el) {
3536             if (el.inputEl) {
3537                 r.add(el);
3538             }
3539             if (!el.items) {
3540                 return;
3541             }
3542             Roo.each(el.items,function(e) {
3543                 iter(e);
3544             });
3545             
3546             
3547         };
3548         iter(this);
3549         return r;
3550         
3551         
3552         
3553         
3554     }
3555     
3556 });
3557
3558  
3559 /*
3560  * Based on:
3561  * Ext JS Library 1.1.1
3562  * Copyright(c) 2006-2007, Ext JS, LLC.
3563  *
3564  * Originally Released Under LGPL - original licence link has changed is not relivant.
3565  *
3566  * Fork - LGPL
3567  * <script type="text/javascript">
3568  */
3569 /**
3570  * @class Roo.form.VTypes
3571  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
3572  * @singleton
3573  */
3574 Roo.form.VTypes = function(){
3575     // closure these in so they are only created once.
3576     var alpha = /^[a-zA-Z_]+$/;
3577     var alphanum = /^[a-zA-Z0-9_]+$/;
3578     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
3579     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
3580
3581     // All these messages and functions are configurable
3582     return {
3583         /**
3584          * The function used to validate email addresses
3585          * @param {String} value The email address
3586          */
3587         'email' : function(v){
3588             return email.test(v);
3589         },
3590         /**
3591          * The error text to display when the email validation function returns false
3592          * @type String
3593          */
3594         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
3595         /**
3596          * The keystroke filter mask to be applied on email input
3597          * @type RegExp
3598          */
3599         'emailMask' : /[a-z0-9_\.\-@]/i,
3600
3601         /**
3602          * The function used to validate URLs
3603          * @param {String} value The URL
3604          */
3605         'url' : function(v){
3606             return url.test(v);
3607         },
3608         /**
3609          * The error text to display when the url validation function returns false
3610          * @type String
3611          */
3612         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
3613         
3614         /**
3615          * The function used to validate alpha values
3616          * @param {String} value The value
3617          */
3618         'alpha' : function(v){
3619             return alpha.test(v);
3620         },
3621         /**
3622          * The error text to display when the alpha validation function returns false
3623          * @type String
3624          */
3625         'alphaText' : 'This field should only contain letters and _',
3626         /**
3627          * The keystroke filter mask to be applied on alpha input
3628          * @type RegExp
3629          */
3630         'alphaMask' : /[a-z_]/i,
3631
3632         /**
3633          * The function used to validate alphanumeric values
3634          * @param {String} value The value
3635          */
3636         'alphanum' : function(v){
3637             return alphanum.test(v);
3638         },
3639         /**
3640          * The error text to display when the alphanumeric validation function returns false
3641          * @type String
3642          */
3643         'alphanumText' : 'This field should only contain letters, numbers and _',
3644         /**
3645          * The keystroke filter mask to be applied on alphanumeric input
3646          * @type RegExp
3647          */
3648         'alphanumMask' : /[a-z0-9_]/i
3649     };
3650 }();/*
3651  * - LGPL
3652  *
3653  * Input
3654  * 
3655  */
3656
3657 /**
3658  * @class Roo.bootstrap.Input
3659  * @extends Roo.bootstrap.Component
3660  * Bootstrap Input class
3661  * @cfg {Boolean} disabled is it disabled
3662  * @cfg {String} fieldLabel - the label associated
3663  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
3664  * @cfg {String} name name of the input
3665  * @cfg {string} fieldLabel - the label associated
3666  * @cfg {string}  inputType - input / file submit ...
3667  * @cfg {string} placeholder - placeholder to put in text.
3668  * @cfg {string}  before - input group add on before
3669  * @cfg {string} after - input group add on after
3670  * @cfg {string} size - (lg|sm) or leave empty..
3671  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
3672  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
3673  * @cfg {Number} md colspan out of 12 for computer-sized screens
3674  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
3675  * @cfg {string} value default value of the input
3676  * @cfg {Number} labelWidth set the width of label (0-12)
3677  * @cfg {String} labelAlign (top|left)
3678  * 
3679  * 
3680  * @constructor
3681  * Create a new Input
3682  * @param {Object} config The config object
3683  */
3684
3685 Roo.bootstrap.Input = function(config){
3686     Roo.bootstrap.Input.superclass.constructor.call(this, config);
3687    
3688         this.addEvents({
3689             /**
3690              * @event focus
3691              * Fires when this field receives input focus.
3692              * @param {Roo.form.Field} this
3693              */
3694             focus : true,
3695             /**
3696              * @event blur
3697              * Fires when this field loses input focus.
3698              * @param {Roo.form.Field} this
3699              */
3700             blur : true,
3701             /**
3702              * @event specialkey
3703              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
3704              * {@link Roo.EventObject#getKey} to determine which key was pressed.
3705              * @param {Roo.form.Field} this
3706              * @param {Roo.EventObject} e The event object
3707              */
3708             specialkey : true,
3709             /**
3710              * @event change
3711              * Fires just before the field blurs if the field value has changed.
3712              * @param {Roo.form.Field} this
3713              * @param {Mixed} newValue The new value
3714              * @param {Mixed} oldValue The original value
3715              */
3716             change : true,
3717             /**
3718              * @event invalid
3719              * Fires after the field has been marked as invalid.
3720              * @param {Roo.form.Field} this
3721              * @param {String} msg The validation message
3722              */
3723             invalid : true,
3724             /**
3725              * @event valid
3726              * Fires after the field has been validated with no errors.
3727              * @param {Roo.form.Field} this
3728              */
3729             valid : true,
3730              /**
3731              * @event keyup
3732              * Fires after the key up
3733              * @param {Roo.form.Field} this
3734              * @param {Roo.EventObject}  e The event Object
3735              */
3736             keyup : true
3737         });
3738 };
3739
3740 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
3741      /**
3742      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
3743       automatic validation (defaults to "keyup").
3744      */
3745     validationEvent : "keyup",
3746      /**
3747      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
3748      */
3749     validateOnBlur : true,
3750     /**
3751      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
3752      */
3753     validationDelay : 250,
3754      /**
3755      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
3756      */
3757     focusClass : "x-form-focus",  // not needed???
3758     
3759        
3760     /**
3761      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
3762      */
3763     invalidClass : "has-error",
3764     
3765     /**
3766      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
3767      */
3768     selectOnFocus : false,
3769     
3770      /**
3771      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
3772      */
3773     maskRe : null,
3774        /**
3775      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
3776      */
3777     vtype : null,
3778     
3779       /**
3780      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
3781      */
3782     disableKeyFilter : false,
3783     
3784        /**
3785      * @cfg {Boolean} disabled True to disable the field (defaults to false).
3786      */
3787     disabled : false,
3788      /**
3789      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
3790      */
3791     allowBlank : true,
3792     /**
3793      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
3794      */
3795     blankText : "This field is required",
3796     
3797      /**
3798      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
3799      */
3800     minLength : 0,
3801     /**
3802      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
3803      */
3804     maxLength : Number.MAX_VALUE,
3805     /**
3806      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
3807      */
3808     minLengthText : "The minimum length for this field is {0}",
3809     /**
3810      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
3811      */
3812     maxLengthText : "The maximum length for this field is {0}",
3813   
3814     
3815     /**
3816      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
3817      * If available, this function will be called only after the basic validators all return true, and will be passed the
3818      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
3819      */
3820     validator : null,
3821     /**
3822      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
3823      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
3824      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
3825      */
3826     regex : null,
3827     /**
3828      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
3829      */
3830     regexText : "",
3831     
3832     
3833     
3834     fieldLabel : '',
3835     inputType : 'text',
3836     
3837     name : false,
3838     placeholder: false,
3839     before : false,
3840     after : false,
3841     size : false,
3842     // private
3843     hasFocus : false,
3844     preventMark: false,
3845     isFormField : true,
3846     value : '',
3847     labelWidth : 2,
3848     labelAlign : false,
3849     
3850     parentLabelAlign : function()
3851     {
3852         var parent = this;
3853         while (parent.parent()) {
3854             parent = parent.parent();
3855             if (typeof(parent.labelAlign) !='undefined') {
3856                 return parent.labelAlign;
3857             }
3858         }
3859         return 'left';
3860         
3861     },
3862     
3863     getAutoCreate : function(){
3864         
3865         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
3866         
3867         var id = Roo.id();
3868         
3869         var cfg = {};
3870         
3871         if(this.inputType != 'hidden'){
3872             cfg.cls = 'form-group' //input-group
3873         }
3874         
3875         var input =  {
3876             tag: 'input',
3877             id : id,
3878             type : this.inputType,
3879             value : this.value,
3880             cls : 'form-control',
3881             placeholder : this.placeholder || ''
3882             
3883         };
3884         
3885         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
3886             input.maxLength = this.maxLength;
3887         }
3888         
3889         if (this.disabled) {
3890             input.disabled=true;
3891         }
3892         
3893         if (this.name) {
3894             input.name = this.name;
3895         }
3896         if (this.size) {
3897             input.cls += ' input-' + this.size;
3898         }
3899         var settings=this;
3900         ['xs','sm','md','lg'].map(function(size){
3901             if (settings[size]) {
3902                 cfg.cls += ' col-' + size + '-' + settings[size];
3903             }
3904         });
3905         
3906         var inputblock = input;
3907         
3908         if (this.before || this.after) {
3909             
3910             inputblock = {
3911                 cls : 'input-group',
3912                 cn :  [] 
3913             };
3914             if (this.before) {
3915                 inputblock.cn.push({
3916                     tag :'span',
3917                     cls : 'input-group-addon',
3918                     html : this.before
3919                 });
3920             }
3921             inputblock.cn.push(input);
3922             if (this.after) {
3923                 inputblock.cn.push({
3924                     tag :'span',
3925                     cls : 'input-group-addon',
3926                     html : this.after
3927                 });
3928             }
3929             
3930         };
3931         
3932         if (align ==='left' && this.fieldLabel.length) {
3933                 Roo.log("left and has label");
3934                 cfg.cn = [
3935                     
3936                     {
3937                         tag: 'label',
3938                         'for' :  id,
3939                         cls : 'control-label col-sm-' + this.labelWidth,
3940                         html : this.fieldLabel
3941                         
3942                     },
3943                     {
3944                         cls : "col-sm-" + (12 - this.labelWidth), 
3945                         cn: [
3946                             inputblock
3947                         ]
3948                     }
3949                     
3950                 ];
3951         } else if ( this.fieldLabel.length) {
3952                 Roo.log(" label");
3953                  cfg.cn = [
3954                    
3955                     {
3956                         tag: 'label',
3957                         //cls : 'input-group-addon',
3958                         html : this.fieldLabel
3959                         
3960                     },
3961                     
3962                     inputblock
3963                     
3964                 ];
3965
3966         } else {
3967             
3968                    Roo.log(" no label && no align");
3969                 cfg.cn = [
3970                     
3971                         inputblock
3972                     
3973                 ];
3974                 
3975                 
3976         };
3977         
3978         return cfg;
3979         
3980     },
3981     /**
3982      * return the real input element.
3983      */
3984     inputEl: function ()
3985     {
3986         return this.el.select('input.form-control',true).first();
3987     },
3988     setDisabled : function(v)
3989     {
3990         var i  = this.inputEl().dom;
3991         if (!v) {
3992             i.removeAttribute('disabled');
3993             return;
3994             
3995         }
3996         i.setAttribute('disabled','true');
3997     },
3998     initEvents : function()
3999     {
4000         
4001         this.inputEl().on("keydown" , this.fireKey,  this);
4002         this.inputEl().on("focus", this.onFocus,  this);
4003         this.inputEl().on("blur", this.onBlur,  this);
4004         
4005         this.inputEl().relayEvent('keyup', this);
4006
4007         // reference to original value for reset
4008         this.originalValue = this.getValue();
4009         //Roo.form.TextField.superclass.initEvents.call(this);
4010         if(this.validationEvent == 'keyup'){
4011             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4012             this.inputEl().on('keyup', this.filterValidation, this);
4013         }
4014         else if(this.validationEvent !== false){
4015             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4016         }
4017         
4018         if(this.selectOnFocus){
4019             this.on("focus", this.preFocus, this);
4020             
4021         }
4022         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4023             this.inputEl().on("keypress", this.filterKeys, this);
4024         }
4025        /* if(this.grow){
4026             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4027             this.el.on("click", this.autoSize,  this);
4028         }
4029         */
4030         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4031             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4032         }
4033         
4034     },
4035     filterValidation : function(e){
4036         if(!e.isNavKeyPress()){
4037             this.validationTask.delay(this.validationDelay);
4038         }
4039     },
4040      /**
4041      * Validates the field value
4042      * @return {Boolean} True if the value is valid, else false
4043      */
4044     validate : function(){
4045         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4046         if(this.disabled || this.validateValue(this.getRawValue())){
4047             this.clearInvalid();
4048             return true;
4049         }
4050         return false;
4051     },
4052     
4053     
4054     /**
4055      * Validates a value according to the field's validation rules and marks the field as invalid
4056      * if the validation fails
4057      * @param {Mixed} value The value to validate
4058      * @return {Boolean} True if the value is valid, else false
4059      */
4060     validateValue : function(value){
4061         if(value.length < 1)  { // if it's blank
4062              if(this.allowBlank){
4063                 this.clearInvalid();
4064                 return true;
4065              }else{
4066                 this.markInvalid(this.blankText);
4067                 return false;
4068              }
4069         }
4070         if(value.length < this.minLength){
4071             this.markInvalid(String.format(this.minLengthText, this.minLength));
4072             return false;
4073         }
4074         if(value.length > this.maxLength){
4075             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4076             return false;
4077         }
4078         if(this.vtype){
4079             var vt = Roo.form.VTypes;
4080             if(!vt[this.vtype](value, this)){
4081                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4082                 return false;
4083             }
4084         }
4085         if(typeof this.validator == "function"){
4086             var msg = this.validator(value);
4087             if(msg !== true){
4088                 this.markInvalid(msg);
4089                 return false;
4090             }
4091         }
4092         if(this.regex && !this.regex.test(value)){
4093             this.markInvalid(this.regexText);
4094             return false;
4095         }
4096         return true;
4097     },
4098
4099     
4100     
4101      // private
4102     fireKey : function(e){
4103         //Roo.log('field ' + e.getKey());
4104         if(e.isNavKeyPress()){
4105             this.fireEvent("specialkey", this, e);
4106         }
4107     },
4108     focus : function (selectText){
4109         if(this.rendered){
4110             this.inputEl().focus();
4111             if(selectText === true){
4112                 this.inputEl().dom.select();
4113             }
4114         }
4115         return this;
4116     } ,
4117     
4118     onFocus : function(){
4119         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4120            // this.el.addClass(this.focusClass);
4121         }
4122         if(!this.hasFocus){
4123             this.hasFocus = true;
4124             this.startValue = this.getValue();
4125             this.fireEvent("focus", this);
4126         }
4127     },
4128     
4129     beforeBlur : Roo.emptyFn,
4130
4131     
4132     // private
4133     onBlur : function(){
4134         this.beforeBlur();
4135         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4136             //this.el.removeClass(this.focusClass);
4137         }
4138         this.hasFocus = false;
4139         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4140             this.validate();
4141         }
4142         var v = this.getValue();
4143         if(String(v) !== String(this.startValue)){
4144             this.fireEvent('change', this, v, this.startValue);
4145         }
4146         this.fireEvent("blur", this);
4147     },
4148     
4149     /**
4150      * Resets the current field value to the originally loaded value and clears any validation messages
4151      */
4152     reset : function(){
4153         this.setValue(this.originalValue);
4154         this.clearInvalid();
4155     },
4156      /**
4157      * Returns the name of the field
4158      * @return {Mixed} name The name field
4159      */
4160     getName: function(){
4161         return this.name;
4162     },
4163      /**
4164      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4165      * @return {Mixed} value The field value
4166      */
4167     getValue : function(){
4168         return this.inputEl().getValue();
4169     },
4170     /**
4171      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4172      * @return {Mixed} value The field value
4173      */
4174     getRawValue : function(){
4175         var v = this.inputEl().getValue();
4176         
4177         return v;
4178     },
4179     
4180     /**
4181      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4182      * @param {Mixed} value The value to set
4183      */
4184     setRawValue : function(v){
4185         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4186     },
4187     
4188     selectText : function(start, end){
4189         var v = this.getRawValue();
4190         if(v.length > 0){
4191             start = start === undefined ? 0 : start;
4192             end = end === undefined ? v.length : end;
4193             var d = this.inputEl().dom;
4194             if(d.setSelectionRange){
4195                 d.setSelectionRange(start, end);
4196             }else if(d.createTextRange){
4197                 var range = d.createTextRange();
4198                 range.moveStart("character", start);
4199                 range.moveEnd("character", v.length-end);
4200                 range.select();
4201             }
4202         }
4203     },
4204     
4205     /**
4206      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4207      * @param {Mixed} value The value to set
4208      */
4209     setValue : function(v){
4210         this.value = v;
4211         if(this.rendered){
4212             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4213             this.validate();
4214         }
4215     },
4216     
4217     /*
4218     processValue : function(value){
4219         if(this.stripCharsRe){
4220             var newValue = value.replace(this.stripCharsRe, '');
4221             if(newValue !== value){
4222                 this.setRawValue(newValue);
4223                 return newValue;
4224             }
4225         }
4226         return value;
4227     },
4228   */
4229     preFocus : function(){
4230         
4231         if(this.selectOnFocus){
4232             this.inputEl().dom.select();
4233         }
4234     },
4235     filterKeys : function(e){
4236         var k = e.getKey();
4237         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4238             return;
4239         }
4240         var c = e.getCharCode(), cc = String.fromCharCode(c);
4241         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4242             return;
4243         }
4244         if(!this.maskRe.test(cc)){
4245             e.stopEvent();
4246         }
4247     },
4248      /**
4249      * Clear any invalid styles/messages for this field
4250      */
4251     clearInvalid : function(){
4252         
4253         if(!this.el || this.preventMark){ // not rendered
4254             return;
4255         }
4256         this.el.removeClass(this.invalidClass);
4257         /*
4258         switch(this.msgTarget){
4259             case 'qtip':
4260                 this.el.dom.qtip = '';
4261                 break;
4262             case 'title':
4263                 this.el.dom.title = '';
4264                 break;
4265             case 'under':
4266                 if(this.errorEl){
4267                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4268                 }
4269                 break;
4270             case 'side':
4271                 if(this.errorIcon){
4272                     this.errorIcon.dom.qtip = '';
4273                     this.errorIcon.hide();
4274                     this.un('resize', this.alignErrorIcon, this);
4275                 }
4276                 break;
4277             default:
4278                 var t = Roo.getDom(this.msgTarget);
4279                 t.innerHTML = '';
4280                 t.style.display = 'none';
4281                 break;
4282         }
4283         */
4284         this.fireEvent('valid', this);
4285     },
4286      /**
4287      * Mark this field as invalid
4288      * @param {String} msg The validation message
4289      */
4290     markInvalid : function(msg){
4291         if(!this.el  || this.preventMark){ // not rendered
4292             return;
4293         }
4294         this.el.addClass(this.invalidClass);
4295         /*
4296         msg = msg || this.invalidText;
4297         switch(this.msgTarget){
4298             case 'qtip':
4299                 this.el.dom.qtip = msg;
4300                 this.el.dom.qclass = 'x-form-invalid-tip';
4301                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
4302                     Roo.QuickTips.enable();
4303                 }
4304                 break;
4305             case 'title':
4306                 this.el.dom.title = msg;
4307                 break;
4308             case 'under':
4309                 if(!this.errorEl){
4310                     var elp = this.el.findParent('.x-form-element', 5, true);
4311                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
4312                     this.errorEl.setWidth(elp.getWidth(true)-20);
4313                 }
4314                 this.errorEl.update(msg);
4315                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
4316                 break;
4317             case 'side':
4318                 if(!this.errorIcon){
4319                     var elp = this.el.findParent('.x-form-element', 5, true);
4320                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
4321                 }
4322                 this.alignErrorIcon();
4323                 this.errorIcon.dom.qtip = msg;
4324                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
4325                 this.errorIcon.show();
4326                 this.on('resize', this.alignErrorIcon, this);
4327                 break;
4328             default:
4329                 var t = Roo.getDom(this.msgTarget);
4330                 t.innerHTML = msg;
4331                 t.style.display = this.msgDisplay;
4332                 break;
4333         }
4334         */
4335         this.fireEvent('invalid', this, msg);
4336     },
4337     // private
4338     SafariOnKeyDown : function(event)
4339     {
4340         // this is a workaround for a password hang bug on chrome/ webkit.
4341         
4342         var isSelectAll = false;
4343         
4344         if(this.inputEl().dom.selectionEnd > 0){
4345             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
4346         }
4347         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
4348             event.preventDefault();
4349             this.setValue('');
4350             return;
4351         }
4352         
4353         if(isSelectAll){ // backspace and delete key
4354             
4355             event.preventDefault();
4356             // this is very hacky as keydown always get's upper case.
4357             //
4358             var cc = String.fromCharCode(event.getCharCode());
4359             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
4360             
4361         }
4362     }
4363     
4364 });
4365
4366  
4367 /*
4368  * - LGPL
4369  *
4370  * Input
4371  * 
4372  */
4373
4374 /**
4375  * @class Roo.bootstrap.TextArea
4376  * @extends Roo.bootstrap.Input
4377  * Bootstrap TextArea class
4378  * @cfg {Number} cols Specifies the visible width of a text area
4379  * @cfg {Number} rows Specifies the visible number of lines in a text area
4380  * @cfg {Number} readOnly Specifies that a text area should be read-only
4381  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
4382  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
4383  * @cfg {string} html text
4384  * 
4385  * @constructor
4386  * Create a new TextArea
4387  * @param {Object} config The config object
4388  */
4389
4390 Roo.bootstrap.TextArea = function(config){
4391     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
4392    
4393 };
4394
4395 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
4396      
4397     cols : false,
4398     rows : 5,
4399     readOnly : false,
4400     warp : 'soft',
4401     resize : false,
4402     value: false,
4403     html: false,
4404     
4405     getAutoCreate : function(){
4406         
4407         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4408         
4409         var id = Roo.id();
4410         
4411         var cfg = {};
4412         
4413         var input =  {
4414             tag: 'textarea',
4415             id : id,
4416             warp : this.warp,
4417             rows : this.rows,
4418             value : this.value || '',
4419             html: this.html || '',
4420             cls : 'form-control',
4421             placeholder : this.placeholder || '' 
4422             
4423         };
4424         
4425         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4426             input.maxLength = this.maxLength;
4427         }
4428         
4429         if(this.resize){
4430             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
4431         }
4432         
4433         if(this.cols){
4434             input.cols = this.cols;
4435         }
4436         
4437         if (this.readOnly) {
4438             input.readonly = true;
4439         }
4440         
4441         if (this.name) {
4442             input.name = this.name;
4443         }
4444         
4445         if (this.size) {
4446             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
4447         }
4448         
4449         var settings=this;
4450         ['xs','sm','md','lg'].map(function(size){
4451             if (settings[size]) {
4452                 cfg.cls += ' col-' + size + '-' + settings[size];
4453             }
4454         });
4455         
4456         var inputblock = input;
4457         
4458         if (this.before || this.after) {
4459             
4460             inputblock = {
4461                 cls : 'input-group',
4462                 cn :  [] 
4463             };
4464             if (this.before) {
4465                 inputblock.cn.push({
4466                     tag :'span',
4467                     cls : 'input-group-addon',
4468                     html : this.before
4469                 });
4470             }
4471             inputblock.cn.push(input);
4472             if (this.after) {
4473                 inputblock.cn.push({
4474                     tag :'span',
4475                     cls : 'input-group-addon',
4476                     html : this.after
4477                 });
4478             }
4479             
4480         }
4481         
4482         if (align ==='left' && this.fieldLabel.length) {
4483                 Roo.log("left and has label");
4484                 cfg.cn = [
4485                     
4486                     {
4487                         tag: 'label',
4488                         'for' :  id,
4489                         cls : 'control-label col-sm-' + this.labelWidth,
4490                         html : this.fieldLabel
4491                         
4492                     },
4493                     {
4494                         cls : "col-sm-" + (12 - this.labelWidth), 
4495                         cn: [
4496                             inputblock
4497                         ]
4498                     }
4499                     
4500                 ];
4501         } else if ( this.fieldLabel.length) {
4502                 Roo.log(" label");
4503                  cfg.cn = [
4504                    
4505                     {
4506                         tag: 'label',
4507                         //cls : 'input-group-addon',
4508                         html : this.fieldLabel
4509                         
4510                     },
4511                     
4512                     inputblock
4513                     
4514                 ];
4515
4516         } else {
4517             
4518                    Roo.log(" no label && no align");
4519                 cfg.cn = [
4520                     
4521                         inputblock
4522                     
4523                 ];
4524                 
4525                 
4526         }
4527         
4528         if (this.disabled) {
4529             input.disabled=true;
4530         }
4531         
4532         return cfg;
4533         
4534     },
4535     /**
4536      * return the real textarea element.
4537      */
4538     inputEl: function ()
4539     {
4540         return this.el.select('textarea.form-control',true).first();
4541     }
4542 });
4543
4544  
4545 /*
4546  * - LGPL
4547  *
4548  * trigger field - base class for combo..
4549  * 
4550  */
4551  
4552 /**
4553  * @class Roo.bootstrap.TriggerField
4554  * @extends Roo.bootstrap.Input
4555  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
4556  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
4557  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
4558  * for which you can provide a custom implementation.  For example:
4559  * <pre><code>
4560 var trigger = new Roo.bootstrap.TriggerField();
4561 trigger.onTriggerClick = myTriggerFn;
4562 trigger.applyTo('my-field');
4563 </code></pre>
4564  *
4565  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
4566  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
4567  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
4568  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
4569  * @constructor
4570  * Create a new TriggerField.
4571  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
4572  * to the base TextField)
4573  */
4574 Roo.bootstrap.TriggerField = function(config){
4575     this.mimicing = false;
4576     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
4577 };
4578
4579 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
4580     /**
4581      * @cfg {String} triggerClass A CSS class to apply to the trigger
4582      */
4583      /**
4584      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
4585      */
4586     hideTrigger:false,
4587
4588     /** @cfg {Boolean} grow @hide */
4589     /** @cfg {Number} growMin @hide */
4590     /** @cfg {Number} growMax @hide */
4591
4592     /**
4593      * @hide 
4594      * @method
4595      */
4596     autoSize: Roo.emptyFn,
4597     // private
4598     monitorTab : true,
4599     // private
4600     deferHeight : true,
4601
4602     
4603     actionMode : 'wrap',
4604     
4605     
4606     
4607     getAutoCreate : function(){
4608        
4609         var parent = this.parent();
4610         
4611         var align = this.parentLabelAlign();
4612         
4613         var id = Roo.id();
4614         
4615         var cfg = {
4616             cls: 'form-group' //input-group
4617         };
4618         
4619         
4620         var input =  {
4621             tag: 'input',
4622             id : id,
4623             type : this.inputType,
4624             cls : 'form-control',
4625             autocomplete: 'off',
4626             placeholder : this.placeholder || '' 
4627             
4628         };
4629         if (this.name) {
4630             input.name = this.name;
4631         }
4632         if (this.size) {
4633             input.cls += ' input-' + this.size;
4634         }
4635         var inputblock = {
4636             cls: 'combobox-container input-group',
4637             cn: [
4638                 {
4639                     tag: 'input',
4640                     type : 'hidden',
4641                     cls: 'form-hidden-field'
4642                 },
4643                 input,
4644                 {
4645                     tag: 'ul',
4646                     cls : 'typeahead typeahead-long dropdown-menu',
4647                     style : 'display:none'
4648                 },
4649                 {
4650                     tag :'span',
4651                     cls : 'input-group-addon btn dropdown-toggle',
4652                     cn : [
4653                         {
4654                             tag: 'span',
4655                             cls: 'caret'
4656                         },
4657                         {
4658                             tag: 'span',
4659                             cls: 'combobox-clear',
4660                             cn  : [
4661                                 {
4662                                     tag : 'i',
4663                                     cls: 'icon-remove'
4664                                 }
4665                             ]
4666                         }
4667                     ]
4668                         
4669                 }
4670             ]
4671         };
4672         
4673         
4674         
4675         
4676         if (align ==='left' && this.fieldLabel.length) {
4677                 
4678             
4679             
4680                 Roo.log("left and has label");
4681                 cfg.cn = [
4682                     
4683                     {
4684                         tag: 'label',
4685                         'for' :  id,
4686                         cls : 'col-sm-2 control-label',
4687                         html : this.fieldLabel
4688                         
4689                     },
4690                     {
4691                         cls : "col-sm-10", 
4692                         cn: [
4693                             inputblock
4694                         ]
4695                     }
4696                     
4697                 ];
4698         } else if ( this.fieldLabel.length) {
4699                 Roo.log(" label");
4700                  cfg.cn = [
4701                    
4702                     {
4703                         tag: 'label',
4704                         //cls : 'input-group-addon',
4705                         html : this.fieldLabel
4706                         
4707                     },
4708                     
4709                     inputblock
4710                     
4711                 ];
4712
4713         } else {
4714             
4715                 Roo.log(" no label && no align");
4716                 cfg = inputblock
4717                      
4718                 
4719         }
4720          
4721         var settings=this;
4722         ['xs','sm','md','lg'].map(function(size){
4723             if (settings[size]) {
4724                 cfg.cls += ' col-' + size + '-' + settings[size];
4725             }
4726         });
4727         
4728         
4729         
4730         if (this.disabled) {
4731             input.disabled=true;
4732         }
4733         return cfg;
4734         
4735     },
4736     
4737     
4738     
4739     // private
4740     onResize : function(w, h){
4741 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
4742 //        if(typeof w == 'number'){
4743 //            var x = w - this.trigger.getWidth();
4744 //            this.inputEl().setWidth(this.adjustWidth('input', x));
4745 //            this.trigger.setStyle('left', x+'px');
4746 //        }
4747     },
4748
4749     // private
4750     adjustSize : Roo.BoxComponent.prototype.adjustSize,
4751
4752     // private
4753     getResizeEl : function(){
4754         return this.inputEl();
4755     },
4756
4757     // private
4758     getPositionEl : function(){
4759         return this.inputEl();
4760     },
4761
4762     // private
4763     alignErrorIcon : function(){
4764         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
4765     },
4766
4767     // private
4768     initEvents : function(){
4769         
4770         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
4771         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
4772         
4773         this.trigger = this.el.select('span.dropdown-toggle',true).first();
4774         if(this.hideTrigger){
4775             this.trigger.setDisplayed(false);
4776         }
4777         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
4778         //this.trigger.addClassOnOver('x-form-trigger-over');
4779         //this.trigger.addClassOnClick('x-form-trigger-click');
4780         
4781         //if(!this.width){
4782         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
4783         //}
4784     },
4785
4786     // private
4787     initTrigger : function(){
4788        
4789     },
4790
4791     // private
4792     onDestroy : function(){
4793         if(this.trigger){
4794             this.trigger.removeAllListeners();
4795           //  this.trigger.remove();
4796         }
4797         //if(this.wrap){
4798         //    this.wrap.remove();
4799         //}
4800         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
4801     },
4802
4803     // private
4804     onFocus : function(){
4805         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
4806         /*
4807         if(!this.mimicing){
4808             this.wrap.addClass('x-trigger-wrap-focus');
4809             this.mimicing = true;
4810             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
4811             if(this.monitorTab){
4812                 this.el.on("keydown", this.checkTab, this);
4813             }
4814         }
4815         */
4816     },
4817
4818     // private
4819     checkTab : function(e){
4820         if(e.getKey() == e.TAB){
4821             this.triggerBlur();
4822         }
4823     },
4824
4825     // private
4826     onBlur : function(){
4827         // do nothing
4828     },
4829
4830     // private
4831     mimicBlur : function(e, t){
4832         /*
4833         if(!this.wrap.contains(t) && this.validateBlur()){
4834             this.triggerBlur();
4835         }
4836         */
4837     },
4838
4839     // private
4840     triggerBlur : function(){
4841         this.mimicing = false;
4842         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
4843         if(this.monitorTab){
4844             this.el.un("keydown", this.checkTab, this);
4845         }
4846         //this.wrap.removeClass('x-trigger-wrap-focus');
4847         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
4848     },
4849
4850     // private
4851     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
4852     validateBlur : function(e, t){
4853         return true;
4854     },
4855
4856     // private
4857     onDisable : function(){
4858         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
4859         //if(this.wrap){
4860         //    this.wrap.addClass('x-item-disabled');
4861         //}
4862     },
4863
4864     // private
4865     onEnable : function(){
4866         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
4867         //if(this.wrap){
4868         //    this.el.removeClass('x-item-disabled');
4869         //}
4870     },
4871
4872     // private
4873     onShow : function(){
4874         var ae = this.getActionEl();
4875         
4876         if(ae){
4877             ae.dom.style.display = '';
4878             ae.dom.style.visibility = 'visible';
4879         }
4880     },
4881
4882     // private
4883     
4884     onHide : function(){
4885         var ae = this.getActionEl();
4886         ae.dom.style.display = 'none';
4887     },
4888
4889     /**
4890      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
4891      * by an implementing function.
4892      * @method
4893      * @param {EventObject} e
4894      */
4895     onTriggerClick : Roo.emptyFn
4896 });
4897  /*
4898  * Based on:
4899  * Ext JS Library 1.1.1
4900  * Copyright(c) 2006-2007, Ext JS, LLC.
4901  *
4902  * Originally Released Under LGPL - original licence link has changed is not relivant.
4903  *
4904  * Fork - LGPL
4905  * <script type="text/javascript">
4906  */
4907
4908
4909 /**
4910  * @class Roo.data.SortTypes
4911  * @singleton
4912  * Defines the default sorting (casting?) comparison functions used when sorting data.
4913  */
4914 Roo.data.SortTypes = {
4915     /**
4916      * Default sort that does nothing
4917      * @param {Mixed} s The value being converted
4918      * @return {Mixed} The comparison value
4919      */
4920     none : function(s){
4921         return s;
4922     },
4923     
4924     /**
4925      * The regular expression used to strip tags
4926      * @type {RegExp}
4927      * @property
4928      */
4929     stripTagsRE : /<\/?[^>]+>/gi,
4930     
4931     /**
4932      * Strips all HTML tags to sort on text only
4933      * @param {Mixed} s The value being converted
4934      * @return {String} The comparison value
4935      */
4936     asText : function(s){
4937         return String(s).replace(this.stripTagsRE, "");
4938     },
4939     
4940     /**
4941      * Strips all HTML tags to sort on text only - Case insensitive
4942      * @param {Mixed} s The value being converted
4943      * @return {String} The comparison value
4944      */
4945     asUCText : function(s){
4946         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4947     },
4948     
4949     /**
4950      * Case insensitive string
4951      * @param {Mixed} s The value being converted
4952      * @return {String} The comparison value
4953      */
4954     asUCString : function(s) {
4955         return String(s).toUpperCase();
4956     },
4957     
4958     /**
4959      * Date sorting
4960      * @param {Mixed} s The value being converted
4961      * @return {Number} The comparison value
4962      */
4963     asDate : function(s) {
4964         if(!s){
4965             return 0;
4966         }
4967         if(s instanceof Date){
4968             return s.getTime();
4969         }
4970         return Date.parse(String(s));
4971     },
4972     
4973     /**
4974      * Float sorting
4975      * @param {Mixed} s The value being converted
4976      * @return {Float} The comparison value
4977      */
4978     asFloat : function(s) {
4979         var val = parseFloat(String(s).replace(/,/g, ""));
4980         if(isNaN(val)) val = 0;
4981         return val;
4982     },
4983     
4984     /**
4985      * Integer sorting
4986      * @param {Mixed} s The value being converted
4987      * @return {Number} The comparison value
4988      */
4989     asInt : function(s) {
4990         var val = parseInt(String(s).replace(/,/g, ""));
4991         if(isNaN(val)) val = 0;
4992         return val;
4993     }
4994 };/*
4995  * Based on:
4996  * Ext JS Library 1.1.1
4997  * Copyright(c) 2006-2007, Ext JS, LLC.
4998  *
4999  * Originally Released Under LGPL - original licence link has changed is not relivant.
5000  *
5001  * Fork - LGPL
5002  * <script type="text/javascript">
5003  */
5004
5005 /**
5006 * @class Roo.data.Record
5007  * Instances of this class encapsulate both record <em>definition</em> information, and record
5008  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5009  * to access Records cached in an {@link Roo.data.Store} object.<br>
5010  * <p>
5011  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5012  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5013  * objects.<br>
5014  * <p>
5015  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5016  * @constructor
5017  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5018  * {@link #create}. The parameters are the same.
5019  * @param {Array} data An associative Array of data values keyed by the field name.
5020  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5021  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5022  * not specified an integer id is generated.
5023  */
5024 Roo.data.Record = function(data, id){
5025     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5026     this.data = data;
5027 };
5028
5029 /**
5030  * Generate a constructor for a specific record layout.
5031  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5032  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5033  * Each field definition object may contain the following properties: <ul>
5034  * <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,
5035  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5036  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5037  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5038  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5039  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5040  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5041  * this may be omitted.</p></li>
5042  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5043  * <ul><li>auto (Default, implies no conversion)</li>
5044  * <li>string</li>
5045  * <li>int</li>
5046  * <li>float</li>
5047  * <li>boolean</li>
5048  * <li>date</li></ul></p></li>
5049  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5050  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5051  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5052  * by the Reader into an object that will be stored in the Record. It is passed the
5053  * following parameters:<ul>
5054  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5055  * </ul></p></li>
5056  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5057  * </ul>
5058  * <br>usage:<br><pre><code>
5059 var TopicRecord = Roo.data.Record.create(
5060     {name: 'title', mapping: 'topic_title'},
5061     {name: 'author', mapping: 'username'},
5062     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5063     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5064     {name: 'lastPoster', mapping: 'user2'},
5065     {name: 'excerpt', mapping: 'post_text'}
5066 );
5067
5068 var myNewRecord = new TopicRecord({
5069     title: 'Do my job please',
5070     author: 'noobie',
5071     totalPosts: 1,
5072     lastPost: new Date(),
5073     lastPoster: 'Animal',
5074     excerpt: 'No way dude!'
5075 });
5076 myStore.add(myNewRecord);
5077 </code></pre>
5078  * @method create
5079  * @static
5080  */
5081 Roo.data.Record.create = function(o){
5082     var f = function(){
5083         f.superclass.constructor.apply(this, arguments);
5084     };
5085     Roo.extend(f, Roo.data.Record);
5086     var p = f.prototype;
5087     p.fields = new Roo.util.MixedCollection(false, function(field){
5088         return field.name;
5089     });
5090     for(var i = 0, len = o.length; i < len; i++){
5091         p.fields.add(new Roo.data.Field(o[i]));
5092     }
5093     f.getField = function(name){
5094         return p.fields.get(name);  
5095     };
5096     return f;
5097 };
5098
5099 Roo.data.Record.AUTO_ID = 1000;
5100 Roo.data.Record.EDIT = 'edit';
5101 Roo.data.Record.REJECT = 'reject';
5102 Roo.data.Record.COMMIT = 'commit';
5103
5104 Roo.data.Record.prototype = {
5105     /**
5106      * Readonly flag - true if this record has been modified.
5107      * @type Boolean
5108      */
5109     dirty : false,
5110     editing : false,
5111     error: null,
5112     modified: null,
5113
5114     // private
5115     join : function(store){
5116         this.store = store;
5117     },
5118
5119     /**
5120      * Set the named field to the specified value.
5121      * @param {String} name The name of the field to set.
5122      * @param {Object} value The value to set the field to.
5123      */
5124     set : function(name, value){
5125         if(this.data[name] == value){
5126             return;
5127         }
5128         this.dirty = true;
5129         if(!this.modified){
5130             this.modified = {};
5131         }
5132         if(typeof this.modified[name] == 'undefined'){
5133             this.modified[name] = this.data[name];
5134         }
5135         this.data[name] = value;
5136         if(!this.editing && this.store){
5137             this.store.afterEdit(this);
5138         }       
5139     },
5140
5141     /**
5142      * Get the value of the named field.
5143      * @param {String} name The name of the field to get the value of.
5144      * @return {Object} The value of the field.
5145      */
5146     get : function(name){
5147         return this.data[name]; 
5148     },
5149
5150     // private
5151     beginEdit : function(){
5152         this.editing = true;
5153         this.modified = {}; 
5154     },
5155
5156     // private
5157     cancelEdit : function(){
5158         this.editing = false;
5159         delete this.modified;
5160     },
5161
5162     // private
5163     endEdit : function(){
5164         this.editing = false;
5165         if(this.dirty && this.store){
5166             this.store.afterEdit(this);
5167         }
5168     },
5169
5170     /**
5171      * Usually called by the {@link Roo.data.Store} which owns the Record.
5172      * Rejects all changes made to the Record since either creation, or the last commit operation.
5173      * Modified fields are reverted to their original values.
5174      * <p>
5175      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5176      * of reject operations.
5177      */
5178     reject : function(){
5179         var m = this.modified;
5180         for(var n in m){
5181             if(typeof m[n] != "function"){
5182                 this.data[n] = m[n];
5183             }
5184         }
5185         this.dirty = false;
5186         delete this.modified;
5187         this.editing = false;
5188         if(this.store){
5189             this.store.afterReject(this);
5190         }
5191     },
5192
5193     /**
5194      * Usually called by the {@link Roo.data.Store} which owns the Record.
5195      * Commits all changes made to the Record since either creation, or the last commit operation.
5196      * <p>
5197      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5198      * of commit operations.
5199      */
5200     commit : function(){
5201         this.dirty = false;
5202         delete this.modified;
5203         this.editing = false;
5204         if(this.store){
5205             this.store.afterCommit(this);
5206         }
5207     },
5208
5209     // private
5210     hasError : function(){
5211         return this.error != null;
5212     },
5213
5214     // private
5215     clearError : function(){
5216         this.error = null;
5217     },
5218
5219     /**
5220      * Creates a copy of this record.
5221      * @param {String} id (optional) A new record id if you don't want to use this record's id
5222      * @return {Record}
5223      */
5224     copy : function(newId) {
5225         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
5226     }
5227 };/*
5228  * Based on:
5229  * Ext JS Library 1.1.1
5230  * Copyright(c) 2006-2007, Ext JS, LLC.
5231  *
5232  * Originally Released Under LGPL - original licence link has changed is not relivant.
5233  *
5234  * Fork - LGPL
5235  * <script type="text/javascript">
5236  */
5237
5238
5239
5240 /**
5241  * @class Roo.data.Store
5242  * @extends Roo.util.Observable
5243  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
5244  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
5245  * <p>
5246  * 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
5247  * has no knowledge of the format of the data returned by the Proxy.<br>
5248  * <p>
5249  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
5250  * instances from the data object. These records are cached and made available through accessor functions.
5251  * @constructor
5252  * Creates a new Store.
5253  * @param {Object} config A config object containing the objects needed for the Store to access data,
5254  * and read the data into Records.
5255  */
5256 Roo.data.Store = function(config){
5257     this.data = new Roo.util.MixedCollection(false);
5258     this.data.getKey = function(o){
5259         return o.id;
5260     };
5261     this.baseParams = {};
5262     // private
5263     this.paramNames = {
5264         "start" : "start",
5265         "limit" : "limit",
5266         "sort" : "sort",
5267         "dir" : "dir",
5268         "multisort" : "_multisort"
5269     };
5270
5271     if(config && config.data){
5272         this.inlineData = config.data;
5273         delete config.data;
5274     }
5275
5276     Roo.apply(this, config);
5277     
5278     if(this.reader){ // reader passed
5279         this.reader = Roo.factory(this.reader, Roo.data);
5280         this.reader.xmodule = this.xmodule || false;
5281         if(!this.recordType){
5282             this.recordType = this.reader.recordType;
5283         }
5284         if(this.reader.onMetaChange){
5285             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
5286         }
5287     }
5288
5289     if(this.recordType){
5290         this.fields = this.recordType.prototype.fields;
5291     }
5292     this.modified = [];
5293
5294     this.addEvents({
5295         /**
5296          * @event datachanged
5297          * Fires when the data cache has changed, and a widget which is using this Store
5298          * as a Record cache should refresh its view.
5299          * @param {Store} this
5300          */
5301         datachanged : true,
5302         /**
5303          * @event metachange
5304          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
5305          * @param {Store} this
5306          * @param {Object} meta The JSON metadata
5307          */
5308         metachange : true,
5309         /**
5310          * @event add
5311          * Fires when Records have been added to the Store
5312          * @param {Store} this
5313          * @param {Roo.data.Record[]} records The array of Records added
5314          * @param {Number} index The index at which the record(s) were added
5315          */
5316         add : true,
5317         /**
5318          * @event remove
5319          * Fires when a Record has been removed from the Store
5320          * @param {Store} this
5321          * @param {Roo.data.Record} record The Record that was removed
5322          * @param {Number} index The index at which the record was removed
5323          */
5324         remove : true,
5325         /**
5326          * @event update
5327          * Fires when a Record has been updated
5328          * @param {Store} this
5329          * @param {Roo.data.Record} record The Record that was updated
5330          * @param {String} operation The update operation being performed.  Value may be one of:
5331          * <pre><code>
5332  Roo.data.Record.EDIT
5333  Roo.data.Record.REJECT
5334  Roo.data.Record.COMMIT
5335          * </code></pre>
5336          */
5337         update : true,
5338         /**
5339          * @event clear
5340          * Fires when the data cache has been cleared.
5341          * @param {Store} this
5342          */
5343         clear : true,
5344         /**
5345          * @event beforeload
5346          * Fires before a request is made for a new data object.  If the beforeload handler returns false
5347          * the load action will be canceled.
5348          * @param {Store} this
5349          * @param {Object} options The loading options that were specified (see {@link #load} for details)
5350          */
5351         beforeload : true,
5352         /**
5353          * @event beforeloadadd
5354          * Fires after a new set of Records has been loaded.
5355          * @param {Store} this
5356          * @param {Roo.data.Record[]} records The Records that were loaded
5357          * @param {Object} options The loading options that were specified (see {@link #load} for details)
5358          */
5359         beforeloadadd : true,
5360         /**
5361          * @event load
5362          * Fires after a new set of Records has been loaded, before they are added to the store.
5363          * @param {Store} this
5364          * @param {Roo.data.Record[]} records The Records that were loaded
5365          * @param {Object} options The loading options that were specified (see {@link #load} for details)
5366          * @params {Object} return from reader
5367          */
5368         load : true,
5369         /**
5370          * @event loadexception
5371          * Fires if an exception occurs in the Proxy during loading.
5372          * Called with the signature of the Proxy's "loadexception" event.
5373          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
5374          * 
5375          * @param {Proxy} 
5376          * @param {Object} return from JsonData.reader() - success, totalRecords, records
5377          * @param {Object} load options 
5378          * @param {Object} jsonData from your request (normally this contains the Exception)
5379          */
5380         loadexception : true
5381     });
5382     
5383     if(this.proxy){
5384         this.proxy = Roo.factory(this.proxy, Roo.data);
5385         this.proxy.xmodule = this.xmodule || false;
5386         this.relayEvents(this.proxy,  ["loadexception"]);
5387     }
5388     this.sortToggle = {};
5389     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
5390
5391     Roo.data.Store.superclass.constructor.call(this);
5392
5393     if(this.inlineData){
5394         this.loadData(this.inlineData);
5395         delete this.inlineData;
5396     }
5397 };
5398
5399 Roo.extend(Roo.data.Store, Roo.util.Observable, {
5400      /**
5401     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
5402     * without a remote query - used by combo/forms at present.
5403     */
5404     
5405     /**
5406     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
5407     */
5408     /**
5409     * @cfg {Array} data Inline data to be loaded when the store is initialized.
5410     */
5411     /**
5412     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
5413     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
5414     */
5415     /**
5416     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
5417     * on any HTTP request
5418     */
5419     /**
5420     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
5421     */
5422     /**
5423     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
5424     */
5425     multiSort: false,
5426     /**
5427     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
5428     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
5429     */
5430     remoteSort : false,
5431
5432     /**
5433     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
5434      * loaded or when a record is removed. (defaults to false).
5435     */
5436     pruneModifiedRecords : false,
5437
5438     // private
5439     lastOptions : null,
5440
5441     /**
5442      * Add Records to the Store and fires the add event.
5443      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
5444      */
5445     add : function(records){
5446         records = [].concat(records);
5447         for(var i = 0, len = records.length; i < len; i++){
5448             records[i].join(this);
5449         }
5450         var index = this.data.length;
5451         this.data.addAll(records);
5452         this.fireEvent("add", this, records, index);
5453     },
5454
5455     /**
5456      * Remove a Record from the Store and fires the remove event.
5457      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
5458      */
5459     remove : function(record){
5460         var index = this.data.indexOf(record);
5461         this.data.removeAt(index);
5462         if(this.pruneModifiedRecords){
5463             this.modified.remove(record);
5464         }
5465         this.fireEvent("remove", this, record, index);
5466     },
5467
5468     /**
5469      * Remove all Records from the Store and fires the clear event.
5470      */
5471     removeAll : function(){
5472         this.data.clear();
5473         if(this.pruneModifiedRecords){
5474             this.modified = [];
5475         }
5476         this.fireEvent("clear", this);
5477     },
5478
5479     /**
5480      * Inserts Records to the Store at the given index and fires the add event.
5481      * @param {Number} index The start index at which to insert the passed Records.
5482      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
5483      */
5484     insert : function(index, records){
5485         records = [].concat(records);
5486         for(var i = 0, len = records.length; i < len; i++){
5487             this.data.insert(index, records[i]);
5488             records[i].join(this);
5489         }
5490         this.fireEvent("add", this, records, index);
5491     },
5492
5493     /**
5494      * Get the index within the cache of the passed Record.
5495      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
5496      * @return {Number} The index of the passed Record. Returns -1 if not found.
5497      */
5498     indexOf : function(record){
5499         return this.data.indexOf(record);
5500     },
5501
5502     /**
5503      * Get the index within the cache of the Record with the passed id.
5504      * @param {String} id The id of the Record to find.
5505      * @return {Number} The index of the Record. Returns -1 if not found.
5506      */
5507     indexOfId : function(id){
5508         return this.data.indexOfKey(id);
5509     },
5510
5511     /**
5512      * Get the Record with the specified id.
5513      * @param {String} id The id of the Record to find.
5514      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
5515      */
5516     getById : function(id){
5517         return this.data.key(id);
5518     },
5519
5520     /**
5521      * Get the Record at the specified index.
5522      * @param {Number} index The index of the Record to find.
5523      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5524      */
5525     getAt : function(index){
5526         return this.data.itemAt(index);
5527     },
5528
5529     /**
5530      * Returns a range of Records between specified indices.
5531      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5532      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5533      * @return {Roo.data.Record[]} An array of Records
5534      */
5535     getRange : function(start, end){
5536         return this.data.getRange(start, end);
5537     },
5538
5539     // private
5540     storeOptions : function(o){
5541         o = Roo.apply({}, o);
5542         delete o.callback;
5543         delete o.scope;
5544         this.lastOptions = o;
5545     },
5546
5547     /**
5548      * Loads the Record cache from the configured Proxy using the configured Reader.
5549      * <p>
5550      * If using remote paging, then the first load call must specify the <em>start</em>
5551      * and <em>limit</em> properties in the options.params property to establish the initial
5552      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5553      * <p>
5554      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5555      * and this call will return before the new data has been loaded. Perform any post-processing
5556      * in a callback function, or in a "load" event handler.</strong>
5557      * <p>
5558      * @param {Object} options An object containing properties which control loading options:<ul>
5559      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5560      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5561      * passed the following arguments:<ul>
5562      * <li>r : Roo.data.Record[]</li>
5563      * <li>options: Options object from the load call</li>
5564      * <li>success: Boolean success indicator</li></ul></li>
5565      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5566      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5567      * </ul>
5568      */
5569     load : function(options){
5570         options = options || {};
5571         if(this.fireEvent("beforeload", this, options) !== false){
5572             this.storeOptions(options);
5573             var p = Roo.apply(options.params || {}, this.baseParams);
5574             // if meta was not loaded from remote source.. try requesting it.
5575             if (!this.reader.metaFromRemote) {
5576                 p._requestMeta = 1;
5577             }
5578             if(this.sortInfo && this.remoteSort){
5579                 var pn = this.paramNames;
5580                 p[pn["sort"]] = this.sortInfo.field;
5581                 p[pn["dir"]] = this.sortInfo.direction;
5582             }
5583             if (this.multiSort) {
5584                 var pn = this.paramNames;
5585                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5586             }
5587             
5588             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5589         }
5590     },
5591
5592     /**
5593      * Reloads the Record cache from the configured Proxy using the configured Reader and
5594      * the options from the last load operation performed.
5595      * @param {Object} options (optional) An object containing properties which may override the options
5596      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5597      * the most recently used options are reused).
5598      */
5599     reload : function(options){
5600         this.load(Roo.applyIf(options||{}, this.lastOptions));
5601     },
5602
5603     // private
5604     // Called as a callback by the Reader during a load operation.
5605     loadRecords : function(o, options, success){
5606         if(!o || success === false){
5607             if(success !== false){
5608                 this.fireEvent("load", this, [], options, o);
5609             }
5610             if(options.callback){
5611                 options.callback.call(options.scope || this, [], options, false);
5612             }
5613             return;
5614         }
5615         // if data returned failure - throw an exception.
5616         if (o.success === false) {
5617             // show a message if no listener is registered.
5618             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5619                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5620             }
5621             // loadmask wil be hooked into this..
5622             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5623             return;
5624         }
5625         var r = o.records, t = o.totalRecords || r.length;
5626         
5627         this.fireEvent("beforeloadadd", this, r, options, o);
5628         
5629         if(!options || options.add !== true){
5630             if(this.pruneModifiedRecords){
5631                 this.modified = [];
5632             }
5633             for(var i = 0, len = r.length; i < len; i++){
5634                 r[i].join(this);
5635             }
5636             if(this.snapshot){
5637                 this.data = this.snapshot;
5638                 delete this.snapshot;
5639             }
5640             this.data.clear();
5641             this.data.addAll(r);
5642             this.totalLength = t;
5643             this.applySort();
5644             this.fireEvent("datachanged", this);
5645         }else{
5646             this.totalLength = Math.max(t, this.data.length+r.length);
5647             this.add(r);
5648         }
5649         this.fireEvent("load", this, r, options, o);
5650         if(options.callback){
5651             options.callback.call(options.scope || this, r, options, true);
5652         }
5653     },
5654
5655
5656     /**
5657      * Loads data from a passed data block. A Reader which understands the format of the data
5658      * must have been configured in the constructor.
5659      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5660      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5661      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5662      */
5663     loadData : function(o, append){
5664         var r = this.reader.readRecords(o);
5665         this.loadRecords(r, {add: append}, true);
5666     },
5667
5668     /**
5669      * Gets the number of cached records.
5670      * <p>
5671      * <em>If using paging, this may not be the total size of the dataset. If the data object
5672      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5673      * the data set size</em>
5674      */
5675     getCount : function(){
5676         return this.data.length || 0;
5677     },
5678
5679     /**
5680      * Gets the total number of records in the dataset as returned by the server.
5681      * <p>
5682      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5683      * the dataset size</em>
5684      */
5685     getTotalCount : function(){
5686         return this.totalLength || 0;
5687     },
5688
5689     /**
5690      * Returns the sort state of the Store as an object with two properties:
5691      * <pre><code>
5692  field {String} The name of the field by which the Records are sorted
5693  direction {String} The sort order, "ASC" or "DESC"
5694      * </code></pre>
5695      */
5696     getSortState : function(){
5697         return this.sortInfo;
5698     },
5699
5700     // private
5701     applySort : function(){
5702         if(this.sortInfo && !this.remoteSort){
5703             var s = this.sortInfo, f = s.field;
5704             var st = this.fields.get(f).sortType;
5705             var fn = function(r1, r2){
5706                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5707                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5708             };
5709             this.data.sort(s.direction, fn);
5710             if(this.snapshot && this.snapshot != this.data){
5711                 this.snapshot.sort(s.direction, fn);
5712             }
5713         }
5714     },
5715
5716     /**
5717      * Sets the default sort column and order to be used by the next load operation.
5718      * @param {String} fieldName The name of the field to sort by.
5719      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5720      */
5721     setDefaultSort : function(field, dir){
5722         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5723     },
5724
5725     /**
5726      * Sort the Records.
5727      * If remote sorting is used, the sort is performed on the server, and the cache is
5728      * reloaded. If local sorting is used, the cache is sorted internally.
5729      * @param {String} fieldName The name of the field to sort by.
5730      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5731      */
5732     sort : function(fieldName, dir){
5733         var f = this.fields.get(fieldName);
5734         if(!dir){
5735             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5736             
5737             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5738                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5739             }else{
5740                 dir = f.sortDir;
5741             }
5742         }
5743         this.sortToggle[f.name] = dir;
5744         this.sortInfo = {field: f.name, direction: dir};
5745         if(!this.remoteSort){
5746             this.applySort();
5747             this.fireEvent("datachanged", this);
5748         }else{
5749             this.load(this.lastOptions);
5750         }
5751     },
5752
5753     /**
5754      * Calls the specified function for each of the Records in the cache.
5755      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5756      * Returning <em>false</em> aborts and exits the iteration.
5757      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5758      */
5759     each : function(fn, scope){
5760         this.data.each(fn, scope);
5761     },
5762
5763     /**
5764      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5765      * (e.g., during paging).
5766      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5767      */
5768     getModifiedRecords : function(){
5769         return this.modified;
5770     },
5771
5772     // private
5773     createFilterFn : function(property, value, anyMatch){
5774         if(!value.exec){ // not a regex
5775             value = String(value);
5776             if(value.length == 0){
5777                 return false;
5778             }
5779             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5780         }
5781         return function(r){
5782             return value.test(r.data[property]);
5783         };
5784     },
5785
5786     /**
5787      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5788      * @param {String} property A field on your records
5789      * @param {Number} start The record index to start at (defaults to 0)
5790      * @param {Number} end The last record index to include (defaults to length - 1)
5791      * @return {Number} The sum
5792      */
5793     sum : function(property, start, end){
5794         var rs = this.data.items, v = 0;
5795         start = start || 0;
5796         end = (end || end === 0) ? end : rs.length-1;
5797
5798         for(var i = start; i <= end; i++){
5799             v += (rs[i].data[property] || 0);
5800         }
5801         return v;
5802     },
5803
5804     /**
5805      * Filter the records by a specified property.
5806      * @param {String} field A field on your records
5807      * @param {String/RegExp} value Either a string that the field
5808      * should start with or a RegExp to test against the field
5809      * @param {Boolean} anyMatch True to match any part not just the beginning
5810      */
5811     filter : function(property, value, anyMatch){
5812         var fn = this.createFilterFn(property, value, anyMatch);
5813         return fn ? this.filterBy(fn) : this.clearFilter();
5814     },
5815
5816     /**
5817      * Filter by a function. The specified function will be called with each
5818      * record in this data source. If the function returns true the record is included,
5819      * otherwise it is filtered.
5820      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5821      * @param {Object} scope (optional) The scope of the function (defaults to this)
5822      */
5823     filterBy : function(fn, scope){
5824         this.snapshot = this.snapshot || this.data;
5825         this.data = this.queryBy(fn, scope||this);
5826         this.fireEvent("datachanged", this);
5827     },
5828
5829     /**
5830      * Query the records by a specified property.
5831      * @param {String} field A field on your records
5832      * @param {String/RegExp} value Either a string that the field
5833      * should start with or a RegExp to test against the field
5834      * @param {Boolean} anyMatch True to match any part not just the beginning
5835      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5836      */
5837     query : function(property, value, anyMatch){
5838         var fn = this.createFilterFn(property, value, anyMatch);
5839         return fn ? this.queryBy(fn) : this.data.clone();
5840     },
5841
5842     /**
5843      * Query by a function. The specified function will be called with each
5844      * record in this data source. If the function returns true the record is included
5845      * in the results.
5846      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5847      * @param {Object} scope (optional) The scope of the function (defaults to this)
5848       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5849      **/
5850     queryBy : function(fn, scope){
5851         var data = this.snapshot || this.data;
5852         return data.filterBy(fn, scope||this);
5853     },
5854
5855     /**
5856      * Collects unique values for a particular dataIndex from this store.
5857      * @param {String} dataIndex The property to collect
5858      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5859      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5860      * @return {Array} An array of the unique values
5861      **/
5862     collect : function(dataIndex, allowNull, bypassFilter){
5863         var d = (bypassFilter === true && this.snapshot) ?
5864                 this.snapshot.items : this.data.items;
5865         var v, sv, r = [], l = {};
5866         for(var i = 0, len = d.length; i < len; i++){
5867             v = d[i].data[dataIndex];
5868             sv = String(v);
5869             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5870                 l[sv] = true;
5871                 r[r.length] = v;
5872             }
5873         }
5874         return r;
5875     },
5876
5877     /**
5878      * Revert to a view of the Record cache with no filtering applied.
5879      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5880      */
5881     clearFilter : function(suppressEvent){
5882         if(this.snapshot && this.snapshot != this.data){
5883             this.data = this.snapshot;
5884             delete this.snapshot;
5885             if(suppressEvent !== true){
5886                 this.fireEvent("datachanged", this);
5887             }
5888         }
5889     },
5890
5891     // private
5892     afterEdit : function(record){
5893         if(this.modified.indexOf(record) == -1){
5894             this.modified.push(record);
5895         }
5896         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5897     },
5898     
5899     // private
5900     afterReject : function(record){
5901         this.modified.remove(record);
5902         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5903     },
5904
5905     // private
5906     afterCommit : function(record){
5907         this.modified.remove(record);
5908         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5909     },
5910
5911     /**
5912      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5913      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5914      */
5915     commitChanges : function(){
5916         var m = this.modified.slice(0);
5917         this.modified = [];
5918         for(var i = 0, len = m.length; i < len; i++){
5919             m[i].commit();
5920         }
5921     },
5922
5923     /**
5924      * Cancel outstanding changes on all changed records.
5925      */
5926     rejectChanges : function(){
5927         var m = this.modified.slice(0);
5928         this.modified = [];
5929         for(var i = 0, len = m.length; i < len; i++){
5930             m[i].reject();
5931         }
5932     },
5933
5934     onMetaChange : function(meta, rtype, o){
5935         this.recordType = rtype;
5936         this.fields = rtype.prototype.fields;
5937         delete this.snapshot;
5938         this.sortInfo = meta.sortInfo || this.sortInfo;
5939         this.modified = [];
5940         this.fireEvent('metachange', this, this.reader.meta);
5941     }
5942 });/*
5943  * Based on:
5944  * Ext JS Library 1.1.1
5945  * Copyright(c) 2006-2007, Ext JS, LLC.
5946  *
5947  * Originally Released Under LGPL - original licence link has changed is not relivant.
5948  *
5949  * Fork - LGPL
5950  * <script type="text/javascript">
5951  */
5952
5953 /**
5954  * @class Roo.data.SimpleStore
5955  * @extends Roo.data.Store
5956  * Small helper class to make creating Stores from Array data easier.
5957  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5958  * @cfg {Array} fields An array of field definition objects, or field name strings.
5959  * @cfg {Array} data The multi-dimensional array of data
5960  * @constructor
5961  * @param {Object} config
5962  */
5963 Roo.data.SimpleStore = function(config){
5964     Roo.data.SimpleStore.superclass.constructor.call(this, {
5965         isLocal : true,
5966         reader: new Roo.data.ArrayReader({
5967                 id: config.id
5968             },
5969             Roo.data.Record.create(config.fields)
5970         ),
5971         proxy : new Roo.data.MemoryProxy(config.data)
5972     });
5973     this.load();
5974 };
5975 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5976  * Based on:
5977  * Ext JS Library 1.1.1
5978  * Copyright(c) 2006-2007, Ext JS, LLC.
5979  *
5980  * Originally Released Under LGPL - original licence link has changed is not relivant.
5981  *
5982  * Fork - LGPL
5983  * <script type="text/javascript">
5984  */
5985
5986 /**
5987 /**
5988  * @extends Roo.data.Store
5989  * @class Roo.data.JsonStore
5990  * Small helper class to make creating Stores for JSON data easier. <br/>
5991 <pre><code>
5992 var store = new Roo.data.JsonStore({
5993     url: 'get-images.php',
5994     root: 'images',
5995     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5996 });
5997 </code></pre>
5998  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5999  * JsonReader and HttpProxy (unless inline data is provided).</b>
6000  * @cfg {Array} fields An array of field definition objects, or field name strings.
6001  * @constructor
6002  * @param {Object} config
6003  */
6004 Roo.data.JsonStore = function(c){
6005     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6006         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6007         reader: new Roo.data.JsonReader(c, c.fields)
6008     }));
6009 };
6010 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6011  * Based on:
6012  * Ext JS Library 1.1.1
6013  * Copyright(c) 2006-2007, Ext JS, LLC.
6014  *
6015  * Originally Released Under LGPL - original licence link has changed is not relivant.
6016  *
6017  * Fork - LGPL
6018  * <script type="text/javascript">
6019  */
6020
6021  
6022 Roo.data.Field = function(config){
6023     if(typeof config == "string"){
6024         config = {name: config};
6025     }
6026     Roo.apply(this, config);
6027     
6028     if(!this.type){
6029         this.type = "auto";
6030     }
6031     
6032     var st = Roo.data.SortTypes;
6033     // named sortTypes are supported, here we look them up
6034     if(typeof this.sortType == "string"){
6035         this.sortType = st[this.sortType];
6036     }
6037     
6038     // set default sortType for strings and dates
6039     if(!this.sortType){
6040         switch(this.type){
6041             case "string":
6042                 this.sortType = st.asUCString;
6043                 break;
6044             case "date":
6045                 this.sortType = st.asDate;
6046                 break;
6047             default:
6048                 this.sortType = st.none;
6049         }
6050     }
6051
6052     // define once
6053     var stripRe = /[\$,%]/g;
6054
6055     // prebuilt conversion function for this field, instead of
6056     // switching every time we're reading a value
6057     if(!this.convert){
6058         var cv, dateFormat = this.dateFormat;
6059         switch(this.type){
6060             case "":
6061             case "auto":
6062             case undefined:
6063                 cv = function(v){ return v; };
6064                 break;
6065             case "string":
6066                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6067                 break;
6068             case "int":
6069                 cv = function(v){
6070                     return v !== undefined && v !== null && v !== '' ?
6071                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6072                     };
6073                 break;
6074             case "float":
6075                 cv = function(v){
6076                     return v !== undefined && v !== null && v !== '' ?
6077                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6078                     };
6079                 break;
6080             case "bool":
6081             case "boolean":
6082                 cv = function(v){ return v === true || v === "true" || v == 1; };
6083                 break;
6084             case "date":
6085                 cv = function(v){
6086                     if(!v){
6087                         return '';
6088                     }
6089                     if(v instanceof Date){
6090                         return v;
6091                     }
6092                     if(dateFormat){
6093                         if(dateFormat == "timestamp"){
6094                             return new Date(v*1000);
6095                         }
6096                         return Date.parseDate(v, dateFormat);
6097                     }
6098                     var parsed = Date.parse(v);
6099                     return parsed ? new Date(parsed) : null;
6100                 };
6101              break;
6102             
6103         }
6104         this.convert = cv;
6105     }
6106 };
6107
6108 Roo.data.Field.prototype = {
6109     dateFormat: null,
6110     defaultValue: "",
6111     mapping: null,
6112     sortType : null,
6113     sortDir : "ASC"
6114 };/*
6115  * Based on:
6116  * Ext JS Library 1.1.1
6117  * Copyright(c) 2006-2007, Ext JS, LLC.
6118  *
6119  * Originally Released Under LGPL - original licence link has changed is not relivant.
6120  *
6121  * Fork - LGPL
6122  * <script type="text/javascript">
6123  */
6124  
6125 // Base class for reading structured data from a data source.  This class is intended to be
6126 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6127
6128 /**
6129  * @class Roo.data.DataReader
6130  * Base class for reading structured data from a data source.  This class is intended to be
6131  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6132  */
6133
6134 Roo.data.DataReader = function(meta, recordType){
6135     
6136     this.meta = meta;
6137     
6138     this.recordType = recordType instanceof Array ? 
6139         Roo.data.Record.create(recordType) : recordType;
6140 };
6141
6142 Roo.data.DataReader.prototype = {
6143      /**
6144      * Create an empty record
6145      * @param {Object} data (optional) - overlay some values
6146      * @return {Roo.data.Record} record created.
6147      */
6148     newRow :  function(d) {
6149         var da =  {};
6150         this.recordType.prototype.fields.each(function(c) {
6151             switch( c.type) {
6152                 case 'int' : da[c.name] = 0; break;
6153                 case 'date' : da[c.name] = new Date(); break;
6154                 case 'float' : da[c.name] = 0.0; break;
6155                 case 'boolean' : da[c.name] = false; break;
6156                 default : da[c.name] = ""; break;
6157             }
6158             
6159         });
6160         return new this.recordType(Roo.apply(da, d));
6161     }
6162     
6163 };/*
6164  * Based on:
6165  * Ext JS Library 1.1.1
6166  * Copyright(c) 2006-2007, Ext JS, LLC.
6167  *
6168  * Originally Released Under LGPL - original licence link has changed is not relivant.
6169  *
6170  * Fork - LGPL
6171  * <script type="text/javascript">
6172  */
6173
6174 /**
6175  * @class Roo.data.DataProxy
6176  * @extends Roo.data.Observable
6177  * This class is an abstract base class for implementations which provide retrieval of
6178  * unformatted data objects.<br>
6179  * <p>
6180  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6181  * (of the appropriate type which knows how to parse the data object) to provide a block of
6182  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6183  * <p>
6184  * Custom implementations must implement the load method as described in
6185  * {@link Roo.data.HttpProxy#load}.
6186  */
6187 Roo.data.DataProxy = function(){
6188     this.addEvents({
6189         /**
6190          * @event beforeload
6191          * Fires before a network request is made to retrieve a data object.
6192          * @param {Object} This DataProxy object.
6193          * @param {Object} params The params parameter to the load function.
6194          */
6195         beforeload : true,
6196         /**
6197          * @event load
6198          * Fires before the load method's callback is called.
6199          * @param {Object} This DataProxy object.
6200          * @param {Object} o The data object.
6201          * @param {Object} arg The callback argument object passed to the load function.
6202          */
6203         load : true,
6204         /**
6205          * @event loadexception
6206          * Fires if an Exception occurs during data retrieval.
6207          * @param {Object} This DataProxy object.
6208          * @param {Object} o The data object.
6209          * @param {Object} arg The callback argument object passed to the load function.
6210          * @param {Object} e The Exception.
6211          */
6212         loadexception : true
6213     });
6214     Roo.data.DataProxy.superclass.constructor.call(this);
6215 };
6216
6217 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
6218
6219     /**
6220      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
6221      */
6222 /*
6223  * Based on:
6224  * Ext JS Library 1.1.1
6225  * Copyright(c) 2006-2007, Ext JS, LLC.
6226  *
6227  * Originally Released Under LGPL - original licence link has changed is not relivant.
6228  *
6229  * Fork - LGPL
6230  * <script type="text/javascript">
6231  */
6232 /**
6233  * @class Roo.data.MemoryProxy
6234  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
6235  * to the Reader when its load method is called.
6236  * @constructor
6237  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
6238  */
6239 Roo.data.MemoryProxy = function(data){
6240     if (data.data) {
6241         data = data.data;
6242     }
6243     Roo.data.MemoryProxy.superclass.constructor.call(this);
6244     this.data = data;
6245 };
6246
6247 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
6248     /**
6249      * Load data from the requested source (in this case an in-memory
6250      * data object passed to the constructor), read the data object into
6251      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6252      * process that block using the passed callback.
6253      * @param {Object} params This parameter is not used by the MemoryProxy class.
6254      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6255      * object into a block of Roo.data.Records.
6256      * @param {Function} callback The function into which to pass the block of Roo.data.records.
6257      * The function must be passed <ul>
6258      * <li>The Record block object</li>
6259      * <li>The "arg" argument from the load function</li>
6260      * <li>A boolean success indicator</li>
6261      * </ul>
6262      * @param {Object} scope The scope in which to call the callback
6263      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6264      */
6265     load : function(params, reader, callback, scope, arg){
6266         params = params || {};
6267         var result;
6268         try {
6269             result = reader.readRecords(this.data);
6270         }catch(e){
6271             this.fireEvent("loadexception", this, arg, null, e);
6272             callback.call(scope, null, arg, false);
6273             return;
6274         }
6275         callback.call(scope, result, arg, true);
6276     },
6277     
6278     // private
6279     update : function(params, records){
6280         
6281     }
6282 });/*
6283  * Based on:
6284  * Ext JS Library 1.1.1
6285  * Copyright(c) 2006-2007, Ext JS, LLC.
6286  *
6287  * Originally Released Under LGPL - original licence link has changed is not relivant.
6288  *
6289  * Fork - LGPL
6290  * <script type="text/javascript">
6291  */
6292 /**
6293  * @class Roo.data.HttpProxy
6294  * @extends Roo.data.DataProxy
6295  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
6296  * configured to reference a certain URL.<br><br>
6297  * <p>
6298  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
6299  * from which the running page was served.<br><br>
6300  * <p>
6301  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
6302  * <p>
6303  * Be aware that to enable the browser to parse an XML document, the server must set
6304  * the Content-Type header in the HTTP response to "text/xml".
6305  * @constructor
6306  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
6307  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
6308  * will be used to make the request.
6309  */
6310 Roo.data.HttpProxy = function(conn){
6311     Roo.data.HttpProxy.superclass.constructor.call(this);
6312     // is conn a conn config or a real conn?
6313     this.conn = conn;
6314     this.useAjax = !conn || !conn.events;
6315   
6316 };
6317
6318 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
6319     // thse are take from connection...
6320     
6321     /**
6322      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
6323      */
6324     /**
6325      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
6326      * extra parameters to each request made by this object. (defaults to undefined)
6327      */
6328     /**
6329      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
6330      *  to each request made by this object. (defaults to undefined)
6331      */
6332     /**
6333      * @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)
6334      */
6335     /**
6336      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
6337      */
6338      /**
6339      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
6340      * @type Boolean
6341      */
6342   
6343
6344     /**
6345      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
6346      * @type Boolean
6347      */
6348     /**
6349      * Return the {@link Roo.data.Connection} object being used by this Proxy.
6350      * @return {Connection} The Connection object. This object may be used to subscribe to events on
6351      * a finer-grained basis than the DataProxy events.
6352      */
6353     getConnection : function(){
6354         return this.useAjax ? Roo.Ajax : this.conn;
6355     },
6356
6357     /**
6358      * Load data from the configured {@link Roo.data.Connection}, read the data object into
6359      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
6360      * process that block using the passed callback.
6361      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6362      * for the request to the remote server.
6363      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6364      * object into a block of Roo.data.Records.
6365      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6366      * The function must be passed <ul>
6367      * <li>The Record block object</li>
6368      * <li>The "arg" argument from the load function</li>
6369      * <li>A boolean success indicator</li>
6370      * </ul>
6371      * @param {Object} scope The scope in which to call the callback
6372      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6373      */
6374     load : function(params, reader, callback, scope, arg){
6375         if(this.fireEvent("beforeload", this, params) !== false){
6376             var  o = {
6377                 params : params || {},
6378                 request: {
6379                     callback : callback,
6380                     scope : scope,
6381                     arg : arg
6382                 },
6383                 reader: reader,
6384                 callback : this.loadResponse,
6385                 scope: this
6386             };
6387             if(this.useAjax){
6388                 Roo.applyIf(o, this.conn);
6389                 if(this.activeRequest){
6390                     Roo.Ajax.abort(this.activeRequest);
6391                 }
6392                 this.activeRequest = Roo.Ajax.request(o);
6393             }else{
6394                 this.conn.request(o);
6395             }
6396         }else{
6397             callback.call(scope||this, null, arg, false);
6398         }
6399     },
6400
6401     // private
6402     loadResponse : function(o, success, response){
6403         delete this.activeRequest;
6404         if(!success){
6405             this.fireEvent("loadexception", this, o, response);
6406             o.request.callback.call(o.request.scope, null, o.request.arg, false);
6407             return;
6408         }
6409         var result;
6410         try {
6411             result = o.reader.read(response);
6412         }catch(e){
6413             this.fireEvent("loadexception", this, o, response, e);
6414             o.request.callback.call(o.request.scope, null, o.request.arg, false);
6415             return;
6416         }
6417         
6418         this.fireEvent("load", this, o, o.request.arg);
6419         o.request.callback.call(o.request.scope, result, o.request.arg, true);
6420     },
6421
6422     // private
6423     update : function(dataSet){
6424
6425     },
6426
6427     // private
6428     updateResponse : function(dataSet){
6429
6430     }
6431 });/*
6432  * Based on:
6433  * Ext JS Library 1.1.1
6434  * Copyright(c) 2006-2007, Ext JS, LLC.
6435  *
6436  * Originally Released Under LGPL - original licence link has changed is not relivant.
6437  *
6438  * Fork - LGPL
6439  * <script type="text/javascript">
6440  */
6441
6442 /**
6443  * @class Roo.data.ScriptTagProxy
6444  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
6445  * other than the originating domain of the running page.<br><br>
6446  * <p>
6447  * <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
6448  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
6449  * <p>
6450  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
6451  * source code that is used as the source inside a &lt;script> tag.<br><br>
6452  * <p>
6453  * In order for the browser to process the returned data, the server must wrap the data object
6454  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
6455  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
6456  * depending on whether the callback name was passed:
6457  * <p>
6458  * <pre><code>
6459 boolean scriptTag = false;
6460 String cb = request.getParameter("callback");
6461 if (cb != null) {
6462     scriptTag = true;
6463     response.setContentType("text/javascript");
6464 } else {
6465     response.setContentType("application/x-json");
6466 }
6467 Writer out = response.getWriter();
6468 if (scriptTag) {
6469     out.write(cb + "(");
6470 }
6471 out.print(dataBlock.toJsonString());
6472 if (scriptTag) {
6473     out.write(");");
6474 }
6475 </pre></code>
6476  *
6477  * @constructor
6478  * @param {Object} config A configuration object.
6479  */
6480 Roo.data.ScriptTagProxy = function(config){
6481     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
6482     Roo.apply(this, config);
6483     this.head = document.getElementsByTagName("head")[0];
6484 };
6485
6486 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
6487
6488 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
6489     /**
6490      * @cfg {String} url The URL from which to request the data object.
6491      */
6492     /**
6493      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
6494      */
6495     timeout : 30000,
6496     /**
6497      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
6498      * the server the name of the callback function set up by the load call to process the returned data object.
6499      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
6500      * javascript output which calls this named function passing the data object as its only parameter.
6501      */
6502     callbackParam : "callback",
6503     /**
6504      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6505      * name to the request.
6506      */
6507     nocache : true,
6508
6509     /**
6510      * Load data from the configured URL, read the data object into
6511      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6512      * process that block using the passed callback.
6513      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6514      * for the request to the remote server.
6515      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6516      * object into a block of Roo.data.Records.
6517      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6518      * The function must be passed <ul>
6519      * <li>The Record block object</li>
6520      * <li>The "arg" argument from the load function</li>
6521      * <li>A boolean success indicator</li>
6522      * </ul>
6523      * @param {Object} scope The scope in which to call the callback
6524      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6525      */
6526     load : function(params, reader, callback, scope, arg){
6527         if(this.fireEvent("beforeload", this, params) !== false){
6528
6529             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6530
6531             var url = this.url;
6532             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6533             if(this.nocache){
6534                 url += "&_dc=" + (new Date().getTime());
6535             }
6536             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6537             var trans = {
6538                 id : transId,
6539                 cb : "stcCallback"+transId,
6540                 scriptId : "stcScript"+transId,
6541                 params : params,
6542                 arg : arg,
6543                 url : url,
6544                 callback : callback,
6545                 scope : scope,
6546                 reader : reader
6547             };
6548             var conn = this;
6549
6550             window[trans.cb] = function(o){
6551                 conn.handleResponse(o, trans);
6552             };
6553
6554             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6555
6556             if(this.autoAbort !== false){
6557                 this.abort();
6558             }
6559
6560             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6561
6562             var script = document.createElement("script");
6563             script.setAttribute("src", url);
6564             script.setAttribute("type", "text/javascript");
6565             script.setAttribute("id", trans.scriptId);
6566             this.head.appendChild(script);
6567
6568             this.trans = trans;
6569         }else{
6570             callback.call(scope||this, null, arg, false);
6571         }
6572     },
6573
6574     // private
6575     isLoading : function(){
6576         return this.trans ? true : false;
6577     },
6578
6579     /**
6580      * Abort the current server request.
6581      */
6582     abort : function(){
6583         if(this.isLoading()){
6584             this.destroyTrans(this.trans);
6585         }
6586     },
6587
6588     // private
6589     destroyTrans : function(trans, isLoaded){
6590         this.head.removeChild(document.getElementById(trans.scriptId));
6591         clearTimeout(trans.timeoutId);
6592         if(isLoaded){
6593             window[trans.cb] = undefined;
6594             try{
6595                 delete window[trans.cb];
6596             }catch(e){}
6597         }else{
6598             // if hasn't been loaded, wait for load to remove it to prevent script error
6599             window[trans.cb] = function(){
6600                 window[trans.cb] = undefined;
6601                 try{
6602                     delete window[trans.cb];
6603                 }catch(e){}
6604             };
6605         }
6606     },
6607
6608     // private
6609     handleResponse : function(o, trans){
6610         this.trans = false;
6611         this.destroyTrans(trans, true);
6612         var result;
6613         try {
6614             result = trans.reader.readRecords(o);
6615         }catch(e){
6616             this.fireEvent("loadexception", this, o, trans.arg, e);
6617             trans.callback.call(trans.scope||window, null, trans.arg, false);
6618             return;
6619         }
6620         this.fireEvent("load", this, o, trans.arg);
6621         trans.callback.call(trans.scope||window, result, trans.arg, true);
6622     },
6623
6624     // private
6625     handleFailure : function(trans){
6626         this.trans = false;
6627         this.destroyTrans(trans, false);
6628         this.fireEvent("loadexception", this, null, trans.arg);
6629         trans.callback.call(trans.scope||window, null, trans.arg, false);
6630     }
6631 });/*
6632  * Based on:
6633  * Ext JS Library 1.1.1
6634  * Copyright(c) 2006-2007, Ext JS, LLC.
6635  *
6636  * Originally Released Under LGPL - original licence link has changed is not relivant.
6637  *
6638  * Fork - LGPL
6639  * <script type="text/javascript">
6640  */
6641
6642 /**
6643  * @class Roo.data.JsonReader
6644  * @extends Roo.data.DataReader
6645  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6646  * based on mappings in a provided Roo.data.Record constructor.
6647  * 
6648  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6649  * in the reply previously. 
6650  * 
6651  * <p>
6652  * Example code:
6653  * <pre><code>
6654 var RecordDef = Roo.data.Record.create([
6655     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6656     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6657 ]);
6658 var myReader = new Roo.data.JsonReader({
6659     totalProperty: "results",    // The property which contains the total dataset size (optional)
6660     root: "rows",                // The property which contains an Array of row objects
6661     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6662 }, RecordDef);
6663 </code></pre>
6664  * <p>
6665  * This would consume a JSON file like this:
6666  * <pre><code>
6667 { 'results': 2, 'rows': [
6668     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6669     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6670 }
6671 </code></pre>
6672  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6673  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6674  * paged from the remote server.
6675  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6676  * @cfg {String} root name of the property which contains the Array of row objects.
6677  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6678  * @constructor
6679  * Create a new JsonReader
6680  * @param {Object} meta Metadata configuration options
6681  * @param {Object} recordType Either an Array of field definition objects,
6682  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6683  */
6684 Roo.data.JsonReader = function(meta, recordType){
6685     
6686     meta = meta || {};
6687     // set some defaults:
6688     Roo.applyIf(meta, {
6689         totalProperty: 'total',
6690         successProperty : 'success',
6691         root : 'data',
6692         id : 'id'
6693     });
6694     
6695     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6696 };
6697 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6698     
6699     /**
6700      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6701      * Used by Store query builder to append _requestMeta to params.
6702      * 
6703      */
6704     metaFromRemote : false,
6705     /**
6706      * This method is only used by a DataProxy which has retrieved data from a remote server.
6707      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6708      * @return {Object} data A data block which is used by an Roo.data.Store object as
6709      * a cache of Roo.data.Records.
6710      */
6711     read : function(response){
6712         var json = response.responseText;
6713        
6714         var o = /* eval:var:o */ eval("("+json+")");
6715         if(!o) {
6716             throw {message: "JsonReader.read: Json object not found"};
6717         }
6718         
6719         if(o.metaData){
6720             
6721             delete this.ef;
6722             this.metaFromRemote = true;
6723             this.meta = o.metaData;
6724             this.recordType = Roo.data.Record.create(o.metaData.fields);
6725             this.onMetaChange(this.meta, this.recordType, o);
6726         }
6727         return this.readRecords(o);
6728     },
6729
6730     // private function a store will implement
6731     onMetaChange : function(meta, recordType, o){
6732
6733     },
6734
6735     /**
6736          * @ignore
6737          */
6738     simpleAccess: function(obj, subsc) {
6739         return obj[subsc];
6740     },
6741
6742         /**
6743          * @ignore
6744          */
6745     getJsonAccessor: function(){
6746         var re = /[\[\.]/;
6747         return function(expr) {
6748             try {
6749                 return(re.test(expr))
6750                     ? new Function("obj", "return obj." + expr)
6751                     : function(obj){
6752                         return obj[expr];
6753                     };
6754             } catch(e){}
6755             return Roo.emptyFn;
6756         };
6757     }(),
6758
6759     /**
6760      * Create a data block containing Roo.data.Records from an XML document.
6761      * @param {Object} o An object which contains an Array of row objects in the property specified
6762      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6763      * which contains the total size of the dataset.
6764      * @return {Object} data A data block which is used by an Roo.data.Store object as
6765      * a cache of Roo.data.Records.
6766      */
6767     readRecords : function(o){
6768         /**
6769          * After any data loads, the raw JSON data is available for further custom processing.
6770          * @type Object
6771          */
6772         this.o = o;
6773         var s = this.meta, Record = this.recordType,
6774             f = Record.prototype.fields, fi = f.items, fl = f.length;
6775
6776 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6777         if (!this.ef) {
6778             if(s.totalProperty) {
6779                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6780                 }
6781                 if(s.successProperty) {
6782                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6783                 }
6784                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6785                 if (s.id) {
6786                         var g = this.getJsonAccessor(s.id);
6787                         this.getId = function(rec) {
6788                                 var r = g(rec);
6789                                 return (r === undefined || r === "") ? null : r;
6790                         };
6791                 } else {
6792                         this.getId = function(){return null;};
6793                 }
6794             this.ef = [];
6795             for(var jj = 0; jj < fl; jj++){
6796                 f = fi[jj];
6797                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6798                 this.ef[jj] = this.getJsonAccessor(map);
6799             }
6800         }
6801
6802         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6803         if(s.totalProperty){
6804             var vt = parseInt(this.getTotal(o), 10);
6805             if(!isNaN(vt)){
6806                 totalRecords = vt;
6807             }
6808         }
6809         if(s.successProperty){
6810             var vs = this.getSuccess(o);
6811             if(vs === false || vs === 'false'){
6812                 success = false;
6813             }
6814         }
6815         var records = [];
6816             for(var i = 0; i < c; i++){
6817                     var n = root[i];
6818                 var values = {};
6819                 var id = this.getId(n);
6820                 for(var j = 0; j < fl; j++){
6821                     f = fi[j];
6822                 var v = this.ef[j](n);
6823                 if (!f.convert) {
6824                     Roo.log('missing convert for ' + f.name);
6825                     Roo.log(f);
6826                     continue;
6827                 }
6828                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6829                 }
6830                 var record = new Record(values, id);
6831                 record.json = n;
6832                 records[i] = record;
6833             }
6834             return {
6835             raw : o,
6836                 success : success,
6837                 records : records,
6838                 totalRecords : totalRecords
6839             };
6840     }
6841 });/*
6842  * Based on:
6843  * Ext JS Library 1.1.1
6844  * Copyright(c) 2006-2007, Ext JS, LLC.
6845  *
6846  * Originally Released Under LGPL - original licence link has changed is not relivant.
6847  *
6848  * Fork - LGPL
6849  * <script type="text/javascript">
6850  */
6851
6852 /**
6853  * @class Roo.data.ArrayReader
6854  * @extends Roo.data.DataReader
6855  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6856  * Each element of that Array represents a row of data fields. The
6857  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6858  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6859  * <p>
6860  * Example code:.
6861  * <pre><code>
6862 var RecordDef = Roo.data.Record.create([
6863     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6864     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6865 ]);
6866 var myReader = new Roo.data.ArrayReader({
6867     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6868 }, RecordDef);
6869 </code></pre>
6870  * <p>
6871  * This would consume an Array like this:
6872  * <pre><code>
6873 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6874   </code></pre>
6875  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6876  * @constructor
6877  * Create a new JsonReader
6878  * @param {Object} meta Metadata configuration options.
6879  * @param {Object} recordType Either an Array of field definition objects
6880  * as specified to {@link Roo.data.Record#create},
6881  * or an {@link Roo.data.Record} object
6882  * created using {@link Roo.data.Record#create}.
6883  */
6884 Roo.data.ArrayReader = function(meta, recordType){
6885     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6886 };
6887
6888 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6889     /**
6890      * Create a data block containing Roo.data.Records from an XML document.
6891      * @param {Object} o An Array of row objects which represents the dataset.
6892      * @return {Object} data A data block which is used by an Roo.data.Store object as
6893      * a cache of Roo.data.Records.
6894      */
6895     readRecords : function(o){
6896         var sid = this.meta ? this.meta.id : null;
6897         var recordType = this.recordType, fields = recordType.prototype.fields;
6898         var records = [];
6899         var root = o;
6900             for(var i = 0; i < root.length; i++){
6901                     var n = root[i];
6902                 var values = {};
6903                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6904                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6905                 var f = fields.items[j];
6906                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6907                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6908                 v = f.convert(v);
6909                 values[f.name] = v;
6910             }
6911                 var record = new recordType(values, id);
6912                 record.json = n;
6913                 records[records.length] = record;
6914             }
6915             return {
6916                 records : records,
6917                 totalRecords : records.length
6918             };
6919     }
6920 });/*
6921  * - LGPL
6922  * * 
6923  */
6924
6925 /**
6926  * @class Roo.bootstrap.ComboBox
6927  * @extends Roo.bootstrap.TriggerField
6928  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
6929  * @constructor
6930  * Create a new ComboBox.
6931  * @param {Object} config Configuration options
6932  */
6933 Roo.bootstrap.ComboBox = function(config){
6934     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
6935     this.addEvents({
6936         /**
6937          * @event expand
6938          * Fires when the dropdown list is expanded
6939              * @param {Roo.bootstrap.ComboBox} combo This combo box
6940              */
6941         'expand' : true,
6942         /**
6943          * @event collapse
6944          * Fires when the dropdown list is collapsed
6945              * @param {Roo.bootstrap.ComboBox} combo This combo box
6946              */
6947         'collapse' : true,
6948         /**
6949          * @event beforeselect
6950          * Fires before a list item is selected. Return false to cancel the selection.
6951              * @param {Roo.bootstrap.ComboBox} combo This combo box
6952              * @param {Roo.data.Record} record The data record returned from the underlying store
6953              * @param {Number} index The index of the selected item in the dropdown list
6954              */
6955         'beforeselect' : true,
6956         /**
6957          * @event select
6958          * Fires when a list item is selected
6959              * @param {Roo.bootstrap.ComboBox} combo This combo box
6960              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
6961              * @param {Number} index The index of the selected item in the dropdown list
6962              */
6963         'select' : true,
6964         /**
6965          * @event beforequery
6966          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
6967          * The event object passed has these properties:
6968              * @param {Roo.bootstrap.ComboBox} combo This combo box
6969              * @param {String} query The query
6970              * @param {Boolean} forceAll true to force "all" query
6971              * @param {Boolean} cancel true to cancel the query
6972              * @param {Object} e The query event object
6973              */
6974         'beforequery': true,
6975          /**
6976          * @event add
6977          * Fires when the 'add' icon is pressed (add a listener to enable add button)
6978              * @param {Roo.bootstrap.ComboBox} combo This combo box
6979              */
6980         'add' : true,
6981         /**
6982          * @event edit
6983          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
6984              * @param {Roo.bootstrap.ComboBox} combo This combo box
6985              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
6986              */
6987         'edit' : true
6988         
6989         
6990     });
6991     
6992     
6993     this.selectedIndex = -1;
6994     if(this.mode == 'local'){
6995         if(config.queryDelay === undefined){
6996             this.queryDelay = 10;
6997         }
6998         if(config.minChars === undefined){
6999             this.minChars = 0;
7000         }
7001     }
7002 };
7003
7004 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7005      
7006     /**
7007      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7008      * rendering into an Roo.Editor, defaults to false)
7009      */
7010     /**
7011      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7012      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7013      */
7014     /**
7015      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7016      */
7017     /**
7018      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7019      * the dropdown list (defaults to undefined, with no header element)
7020      */
7021
7022      /**
7023      * @cfg {String/Roo.Template} tpl The template to use to render the output
7024      */
7025      
7026      /**
7027      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7028      */
7029     listWidth: undefined,
7030     /**
7031      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7032      * mode = 'remote' or 'text' if mode = 'local')
7033      */
7034     displayField: undefined,
7035     /**
7036      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7037      * mode = 'remote' or 'value' if mode = 'local'). 
7038      * Note: use of a valueField requires the user make a selection
7039      * in order for a value to be mapped.
7040      */
7041     valueField: undefined,
7042     
7043     
7044     /**
7045      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7046      * field's data value (defaults to the underlying DOM element's name)
7047      */
7048     hiddenName: undefined,
7049     /**
7050      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7051      */
7052     listClass: '',
7053     /**
7054      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7055      */
7056     selectedClass: 'active',
7057     
7058     /**
7059      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7060      */
7061     shadow:'sides',
7062     /**
7063      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7064      * anchor positions (defaults to 'tl-bl')
7065      */
7066     listAlign: 'tl-bl?',
7067     /**
7068      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7069      */
7070     maxHeight: 300,
7071     /**
7072      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7073      * query specified by the allQuery config option (defaults to 'query')
7074      */
7075     triggerAction: 'query',
7076     /**
7077      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7078      * (defaults to 4, does not apply if editable = false)
7079      */
7080     minChars : 4,
7081     /**
7082      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7083      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7084      */
7085     typeAhead: false,
7086     /**
7087      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7088      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7089      */
7090     queryDelay: 500,
7091     /**
7092      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7093      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7094      */
7095     pageSize: 0,
7096     /**
7097      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7098      * when editable = true (defaults to false)
7099      */
7100     selectOnFocus:false,
7101     /**
7102      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7103      */
7104     queryParam: 'query',
7105     /**
7106      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7107      * when mode = 'remote' (defaults to 'Loading...')
7108      */
7109     loadingText: 'Loading...',
7110     /**
7111      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7112      */
7113     resizable: false,
7114     /**
7115      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7116      */
7117     handleHeight : 8,
7118     /**
7119      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7120      * traditional select (defaults to true)
7121      */
7122     editable: true,
7123     /**
7124      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7125      */
7126     allQuery: '',
7127     /**
7128      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7129      */
7130     mode: 'remote',
7131     /**
7132      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7133      * listWidth has a higher value)
7134      */
7135     minListWidth : 70,
7136     /**
7137      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7138      * allow the user to set arbitrary text into the field (defaults to false)
7139      */
7140     forceSelection:false,
7141     /**
7142      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7143      * if typeAhead = true (defaults to 250)
7144      */
7145     typeAheadDelay : 250,
7146     /**
7147      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7148      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7149      */
7150     valueNotFoundText : undefined,
7151     /**
7152      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7153      */
7154     blockFocus : false,
7155     
7156     /**
7157      * @cfg {Boolean} disableClear Disable showing of clear button.
7158      */
7159     disableClear : false,
7160     /**
7161      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7162      */
7163     alwaysQuery : false,
7164     
7165     //private
7166     addicon : false,
7167     editicon: false,
7168     
7169     // element that contains real text value.. (when hidden is used..)
7170      
7171     // private
7172     initEvents: function(){
7173         
7174         if (!this.store) {
7175             throw "can not find store for combo";
7176         }
7177         this.store = Roo.factory(this.store, Roo.data);
7178         
7179         
7180         
7181         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
7182         
7183         
7184         if(this.hiddenName){
7185             
7186             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
7187             
7188             this.hiddenField.dom.value =
7189                 this.hiddenValue !== undefined ? this.hiddenValue :
7190                 this.value !== undefined ? this.value : '';
7191
7192             // prevent input submission
7193             this.el.dom.removeAttribute('name');
7194             this.hiddenField.dom.setAttribute('name', this.hiddenName);
7195              
7196              
7197         }
7198         //if(Roo.isGecko){
7199         //    this.el.dom.setAttribute('autocomplete', 'off');
7200         //}
7201
7202         var cls = 'x-combo-list';
7203         this.list = this.el.select('ul',true).first();
7204
7205         //this.list = new Roo.Layer({
7206         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
7207         //});
7208         
7209         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
7210         this.list.setWidth(lw);
7211         
7212         this.list.on('mouseover', this.onViewOver, this);
7213         this.list.on('mousemove', this.onViewMove, this);
7214         
7215         /*
7216         this.list.swallowEvent('mousewheel');
7217         this.assetHeight = 0;
7218
7219         if(this.title){
7220             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
7221             this.assetHeight += this.header.getHeight();
7222         }
7223
7224         this.innerList = this.list.createChild({cls:cls+'-inner'});
7225         this.innerList.on('mouseover', this.onViewOver, this);
7226         this.innerList.on('mousemove', this.onViewMove, this);
7227         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
7228         
7229         if(this.allowBlank && !this.pageSize && !this.disableClear){
7230             this.footer = this.list.createChild({cls:cls+'-ft'});
7231             this.pageTb = new Roo.Toolbar(this.footer);
7232            
7233         }
7234         if(this.pageSize){
7235             this.footer = this.list.createChild({cls:cls+'-ft'});
7236             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
7237                     {pageSize: this.pageSize});
7238             
7239         }
7240         
7241         if (this.pageTb && this.allowBlank && !this.disableClear) {
7242             var _this = this;
7243             this.pageTb.add(new Roo.Toolbar.Fill(), {
7244                 cls: 'x-btn-icon x-btn-clear',
7245                 text: '&#160;',
7246                 handler: function()
7247                 {
7248                     _this.collapse();
7249                     _this.clearValue();
7250                     _this.onSelect(false, -1);
7251                 }
7252             });
7253         }
7254         if (this.footer) {
7255             this.assetHeight += this.footer.getHeight();
7256         }
7257         */
7258             
7259         if(!this.tpl){
7260             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
7261         }
7262
7263         this.view = new Roo.View(this.el.select('ul',true).first(), this.tpl, {
7264             singleSelect:true, store: this.store, selectedClass: this.selectedClass
7265         });
7266         //this.view.wrapEl.setDisplayed(false);
7267         this.view.on('click', this.onViewClick, this);
7268         
7269         
7270         
7271         this.store.on('beforeload', this.onBeforeLoad, this);
7272         this.store.on('load', this.onLoad, this);
7273         this.store.on('loadexception', this.onLoadException, this);
7274         /*
7275         if(this.resizable){
7276             this.resizer = new Roo.Resizable(this.list,  {
7277                pinned:true, handles:'se'
7278             });
7279             this.resizer.on('resize', function(r, w, h){
7280                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
7281                 this.listWidth = w;
7282                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
7283                 this.restrictHeight();
7284             }, this);
7285             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
7286         }
7287         */
7288         if(!this.editable){
7289             this.editable = true;
7290             this.setEditable(false);
7291         }
7292         
7293         /*
7294         
7295         if (typeof(this.events.add.listeners) != 'undefined') {
7296             
7297             this.addicon = this.wrap.createChild(
7298                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
7299        
7300             this.addicon.on('click', function(e) {
7301                 this.fireEvent('add', this);
7302             }, this);
7303         }
7304         if (typeof(this.events.edit.listeners) != 'undefined') {
7305             
7306             this.editicon = this.wrap.createChild(
7307                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
7308             if (this.addicon) {
7309                 this.editicon.setStyle('margin-left', '40px');
7310             }
7311             this.editicon.on('click', function(e) {
7312                 
7313                 // we fire even  if inothing is selected..
7314                 this.fireEvent('edit', this, this.lastData );
7315                 
7316             }, this);
7317         }
7318         */
7319         
7320  
7321         this.keyNav = new Roo.KeyNav(this.inputEl(), {
7322             "up" : function(e){
7323                 this.inKeyMode = true;
7324                 this.selectPrev();
7325             },
7326
7327             "down" : function(e){
7328                 if(!this.isExpanded()){
7329                     this.onTriggerClick();
7330                 }else{
7331                     this.inKeyMode = true;
7332                     this.selectNext();
7333                 }
7334             },
7335
7336             "enter" : function(e){
7337                 this.onViewClick();
7338                 //return true;
7339             },
7340
7341             "esc" : function(e){
7342                 this.collapse();
7343             },
7344
7345             "tab" : function(e){
7346                 this.collapse();
7347                 
7348                 if(this.fireEvent("specialkey", this, e)){
7349                     this.onViewClick(false);
7350                 }
7351                 
7352                 return true;
7353             },
7354
7355             scope : this,
7356
7357             doRelay : function(foo, bar, hname){
7358                 if(hname == 'down' || this.scope.isExpanded()){
7359                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
7360                 }
7361                 return true;
7362             },
7363
7364             forceKeyDown: true
7365         });
7366         
7367         
7368         this.queryDelay = Math.max(this.queryDelay || 10,
7369                 this.mode == 'local' ? 10 : 250);
7370         
7371         
7372         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
7373         
7374         if(this.typeAhead){
7375             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
7376         }
7377         if(this.editable !== false){
7378             this.inputEl().on("keyup", this.onKeyUp, this);
7379         }
7380         if(this.forceSelection){
7381             this.on('blur', this.doForce, this);
7382         }
7383     },
7384
7385     onDestroy : function(){
7386         if(this.view){
7387             this.view.setStore(null);
7388             this.view.el.removeAllListeners();
7389             this.view.el.remove();
7390             this.view.purgeListeners();
7391         }
7392         if(this.list){
7393             this.list.dom.innerHTML  = '';
7394         }
7395         if(this.store){
7396             this.store.un('beforeload', this.onBeforeLoad, this);
7397             this.store.un('load', this.onLoad, this);
7398             this.store.un('loadexception', this.onLoadException, this);
7399         }
7400         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
7401     },
7402
7403     // private
7404     fireKey : function(e){
7405         if(e.isNavKeyPress() && !this.list.isVisible()){
7406             this.fireEvent("specialkey", this, e);
7407         }
7408     },
7409
7410     // private
7411     onResize: function(w, h){
7412 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
7413 //        
7414 //        if(typeof w != 'number'){
7415 //            // we do not handle it!?!?
7416 //            return;
7417 //        }
7418 //        var tw = this.trigger.getWidth();
7419 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
7420 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
7421 //        var x = w - tw;
7422 //        this.inputEl().setWidth( this.adjustWidth('input', x));
7423 //            
7424 //        //this.trigger.setStyle('left', x+'px');
7425 //        
7426 //        if(this.list && this.listWidth === undefined){
7427 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
7428 //            this.list.setWidth(lw);
7429 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
7430 //        }
7431         
7432     
7433         
7434     },
7435
7436     /**
7437      * Allow or prevent the user from directly editing the field text.  If false is passed,
7438      * the user will only be able to select from the items defined in the dropdown list.  This method
7439      * is the runtime equivalent of setting the 'editable' config option at config time.
7440      * @param {Boolean} value True to allow the user to directly edit the field text
7441      */
7442     setEditable : function(value){
7443         if(value == this.editable){
7444             return;
7445         }
7446         this.editable = value;
7447         if(!value){
7448             this.inputEl().dom.setAttribute('readOnly', true);
7449             this.inputEl().on('mousedown', this.onTriggerClick,  this);
7450             this.inputEl().addClass('x-combo-noedit');
7451         }else{
7452             this.inputEl().dom.setAttribute('readOnly', false);
7453             this.inputEl().un('mousedown', this.onTriggerClick,  this);
7454             this.inputEl().removeClass('x-combo-noedit');
7455         }
7456     },
7457
7458     // private
7459     onBeforeLoad : function(){
7460         if(!this.hasFocus){
7461             return;
7462         }
7463         //this.innerList.update(this.loadingText ?
7464         //       '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
7465         this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
7466         
7467         this.restrictHeight();
7468         this.selectedIndex = -1;
7469     },
7470
7471     // private
7472     onLoad : function(){
7473         if(!this.hasFocus){
7474             return;
7475         }
7476         if(this.store.getCount() > 0){
7477             this.expand();
7478             this.restrictHeight();
7479             if(this.lastQuery == this.allQuery){
7480                 if(this.editable){
7481                     this.inputEl().dom.select();
7482                 }
7483                 if(!this.selectByValue(this.value, true)){
7484                     this.select(0, true);
7485                 }
7486             }else{
7487                 this.selectNext();
7488                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
7489                     this.taTask.delay(this.typeAheadDelay);
7490                 }
7491             }
7492         }else{
7493             this.onEmptyResults();
7494         }
7495         //this.el.focus();
7496     },
7497     // private
7498     onLoadException : function()
7499     {
7500         this.collapse();
7501         Roo.log(this.store.reader.jsonData);
7502         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7503             // fixme
7504             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7505         }
7506         
7507         
7508     },
7509     // private
7510     onTypeAhead : function(){
7511         if(this.store.getCount() > 0){
7512             var r = this.store.getAt(0);
7513             var newValue = r.data[this.displayField];
7514             var len = newValue.length;
7515             var selStart = this.getRawValue().length;
7516             
7517             if(selStart != len){
7518                 this.setRawValue(newValue);
7519                 this.selectText(selStart, newValue.length);
7520             }
7521         }
7522     },
7523
7524     // private
7525     onSelect : function(record, index){
7526         if(this.fireEvent('beforeselect', this, record, index) !== false){
7527             this.setFromData(index > -1 ? record.data : false);
7528             this.collapse();
7529             this.fireEvent('select', this, record, index);
7530         }
7531     },
7532
7533     /**
7534      * Returns the currently selected field value or empty string if no value is set.
7535      * @return {String} value The selected value
7536      */
7537     getValue : function(){
7538         if(this.valueField){
7539             return typeof this.value != 'undefined' ? this.value : '';
7540         }else{
7541             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
7542         }
7543     },
7544
7545     /**
7546      * Clears any text/value currently set in the field
7547      */
7548     clearValue : function(){
7549         if(this.hiddenField){
7550             this.hiddenField.dom.value = '';
7551         }
7552         this.value = '';
7553         this.setRawValue('');
7554         this.lastSelectionText = '';
7555         
7556     },
7557
7558     /**
7559      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
7560      * will be displayed in the field.  If the value does not match the data value of an existing item,
7561      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
7562      * Otherwise the field will be blank (although the value will still be set).
7563      * @param {String} value The value to match
7564      */
7565     setValue : function(v){
7566         var text = v;
7567         if(this.valueField){
7568             var r = this.findRecord(this.valueField, v);
7569             if(r){
7570                 text = r.data[this.displayField];
7571             }else if(this.valueNotFoundText !== undefined){
7572                 text = this.valueNotFoundText;
7573             }
7574         }
7575         this.lastSelectionText = text;
7576         if(this.hiddenField){
7577             this.hiddenField.dom.value = v;
7578         }
7579         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
7580         this.value = v;
7581     },
7582     /**
7583      * @property {Object} the last set data for the element
7584      */
7585     
7586     lastData : false,
7587     /**
7588      * Sets the value of the field based on a object which is related to the record format for the store.
7589      * @param {Object} value the value to set as. or false on reset?
7590      */
7591     setFromData : function(o){
7592         var dv = ''; // display value
7593         var vv = ''; // value value..
7594         this.lastData = o;
7595         if (this.displayField) {
7596             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
7597         } else {
7598             // this is an error condition!!!
7599             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
7600         }
7601         
7602         if(this.valueField){
7603             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
7604         }
7605         if(this.hiddenField){
7606             this.hiddenField.dom.value = vv;
7607             
7608             this.lastSelectionText = dv;
7609             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
7610             this.value = vv;
7611             return;
7612         }
7613         // no hidden field.. - we store the value in 'value', but still display
7614         // display field!!!!
7615         this.lastSelectionText = dv;
7616         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
7617         this.value = vv;
7618         
7619         
7620     },
7621     // private
7622     reset : function(){
7623         // overridden so that last data is reset..
7624         this.setValue(this.originalValue);
7625         this.clearInvalid();
7626         this.lastData = false;
7627         if (this.view) {
7628             this.view.clearSelections();
7629         }
7630     },
7631     // private
7632     findRecord : function(prop, value){
7633         var record;
7634         if(this.store.getCount() > 0){
7635             this.store.each(function(r){
7636                 if(r.data[prop] == value){
7637                     record = r;
7638                     return false;
7639                 }
7640                 return true;
7641             });
7642         }
7643         return record;
7644     },
7645     
7646     getName: function()
7647     {
7648         // returns hidden if it's set..
7649         if (!this.rendered) {return ''};
7650         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
7651         
7652     },
7653     // private
7654     onViewMove : function(e, t){
7655         this.inKeyMode = false;
7656     },
7657
7658     // private
7659     onViewOver : function(e, t){
7660         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
7661             return;
7662         }
7663         var item = this.view.findItemFromChild(t);
7664         if(item){
7665             var index = this.view.indexOf(item);
7666             this.select(index, false);
7667         }
7668     },
7669
7670     // private
7671     onViewClick : function(doFocus)
7672     {
7673         var index = this.view.getSelectedIndexes()[0];
7674         var r = this.store.getAt(index);
7675         if(r){
7676             this.onSelect(r, index);
7677         }
7678         if(doFocus !== false && !this.blockFocus){
7679             this.inputEl().focus();
7680         }
7681     },
7682
7683     // private
7684     restrictHeight : function(){
7685         //this.innerList.dom.style.height = '';
7686         //var inner = this.innerList.dom;
7687         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
7688         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
7689         //this.list.beginUpdate();
7690         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
7691         this.list.alignTo(this.inputEl(), this.listAlign);
7692         //this.list.endUpdate();
7693     },
7694
7695     // private
7696     onEmptyResults : function(){
7697         this.collapse();
7698     },
7699
7700     /**
7701      * Returns true if the dropdown list is expanded, else false.
7702      */
7703     isExpanded : function(){
7704         return this.list.isVisible();
7705     },
7706
7707     /**
7708      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
7709      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
7710      * @param {String} value The data value of the item to select
7711      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
7712      * selected item if it is not currently in view (defaults to true)
7713      * @return {Boolean} True if the value matched an item in the list, else false
7714      */
7715     selectByValue : function(v, scrollIntoView){
7716         if(v !== undefined && v !== null){
7717             var r = this.findRecord(this.valueField || this.displayField, v);
7718             if(r){
7719                 this.select(this.store.indexOf(r), scrollIntoView);
7720                 return true;
7721             }
7722         }
7723         return false;
7724     },
7725
7726     /**
7727      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
7728      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
7729      * @param {Number} index The zero-based index of the list item to select
7730      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
7731      * selected item if it is not currently in view (defaults to true)
7732      */
7733     select : function(index, scrollIntoView){
7734         this.selectedIndex = index;
7735         this.view.select(index);
7736         if(scrollIntoView !== false){
7737             var el = this.view.getNode(index);
7738             if(el){
7739                 //this.innerList.scrollChildIntoView(el, false);
7740                 
7741             }
7742         }
7743     },
7744
7745     // private
7746     selectNext : function(){
7747         var ct = this.store.getCount();
7748         if(ct > 0){
7749             if(this.selectedIndex == -1){
7750                 this.select(0);
7751             }else if(this.selectedIndex < ct-1){
7752                 this.select(this.selectedIndex+1);
7753             }
7754         }
7755     },
7756
7757     // private
7758     selectPrev : function(){
7759         var ct = this.store.getCount();
7760         if(ct > 0){
7761             if(this.selectedIndex == -1){
7762                 this.select(0);
7763             }else if(this.selectedIndex != 0){
7764                 this.select(this.selectedIndex-1);
7765             }
7766         }
7767     },
7768
7769     // private
7770     onKeyUp : function(e){
7771         if(this.editable !== false && !e.isSpecialKey()){
7772             this.lastKey = e.getKey();
7773             this.dqTask.delay(this.queryDelay);
7774         }
7775     },
7776
7777     // private
7778     validateBlur : function(){
7779         return !this.list || !this.list.isVisible();   
7780     },
7781
7782     // private
7783     initQuery : function(){
7784         this.doQuery(this.getRawValue());
7785     },
7786
7787     // private
7788     doForce : function(){
7789         if(this.el.dom.value.length > 0){
7790             this.el.dom.value =
7791                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
7792              
7793         }
7794     },
7795
7796     /**
7797      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
7798      * query allowing the query action to be canceled if needed.
7799      * @param {String} query The SQL query to execute
7800      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
7801      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
7802      * saved in the current store (defaults to false)
7803      */
7804     doQuery : function(q, forceAll){
7805         if(q === undefined || q === null){
7806             q = '';
7807         }
7808         var qe = {
7809             query: q,
7810             forceAll: forceAll,
7811             combo: this,
7812             cancel:false
7813         };
7814         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
7815             return false;
7816         }
7817         q = qe.query;
7818         forceAll = qe.forceAll;
7819         if(forceAll === true || (q.length >= this.minChars)){
7820             if(this.lastQuery != q || this.alwaysQuery){
7821                 this.lastQuery = q;
7822                 if(this.mode == 'local'){
7823                     this.selectedIndex = -1;
7824                     if(forceAll){
7825                         this.store.clearFilter();
7826                     }else{
7827                         this.store.filter(this.displayField, q);
7828                     }
7829                     this.onLoad();
7830                 }else{
7831                     this.store.baseParams[this.queryParam] = q;
7832                     this.store.load({
7833                         params: this.getParams(q)
7834                     });
7835                     this.expand();
7836                 }
7837             }else{
7838                 this.selectedIndex = -1;
7839                 this.onLoad();   
7840             }
7841         }
7842     },
7843
7844     // private
7845     getParams : function(q){
7846         var p = {};
7847         //p[this.queryParam] = q;
7848         if(this.pageSize){
7849             p.start = 0;
7850             p.limit = this.pageSize;
7851         }
7852         return p;
7853     },
7854
7855     /**
7856      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
7857      */
7858     collapse : function(){
7859         if(!this.isExpanded()){
7860             return;
7861         }
7862         this.list.hide();
7863         Roo.get(document).un('mousedown', this.collapseIf, this);
7864         Roo.get(document).un('mousewheel', this.collapseIf, this);
7865         if (!this.editable) {
7866             Roo.get(document).un('keydown', this.listKeyPress, this);
7867         }
7868         this.fireEvent('collapse', this);
7869     },
7870
7871     // private
7872     collapseIf : function(e){
7873         if(!e.within(this.el) && !e.within(this.el)){
7874             this.collapse();
7875         }
7876     },
7877
7878     /**
7879      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
7880      */
7881     expand : function(){
7882         Roo.log('expand');
7883         if(this.isExpanded() || !this.hasFocus){
7884             return;
7885         }
7886         this.list.alignTo(this.inputEl(), this.listAlign);
7887         this.list.show();
7888         Roo.get(document).on('mousedown', this.collapseIf, this);
7889         Roo.get(document).on('mousewheel', this.collapseIf, this);
7890         if (!this.editable) {
7891             Roo.get(document).on('keydown', this.listKeyPress, this);
7892         }
7893         
7894         this.fireEvent('expand', this);
7895     },
7896
7897     // private
7898     // Implements the default empty TriggerField.onTriggerClick function
7899     onTriggerClick : function()
7900     {
7901         Roo.log('trigger click');
7902         
7903         if(this.disabled){
7904             return;
7905         }
7906         if(this.isExpanded()){
7907             this.collapse();
7908             if (!this.blockFocus) {
7909                 this.inputEl().focus();
7910             }
7911             
7912         }else {
7913             this.hasFocus = true;
7914             if(this.triggerAction == 'all') {
7915                 this.doQuery(this.allQuery, true);
7916             } else {
7917                 this.doQuery(this.getRawValue());
7918             }
7919             if (!this.blockFocus) {
7920                 this.inputEl().focus();
7921             }
7922         }
7923     },
7924     listKeyPress : function(e)
7925     {
7926         //Roo.log('listkeypress');
7927         // scroll to first matching element based on key pres..
7928         if (e.isSpecialKey()) {
7929             return false;
7930         }
7931         var k = String.fromCharCode(e.getKey()).toUpperCase();
7932         //Roo.log(k);
7933         var match  = false;
7934         var csel = this.view.getSelectedNodes();
7935         var cselitem = false;
7936         if (csel.length) {
7937             var ix = this.view.indexOf(csel[0]);
7938             cselitem  = this.store.getAt(ix);
7939             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
7940                 cselitem = false;
7941             }
7942             
7943         }
7944         
7945         this.store.each(function(v) { 
7946             if (cselitem) {
7947                 // start at existing selection.
7948                 if (cselitem.id == v.id) {
7949                     cselitem = false;
7950                 }
7951                 return true;
7952             }
7953                 
7954             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
7955                 match = this.store.indexOf(v);
7956                 return false;
7957             }
7958             return true;
7959         }, this);
7960         
7961         if (match === false) {
7962             return true; // no more action?
7963         }
7964         // scroll to?
7965         this.view.select(match);
7966         var sn = Roo.get(this.view.getSelectedNodes()[0])
7967         //sn.scrollIntoView(sn.dom.parentNode, false);
7968     }
7969
7970     /** 
7971     * @cfg {Boolean} grow 
7972     * @hide 
7973     */
7974     /** 
7975     * @cfg {Number} growMin 
7976     * @hide 
7977     */
7978     /** 
7979     * @cfg {Number} growMax 
7980     * @hide 
7981     */
7982     /**
7983      * @hide
7984      * @method autoSize
7985      */
7986 });/*
7987  * Based on:
7988  * Ext JS Library 1.1.1
7989  * Copyright(c) 2006-2007, Ext JS, LLC.
7990  *
7991  * Originally Released Under LGPL - original licence link has changed is not relivant.
7992  *
7993  * Fork - LGPL
7994  * <script type="text/javascript">
7995  */
7996
7997 /**
7998  * @class Roo.View
7999  * @extends Roo.util.Observable
8000  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8001  * This class also supports single and multi selection modes. <br>
8002  * Create a data model bound view:
8003  <pre><code>
8004  var store = new Roo.data.Store(...);
8005
8006  var view = new Roo.View({
8007     el : "my-element",
8008     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8009  
8010     singleSelect: true,
8011     selectedClass: "ydataview-selected",
8012     store: store
8013  });
8014
8015  // listen for node click?
8016  view.on("click", function(vw, index, node, e){
8017  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8018  });
8019
8020  // load XML data
8021  dataModel.load("foobar.xml");
8022  </code></pre>
8023  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8024  * <br><br>
8025  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8026  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8027  * 
8028  * Note: old style constructor is still suported (container, template, config)
8029  * 
8030  * @constructor
8031  * Create a new View
8032  * @param {Object} config The config object
8033  * 
8034  */
8035 Roo.View = function(config, depreciated_tpl, depreciated_config){
8036     
8037     if (typeof(depreciated_tpl) == 'undefined') {
8038         // new way.. - universal constructor.
8039         Roo.apply(this, config);
8040         this.el  = Roo.get(this.el);
8041     } else {
8042         // old format..
8043         this.el  = Roo.get(config);
8044         this.tpl = depreciated_tpl;
8045         Roo.apply(this, depreciated_config);
8046     }
8047     this.wrapEl  = this.el.wrap().wrap();
8048     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8049     
8050     
8051     if(typeof(this.tpl) == "string"){
8052         this.tpl = new Roo.Template(this.tpl);
8053     } else {
8054         // support xtype ctors..
8055         this.tpl = new Roo.factory(this.tpl, Roo);
8056     }
8057     
8058     
8059     this.tpl.compile();
8060    
8061   
8062     
8063      
8064     /** @private */
8065     this.addEvents({
8066         /**
8067          * @event beforeclick
8068          * Fires before a click is processed. Returns false to cancel the default action.
8069          * @param {Roo.View} this
8070          * @param {Number} index The index of the target node
8071          * @param {HTMLElement} node The target node
8072          * @param {Roo.EventObject} e The raw event object
8073          */
8074             "beforeclick" : true,
8075         /**
8076          * @event click
8077          * Fires when a template node is clicked.
8078          * @param {Roo.View} this
8079          * @param {Number} index The index of the target node
8080          * @param {HTMLElement} node The target node
8081          * @param {Roo.EventObject} e The raw event object
8082          */
8083             "click" : true,
8084         /**
8085          * @event dblclick
8086          * Fires when a template node is double clicked.
8087          * @param {Roo.View} this
8088          * @param {Number} index The index of the target node
8089          * @param {HTMLElement} node The target node
8090          * @param {Roo.EventObject} e The raw event object
8091          */
8092             "dblclick" : true,
8093         /**
8094          * @event contextmenu
8095          * Fires when a template node is right clicked.
8096          * @param {Roo.View} this
8097          * @param {Number} index The index of the target node
8098          * @param {HTMLElement} node The target node
8099          * @param {Roo.EventObject} e The raw event object
8100          */
8101             "contextmenu" : true,
8102         /**
8103          * @event selectionchange
8104          * Fires when the selected nodes change.
8105          * @param {Roo.View} this
8106          * @param {Array} selections Array of the selected nodes
8107          */
8108             "selectionchange" : true,
8109     
8110         /**
8111          * @event beforeselect
8112          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8113          * @param {Roo.View} this
8114          * @param {HTMLElement} node The node to be selected
8115          * @param {Array} selections Array of currently selected nodes
8116          */
8117             "beforeselect" : true,
8118         /**
8119          * @event preparedata
8120          * Fires on every row to render, to allow you to change the data.
8121          * @param {Roo.View} this
8122          * @param {Object} data to be rendered (change this)
8123          */
8124           "preparedata" : true
8125           
8126           
8127         });
8128
8129
8130
8131     this.el.on({
8132         "click": this.onClick,
8133         "dblclick": this.onDblClick,
8134         "contextmenu": this.onContextMenu,
8135         scope:this
8136     });
8137
8138     this.selections = [];
8139     this.nodes = [];
8140     this.cmp = new Roo.CompositeElementLite([]);
8141     if(this.store){
8142         this.store = Roo.factory(this.store, Roo.data);
8143         this.setStore(this.store, true);
8144     }
8145     
8146     if ( this.footer && this.footer.xtype) {
8147            
8148          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8149         
8150         this.footer.dataSource = this.store
8151         this.footer.container = fctr;
8152         this.footer = Roo.factory(this.footer, Roo);
8153         fctr.insertFirst(this.el);
8154         
8155         // this is a bit insane - as the paging toolbar seems to detach the el..
8156 //        dom.parentNode.parentNode.parentNode
8157          // they get detached?
8158     }
8159     
8160     
8161     Roo.View.superclass.constructor.call(this);
8162     
8163     
8164 };
8165
8166 Roo.extend(Roo.View, Roo.util.Observable, {
8167     
8168      /**
8169      * @cfg {Roo.data.Store} store Data store to load data from.
8170      */
8171     store : false,
8172     
8173     /**
8174      * @cfg {String|Roo.Element} el The container element.
8175      */
8176     el : '',
8177     
8178     /**
8179      * @cfg {String|Roo.Template} tpl The template used by this View 
8180      */
8181     tpl : false,
8182     /**
8183      * @cfg {String} dataName the named area of the template to use as the data area
8184      *                          Works with domtemplates roo-name="name"
8185      */
8186     dataName: false,
8187     /**
8188      * @cfg {String} selectedClass The css class to add to selected nodes
8189      */
8190     selectedClass : "x-view-selected",
8191      /**
8192      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8193      */
8194     emptyText : "",
8195     
8196     /**
8197      * @cfg {String} text to display on mask (default Loading)
8198      */
8199     mask : false,
8200     /**
8201      * @cfg {Boolean} multiSelect Allow multiple selection
8202      */
8203     multiSelect : false,
8204     /**
8205      * @cfg {Boolean} singleSelect Allow single selection
8206      */
8207     singleSelect:  false,
8208     
8209     /**
8210      * @cfg {Boolean} toggleSelect - selecting 
8211      */
8212     toggleSelect : false,
8213     
8214     /**
8215      * Returns the element this view is bound to.
8216      * @return {Roo.Element}
8217      */
8218     getEl : function(){
8219         return this.wrapEl;
8220     },
8221     
8222     
8223
8224     /**
8225      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8226      */
8227     refresh : function(){
8228         var t = this.tpl;
8229         
8230         // if we are using something like 'domtemplate', then
8231         // the what gets used is:
8232         // t.applySubtemplate(NAME, data, wrapping data..)
8233         // the outer template then get' applied with
8234         //     the store 'extra data'
8235         // and the body get's added to the
8236         //      roo-name="data" node?
8237         //      <span class='roo-tpl-{name}'></span> ?????
8238         
8239         
8240         
8241         this.clearSelections();
8242         this.el.update("");
8243         var html = [];
8244         var records = this.store.getRange();
8245         if(records.length < 1) {
8246             
8247             // is this valid??  = should it render a template??
8248             
8249             this.el.update(this.emptyText);
8250             return;
8251         }
8252         var el = this.el;
8253         if (this.dataName) {
8254             this.el.update(t.apply(this.store.meta)); //????
8255             el = this.el.child('.roo-tpl-' + this.dataName);
8256         }
8257         
8258         for(var i = 0, len = records.length; i < len; i++){
8259             var data = this.prepareData(records[i].data, i, records[i]);
8260             this.fireEvent("preparedata", this, data, i, records[i]);
8261             html[html.length] = Roo.util.Format.trim(
8262                 this.dataName ?
8263                     t.applySubtemplate(this.dataName, data, this.store.meta) :
8264                     t.apply(data)
8265             );
8266         }
8267         
8268         
8269         
8270         el.update(html.join(""));
8271         this.nodes = el.dom.childNodes;
8272         this.updateIndexes(0);
8273     },
8274
8275     /**
8276      * Function to override to reformat the data that is sent to
8277      * the template for each node.
8278      * DEPRICATED - use the preparedata event handler.
8279      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8280      * a JSON object for an UpdateManager bound view).
8281      */
8282     prepareData : function(data, index, record)
8283     {
8284         this.fireEvent("preparedata", this, data, index, record);
8285         return data;
8286     },
8287
8288     onUpdate : function(ds, record){
8289         this.clearSelections();
8290         var index = this.store.indexOf(record);
8291         var n = this.nodes[index];
8292         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8293         n.parentNode.removeChild(n);
8294         this.updateIndexes(index, index);
8295     },
8296
8297     
8298     
8299 // --------- FIXME     
8300     onAdd : function(ds, records, index)
8301     {
8302         this.clearSelections();
8303         if(this.nodes.length == 0){
8304             this.refresh();
8305             return;
8306         }
8307         var n = this.nodes[index];
8308         for(var i = 0, len = records.length; i < len; i++){
8309             var d = this.prepareData(records[i].data, i, records[i]);
8310             if(n){
8311                 this.tpl.insertBefore(n, d);
8312             }else{
8313                 
8314                 this.tpl.append(this.el, d);
8315             }
8316         }
8317         this.updateIndexes(index);
8318     },
8319
8320     onRemove : function(ds, record, index){
8321         this.clearSelections();
8322         var el = this.dataName  ?
8323             this.el.child('.roo-tpl-' + this.dataName) :
8324             this.el; 
8325         el.dom.removeChild(this.nodes[index]);
8326         this.updateIndexes(index);
8327     },
8328
8329     /**
8330      * Refresh an individual node.
8331      * @param {Number} index
8332      */
8333     refreshNode : function(index){
8334         this.onUpdate(this.store, this.store.getAt(index));
8335     },
8336
8337     updateIndexes : function(startIndex, endIndex){
8338         var ns = this.nodes;
8339         startIndex = startIndex || 0;
8340         endIndex = endIndex || ns.length - 1;
8341         for(var i = startIndex; i <= endIndex; i++){
8342             ns[i].nodeIndex = i;
8343         }
8344     },
8345
8346     /**
8347      * Changes the data store this view uses and refresh the view.
8348      * @param {Store} store
8349      */
8350     setStore : function(store, initial){
8351         if(!initial && this.store){
8352             this.store.un("datachanged", this.refresh);
8353             this.store.un("add", this.onAdd);
8354             this.store.un("remove", this.onRemove);
8355             this.store.un("update", this.onUpdate);
8356             this.store.un("clear", this.refresh);
8357             this.store.un("beforeload", this.onBeforeLoad);
8358             this.store.un("load", this.onLoad);
8359             this.store.un("loadexception", this.onLoad);
8360         }
8361         if(store){
8362           
8363             store.on("datachanged", this.refresh, this);
8364             store.on("add", this.onAdd, this);
8365             store.on("remove", this.onRemove, this);
8366             store.on("update", this.onUpdate, this);
8367             store.on("clear", this.refresh, this);
8368             store.on("beforeload", this.onBeforeLoad, this);
8369             store.on("load", this.onLoad, this);
8370             store.on("loadexception", this.onLoad, this);
8371         }
8372         
8373         if(store){
8374             this.refresh();
8375         }
8376     },
8377     /**
8378      * onbeforeLoad - masks the loading area.
8379      *
8380      */
8381     onBeforeLoad : function()
8382     {
8383         this.el.update("");
8384         this.el.mask(this.mask ? this.mask : "Loading" ); 
8385     },
8386     onLoad : function ()
8387     {
8388         this.el.unmask();
8389     },
8390     
8391
8392     /**
8393      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8394      * @param {HTMLElement} node
8395      * @return {HTMLElement} The template node
8396      */
8397     findItemFromChild : function(node){
8398         var el = this.dataName  ?
8399             this.el.child('.roo-tpl-' + this.dataName,true) :
8400             this.el.dom; 
8401         
8402         if(!node || node.parentNode == el){
8403                     return node;
8404             }
8405             var p = node.parentNode;
8406             while(p && p != el){
8407             if(p.parentNode == el){
8408                 return p;
8409             }
8410             p = p.parentNode;
8411         }
8412             return null;
8413     },
8414
8415     /** @ignore */
8416     onClick : function(e){
8417         var item = this.findItemFromChild(e.getTarget());
8418         if(item){
8419             var index = this.indexOf(item);
8420             if(this.onItemClick(item, index, e) !== false){
8421                 this.fireEvent("click", this, index, item, e);
8422             }
8423         }else{
8424             this.clearSelections();
8425         }
8426     },
8427
8428     /** @ignore */
8429     onContextMenu : function(e){
8430         var item = this.findItemFromChild(e.getTarget());
8431         if(item){
8432             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8433         }
8434     },
8435
8436     /** @ignore */
8437     onDblClick : function(e){
8438         var item = this.findItemFromChild(e.getTarget());
8439         if(item){
8440             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8441         }
8442     },
8443
8444     onItemClick : function(item, index, e)
8445     {
8446         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8447             return false;
8448         }
8449         if (this.toggleSelect) {
8450             var m = this.isSelected(item) ? 'unselect' : 'select';
8451             Roo.log(m);
8452             var _t = this;
8453             _t[m](item, true, false);
8454             return true;
8455         }
8456         if(this.multiSelect || this.singleSelect){
8457             if(this.multiSelect && e.shiftKey && this.lastSelection){
8458                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8459             }else{
8460                 this.select(item, this.multiSelect && e.ctrlKey);
8461                 this.lastSelection = item;
8462             }
8463             e.preventDefault();
8464         }
8465         return true;
8466     },
8467
8468     /**
8469      * Get the number of selected nodes.
8470      * @return {Number}
8471      */
8472     getSelectionCount : function(){
8473         return this.selections.length;
8474     },
8475
8476     /**
8477      * Get the currently selected nodes.
8478      * @return {Array} An array of HTMLElements
8479      */
8480     getSelectedNodes : function(){
8481         return this.selections;
8482     },
8483
8484     /**
8485      * Get the indexes of the selected nodes.
8486      * @return {Array}
8487      */
8488     getSelectedIndexes : function(){
8489         var indexes = [], s = this.selections;
8490         for(var i = 0, len = s.length; i < len; i++){
8491             indexes.push(s[i].nodeIndex);
8492         }
8493         return indexes;
8494     },
8495
8496     /**
8497      * Clear all selections
8498      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8499      */
8500     clearSelections : function(suppressEvent){
8501         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8502             this.cmp.elements = this.selections;
8503             this.cmp.removeClass(this.selectedClass);
8504             this.selections = [];
8505             if(!suppressEvent){
8506                 this.fireEvent("selectionchange", this, this.selections);
8507             }
8508         }
8509     },
8510
8511     /**
8512      * Returns true if the passed node is selected
8513      * @param {HTMLElement/Number} node The node or node index
8514      * @return {Boolean}
8515      */
8516     isSelected : function(node){
8517         var s = this.selections;
8518         if(s.length < 1){
8519             return false;
8520         }
8521         node = this.getNode(node);
8522         return s.indexOf(node) !== -1;
8523     },
8524
8525     /**
8526      * Selects nodes.
8527      * @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
8528      * @param {Boolean} keepExisting (optional) true to keep existing selections
8529      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8530      */
8531     select : function(nodeInfo, keepExisting, suppressEvent){
8532         if(nodeInfo instanceof Array){
8533             if(!keepExisting){
8534                 this.clearSelections(true);
8535             }
8536             for(var i = 0, len = nodeInfo.length; i < len; i++){
8537                 this.select(nodeInfo[i], true, true);
8538             }
8539             return;
8540         } 
8541         var node = this.getNode(nodeInfo);
8542         if(!node || this.isSelected(node)){
8543             return; // already selected.
8544         }
8545         if(!keepExisting){
8546             this.clearSelections(true);
8547         }
8548         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8549             Roo.fly(node).addClass(this.selectedClass);
8550             this.selections.push(node);
8551             if(!suppressEvent){
8552                 this.fireEvent("selectionchange", this, this.selections);
8553             }
8554         }
8555         
8556         
8557     },
8558       /**
8559      * Unselects nodes.
8560      * @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
8561      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8562      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8563      */
8564     unselect : function(nodeInfo, keepExisting, suppressEvent)
8565     {
8566         if(nodeInfo instanceof Array){
8567             Roo.each(this.selections, function(s) {
8568                 this.unselect(s, nodeInfo);
8569             }, this);
8570             return;
8571         }
8572         var node = this.getNode(nodeInfo);
8573         if(!node || !this.isSelected(node)){
8574             Roo.log("not selected");
8575             return; // not selected.
8576         }
8577         // fireevent???
8578         var ns = [];
8579         Roo.each(this.selections, function(s) {
8580             if (s == node ) {
8581                 Roo.fly(node).removeClass(this.selectedClass);
8582
8583                 return;
8584             }
8585             ns.push(s);
8586         },this);
8587         
8588         this.selections= ns;
8589         this.fireEvent("selectionchange", this, this.selections);
8590     },
8591
8592     /**
8593      * Gets a template node.
8594      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
8595      * @return {HTMLElement} The node or null if it wasn't found
8596      */
8597     getNode : function(nodeInfo){
8598         if(typeof nodeInfo == "string"){
8599             return document.getElementById(nodeInfo);
8600         }else if(typeof nodeInfo == "number"){
8601             return this.nodes[nodeInfo];
8602         }
8603         return nodeInfo;
8604     },
8605
8606     /**
8607      * Gets a range template nodes.
8608      * @param {Number} startIndex
8609      * @param {Number} endIndex
8610      * @return {Array} An array of nodes
8611      */
8612     getNodes : function(start, end){
8613         var ns = this.nodes;
8614         start = start || 0;
8615         end = typeof end == "undefined" ? ns.length - 1 : end;
8616         var nodes = [];
8617         if(start <= end){
8618             for(var i = start; i <= end; i++){
8619                 nodes.push(ns[i]);
8620             }
8621         } else{
8622             for(var i = start; i >= end; i--){
8623                 nodes.push(ns[i]);
8624             }
8625         }
8626         return nodes;
8627     },
8628
8629     /**
8630      * Finds the index of the passed node
8631      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
8632      * @return {Number} The index of the node or -1
8633      */
8634     indexOf : function(node){
8635         node = this.getNode(node);
8636         if(typeof node.nodeIndex == "number"){
8637             return node.nodeIndex;
8638         }
8639         var ns = this.nodes;
8640         for(var i = 0, len = ns.length; i < len; i++){
8641             if(ns[i] == node){
8642                 return i;
8643             }
8644         }
8645         return -1;
8646     }
8647 });
8648 /*
8649  * - LGPL
8650  *
8651  * based on jquery fullcalendar
8652  * 
8653  */
8654
8655 Roo.bootstrap = Roo.bootstrap || {};
8656 /**
8657  * @class Roo.bootstrap.Calendar
8658  * @extends Roo.bootstrap.Component
8659  * Bootstrap Calendar class
8660  * @cfg {Boolean} loadMask (true|false) default false
8661     
8662  * @constructor
8663  * Create a new Container
8664  * @param {Object} config The config object
8665  */
8666
8667
8668
8669 Roo.bootstrap.Calendar = function(config){
8670     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
8671      this.addEvents({
8672         /**
8673              * @event select
8674              * Fires when a date is selected
8675              * @param {DatePicker} this
8676              * @param {Date} date The selected date
8677              */
8678         'select': true,
8679         /**
8680              * @event monthchange
8681              * Fires when the displayed month changes 
8682              * @param {DatePicker} this
8683              * @param {Date} date The selected month
8684              */
8685         'monthchange': true,
8686         /**
8687              * @event evententer
8688              * Fires when mouse over an event
8689              * @param {Calendar} this
8690              * @param {event} Event
8691              */
8692         'evententer': true,
8693         /**
8694              * @event eventleave
8695              * Fires when the mouse leaves an
8696              * @param {Calendar} this
8697              * @param {event}
8698              */
8699         'eventleave': true,
8700         /**
8701              * @event eventclick
8702              * Fires when the mouse click an
8703              * @param {Calendar} this
8704              * @param {event}
8705              */
8706         'eventclick': true
8707         
8708     });
8709
8710 };
8711
8712 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
8713     
8714      /**
8715      * @cfg {Number} startDay
8716      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
8717      */
8718     startDay : 0,
8719     
8720     loadMask : false,
8721       
8722     getAutoCreate : function(){
8723         
8724         
8725         var fc_button = function(name, corner, style, content ) {
8726             return Roo.apply({},{
8727                 tag : 'span',
8728                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
8729                          (corner.length ?
8730                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
8731                             ''
8732                         ),
8733                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
8734                 unselectable: 'on'
8735             });
8736         };
8737         
8738         var header = {
8739             tag : 'table',
8740             cls : 'fc-header',
8741             style : 'width:100%',
8742             cn : [
8743                 {
8744                     tag: 'tr',
8745                     cn : [
8746                         {
8747                             tag : 'td',
8748                             cls : 'fc-header-left',
8749                             cn : [
8750                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
8751                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
8752                                 { tag: 'span', cls: 'fc-header-space' },
8753                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
8754                                 
8755                                 
8756                             ]
8757                         },
8758                         
8759                         {
8760                             tag : 'td',
8761                             cls : 'fc-header-center',
8762                             cn : [
8763                                 {
8764                                     tag: 'span',
8765                                     cls: 'fc-header-title',
8766                                     cn : {
8767                                         tag: 'H2',
8768                                         html : 'month / year'
8769                                     }
8770                                 }
8771                                 
8772                             ]
8773                         },
8774                         {
8775                             tag : 'td',
8776                             cls : 'fc-header-right',
8777                             cn : [
8778                           /*      fc_button('month', 'left', '', 'month' ),
8779                                 fc_button('week', '', '', 'week' ),
8780                                 fc_button('day', 'right', '', 'day' )
8781                             */    
8782                                 
8783                             ]
8784                         }
8785                         
8786                     ]
8787                 }
8788             ]
8789         };
8790         
8791        
8792         var cal_heads = function() {
8793             var ret = [];
8794             // fixme - handle this.
8795             
8796             for (var i =0; i < Date.dayNames.length; i++) {
8797                 var d = Date.dayNames[i];
8798                 ret.push({
8799                     tag: 'th',
8800                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
8801                     html : d.substring(0,3)
8802                 });
8803                 
8804             }
8805             ret[0].cls += ' fc-first';
8806             ret[6].cls += ' fc-last';
8807             return ret;
8808         };
8809         var cal_cell = function(n) {
8810             return  {
8811                 tag: 'td',
8812                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
8813                 cn : [
8814                     {
8815                         cn : [
8816                             {
8817                                 cls: 'fc-day-number',
8818                                 html: 'D'
8819                             },
8820                             {
8821                                 cls: 'fc-day-content',
8822                              
8823                                 cn : [
8824                                      {
8825                                         style: 'position: relative;' // height: 17px;
8826                                     }
8827                                 ]
8828                             }
8829                             
8830                             
8831                         ]
8832                     }
8833                 ]
8834                 
8835             }
8836         };
8837         var cal_rows = function() {
8838             
8839             var ret = []
8840             for (var r = 0; r < 6; r++) {
8841                 var row= {
8842                     tag : 'tr',
8843                     cls : 'fc-week',
8844                     cn : []
8845                 };
8846                 
8847                 for (var i =0; i < Date.dayNames.length; i++) {
8848                     var d = Date.dayNames[i];
8849                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
8850
8851                 }
8852                 row.cn[0].cls+=' fc-first';
8853                 row.cn[0].cn[0].style = 'min-height:90px';
8854                 row.cn[6].cls+=' fc-last';
8855                 ret.push(row);
8856                 
8857             }
8858             ret[0].cls += ' fc-first';
8859             ret[4].cls += ' fc-prev-last';
8860             ret[5].cls += ' fc-last';
8861             return ret;
8862             
8863         };
8864         
8865         var cal_table = {
8866             tag: 'table',
8867             cls: 'fc-border-separate',
8868             style : 'width:100%',
8869             cellspacing  : 0,
8870             cn : [
8871                 { 
8872                     tag: 'thead',
8873                     cn : [
8874                         { 
8875                             tag: 'tr',
8876                             cls : 'fc-first fc-last',
8877                             cn : cal_heads()
8878                         }
8879                     ]
8880                 },
8881                 { 
8882                     tag: 'tbody',
8883                     cn : cal_rows()
8884                 }
8885                   
8886             ]
8887         };
8888          
8889          var cfg = {
8890             cls : 'fc fc-ltr',
8891             cn : [
8892                 header,
8893                 {
8894                     cls : 'fc-content',
8895                     style : "position: relative;",
8896                     cn : [
8897                         {
8898                             cls : 'fc-view fc-view-month fc-grid',
8899                             style : 'position: relative',
8900                             unselectable : 'on',
8901                             cn : [
8902                                 {
8903                                     cls : 'fc-event-container',
8904                                     style : 'position:absolute;z-index:8;top:0;left:0;'
8905                                 },
8906                                 cal_table
8907                             ]
8908                         }
8909                     ]
8910     
8911                 }
8912            ] 
8913             
8914         };
8915         
8916          
8917         
8918         return cfg;
8919     },
8920     
8921     
8922     initEvents : function()
8923     {
8924         if(!this.store){
8925             throw "can not find store for calendar";
8926         }
8927         
8928         var mark = {
8929             tag: "div",
8930             cls:"x-dlg-mask",
8931             style: "text-align:center",
8932             cn: [
8933                 {
8934                     tag: "div",
8935                     style: "background-color:white;width:50%;margin:250 auto",
8936                     cn: [
8937                         {
8938                             tag: "img",
8939                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
8940                         },
8941                         {
8942                             tag: "span",
8943                             html: "Loading"
8944                         }
8945                         
8946                     ]
8947                 }
8948             ]
8949         }
8950         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
8951         
8952         var size = this.el.select('.fc-content', true).first().getSize();
8953         this.maskEl.setSize(size.width, size.height);
8954         this.maskEl.enableDisplayMode("block");
8955         if(!this.loadMask){
8956             this.maskEl.hide();
8957         }
8958         
8959         this.store = Roo.factory(this.store, Roo.data);
8960         this.store.on('load', this.onLoad, this);
8961         this.store.on('beforeload', this.onBeforeLoad, this);
8962         
8963         this.resize();
8964         
8965         this.cells = this.el.select('.fc-day',true);
8966         //Roo.log(this.cells);
8967         this.textNodes = this.el.query('.fc-day-number');
8968         this.cells.addClassOnOver('fc-state-hover');
8969         
8970         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
8971         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
8972         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
8973         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
8974         
8975         this.on('monthchange', this.onMonthChange, this);
8976         
8977         this.update(new Date().clearTime());
8978     },
8979     
8980     resize : function() {
8981         var sz  = this.el.getSize();
8982         
8983         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
8984         this.el.select('.fc-day-content div',true).setHeight(34);
8985     },
8986     
8987     
8988     // private
8989     showPrevMonth : function(e){
8990         this.update(this.activeDate.add("mo", -1));
8991     },
8992     showToday : function(e){
8993         this.update(new Date().clearTime());
8994     },
8995     // private
8996     showNextMonth : function(e){
8997         this.update(this.activeDate.add("mo", 1));
8998     },
8999
9000     // private
9001     showPrevYear : function(){
9002         this.update(this.activeDate.add("y", -1));
9003     },
9004
9005     // private
9006     showNextYear : function(){
9007         this.update(this.activeDate.add("y", 1));
9008     },
9009
9010     
9011    // private
9012     update : function(date)
9013     {
9014         var vd = this.activeDate;
9015         this.activeDate = date;
9016 //        if(vd && this.el){
9017 //            var t = date.getTime();
9018 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
9019 //                Roo.log('using add remove');
9020 //                
9021 //                this.fireEvent('monthchange', this, date);
9022 //                
9023 //                this.cells.removeClass("fc-state-highlight");
9024 //                this.cells.each(function(c){
9025 //                   if(c.dateValue == t){
9026 //                       c.addClass("fc-state-highlight");
9027 //                       setTimeout(function(){
9028 //                            try{c.dom.firstChild.focus();}catch(e){}
9029 //                       }, 50);
9030 //                       return false;
9031 //                   }
9032 //                   return true;
9033 //                });
9034 //                return;
9035 //            }
9036 //        }
9037         
9038         var days = date.getDaysInMonth();
9039         
9040         var firstOfMonth = date.getFirstDateOfMonth();
9041         var startingPos = firstOfMonth.getDay()-this.startDay;
9042         
9043         if(startingPos < this.startDay){
9044             startingPos += 7;
9045         }
9046         
9047         var pm = date.add(Date.MONTH, -1);
9048         var prevStart = pm.getDaysInMonth()-startingPos;
9049 //        
9050         this.cells = this.el.select('.fc-day',true);
9051         this.textNodes = this.el.query('.fc-day-number');
9052         this.cells.addClassOnOver('fc-state-hover');
9053         
9054         var cells = this.cells.elements;
9055         var textEls = this.textNodes;
9056         
9057         Roo.each(cells, function(cell){
9058             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
9059         });
9060         
9061         days += startingPos;
9062
9063         // convert everything to numbers so it's fast
9064         var day = 86400000;
9065         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
9066         //Roo.log(d);
9067         //Roo.log(pm);
9068         //Roo.log(prevStart);
9069         
9070         var today = new Date().clearTime().getTime();
9071         var sel = date.clearTime().getTime();
9072         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
9073         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
9074         var ddMatch = this.disabledDatesRE;
9075         var ddText = this.disabledDatesText;
9076         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
9077         var ddaysText = this.disabledDaysText;
9078         var format = this.format;
9079         
9080         var setCellClass = function(cal, cell){
9081             
9082             //Roo.log('set Cell Class');
9083             cell.title = "";
9084             var t = d.getTime();
9085             
9086             //Roo.log(d);
9087             
9088             cell.dateValue = t;
9089             if(t == today){
9090                 cell.className += " fc-today";
9091                 cell.className += " fc-state-highlight";
9092                 cell.title = cal.todayText;
9093             }
9094             if(t == sel){
9095                 // disable highlight in other month..
9096                 //cell.className += " fc-state-highlight";
9097                 
9098             }
9099             // disabling
9100             if(t < min) {
9101                 cell.className = " fc-state-disabled";
9102                 cell.title = cal.minText;
9103                 return;
9104             }
9105             if(t > max) {
9106                 cell.className = " fc-state-disabled";
9107                 cell.title = cal.maxText;
9108                 return;
9109             }
9110             if(ddays){
9111                 if(ddays.indexOf(d.getDay()) != -1){
9112                     cell.title = ddaysText;
9113                     cell.className = " fc-state-disabled";
9114                 }
9115             }
9116             if(ddMatch && format){
9117                 var fvalue = d.dateFormat(format);
9118                 if(ddMatch.test(fvalue)){
9119                     cell.title = ddText.replace("%0", fvalue);
9120                     cell.className = " fc-state-disabled";
9121                 }
9122             }
9123             
9124             if (!cell.initialClassName) {
9125                 cell.initialClassName = cell.dom.className;
9126             }
9127             
9128             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
9129         };
9130
9131         var i = 0;
9132         
9133         for(; i < startingPos; i++) {
9134             textEls[i].innerHTML = (++prevStart);
9135             d.setDate(d.getDate()+1);
9136             
9137             cells[i].className = "fc-past fc-other-month";
9138             setCellClass(this, cells[i]);
9139         }
9140         
9141         var intDay = 0;
9142         
9143         for(; i < days; i++){
9144             intDay = i - startingPos + 1;
9145             textEls[i].innerHTML = (intDay);
9146             d.setDate(d.getDate()+1);
9147             
9148             cells[i].className = ''; // "x-date-active";
9149             setCellClass(this, cells[i]);
9150         }
9151         var extraDays = 0;
9152         
9153         for(; i < 42; i++) {
9154             textEls[i].innerHTML = (++extraDays);
9155             d.setDate(d.getDate()+1);
9156             
9157             cells[i].className = "fc-future fc-other-month";
9158             setCellClass(this, cells[i]);
9159         }
9160         
9161         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
9162         
9163         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
9164         
9165         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
9166         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
9167         
9168         if(totalRows != 6){
9169             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
9170             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
9171         }
9172         
9173         this.fireEvent('monthchange', this, date);
9174         
9175         
9176         /*
9177         if(!this.internalRender){
9178             var main = this.el.dom.firstChild;
9179             var w = main.offsetWidth;
9180             this.el.setWidth(w + this.el.getBorderWidth("lr"));
9181             Roo.fly(main).setWidth(w);
9182             this.internalRender = true;
9183             // opera does not respect the auto grow header center column
9184             // then, after it gets a width opera refuses to recalculate
9185             // without a second pass
9186             if(Roo.isOpera && !this.secondPass){
9187                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
9188                 this.secondPass = true;
9189                 this.update.defer(10, this, [date]);
9190             }
9191         }
9192         */
9193         
9194     },
9195     
9196     findCell : function(dt) {
9197         dt = dt.clearTime().getTime();
9198         var ret = false;
9199         this.cells.each(function(c){
9200             //Roo.log("check " +c.dateValue + '?=' + dt);
9201             if(c.dateValue == dt){
9202                 ret = c;
9203                 return false;
9204             }
9205             return true;
9206         });
9207         
9208         return ret;
9209     },
9210     
9211     findCells : function(ev) {
9212         var s = ev.start.clone().clearTime().getTime();
9213        // Roo.log(s);
9214         var e= ev.end.clone().clearTime().getTime();
9215        // Roo.log(e);
9216         var ret = [];
9217         this.cells.each(function(c){
9218              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
9219             
9220             if(c.dateValue > e){
9221                 return ;
9222             }
9223             if(c.dateValue < s){
9224                 return ;
9225             }
9226             ret.push(c);
9227         });
9228         
9229         return ret;    
9230     },
9231     
9232     findBestRow: function(cells)
9233     {
9234         var ret = 0;
9235         
9236         for (var i =0 ; i < cells.length;i++) {
9237             ret  = Math.max(cells[i].rows || 0,ret);
9238         }
9239         return ret;
9240         
9241     },
9242     
9243     
9244     addItem : function(ev)
9245     {
9246         // look for vertical location slot in
9247         var cells = this.findCells(ev);
9248         
9249         ev.row = this.findBestRow(cells);
9250         
9251         // work out the location.
9252         
9253         var crow = false;
9254         var rows = [];
9255         for(var i =0; i < cells.length; i++) {
9256             if (!crow) {
9257                 crow = {
9258                     start : cells[i],
9259                     end :  cells[i]
9260                 };
9261                 continue;
9262             }
9263             if (crow.start.getY() == cells[i].getY()) {
9264                 // on same row.
9265                 crow.end = cells[i];
9266                 continue;
9267             }
9268             // different row.
9269             rows.push(crow);
9270             crow = {
9271                 start: cells[i],
9272                 end : cells[i]
9273             };
9274             
9275         }
9276         
9277         rows.push(crow);
9278         ev.els = [];
9279         ev.rows = rows;
9280         ev.cells = cells;
9281         for (var i = 0; i < cells.length;i++) {
9282             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
9283             
9284         }
9285         
9286         this.calevents.push(ev);
9287     },
9288     
9289     clearEvents: function() {
9290         
9291         if(!this.calevents){
9292             return;
9293         }
9294         
9295         Roo.each(this.cells.elements, function(c){
9296             c.rows = 0;
9297         });
9298         
9299         Roo.each(this.calevents, function(e) {
9300             Roo.each(e.els, function(el) {
9301                 el.un('mouseenter' ,this.onEventEnter, this);
9302                 el.un('mouseleave' ,this.onEventLeave, this);
9303                 el.remove();
9304             },this);
9305         },this);
9306         
9307     },
9308     
9309     renderEvents: function()
9310     {   
9311         // first make sure there is enough space..
9312         
9313         this.cells.each(function(c) {
9314         
9315             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
9316         });
9317         
9318         for (var e = 0; e < this.calevents.length; e++) {
9319             var ev = this.calevents[e];
9320             var cells = ev.cells;
9321             var rows = ev.rows;
9322             
9323             for(var i =0; i < rows.length; i++) {
9324                 
9325                  
9326                 // how many rows should it span..
9327                 
9328                 var  cfg = {
9329                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
9330                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
9331                     
9332                     unselectable : "on",
9333                     cn : [
9334                         {
9335                             cls: 'fc-event-inner',
9336                             cn : [
9337                                 {
9338                                   tag:'span',
9339                                   cls: 'fc-event-time',
9340                                   html : cells.length > 1 ? '' : ev.time
9341                                 },
9342                                 {
9343                                   tag:'span',
9344                                   cls: 'fc-event-title',
9345                                   html : String.format('{0}', ev.title)
9346                                 }
9347                                 
9348                                 
9349                             ]
9350                         },
9351                         {
9352                             cls: 'ui-resizable-handle ui-resizable-e',
9353                             html : '&nbsp;&nbsp;&nbsp'
9354                         }
9355                         
9356                     ]
9357                 };
9358                 if (i == 0) {
9359                     cfg.cls += ' fc-event-start';
9360                 }
9361                 if ((i+1) == rows.length) {
9362                     cfg.cls += ' fc-event-end';
9363                 }
9364                 
9365                 var ctr = this.el.select('.fc-event-container',true).first();
9366                 var cg = ctr.createChild(cfg);
9367                 
9368                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
9369                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
9370                 cg.on('click', this.onEventClick, this, ev);
9371                 
9372                 ev.els.push(cg);
9373                 
9374                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
9375                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
9376                 //Roo.log(cg);
9377                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
9378                 cg.setWidth(ebox.right - sbox.x -2);
9379             }
9380             
9381             
9382         }
9383         
9384     },
9385     
9386     onEventEnter: function (e, el,event,d) {
9387         this.fireEvent('evententer', this, el, event);
9388     },
9389     
9390     onEventLeave: function (e, el,event,d) {
9391         this.fireEvent('eventleave', this, el, event);
9392     },
9393     
9394     onEventClick: function (e, el,event,d) {
9395         this.fireEvent('eventclick', this, el, event);
9396     },
9397     
9398     onMonthChange: function () {
9399         this.store.load();
9400     },
9401     
9402     onLoad: function () 
9403     {   
9404         this.calevents = [];
9405         var cal = this;
9406         if(this.store.getCount() > 0){
9407             this.store.data.each(function(d){
9408                cal.addItem({
9409                     id : d.data.id,
9410                     start: new Date(d.data.start_dt),
9411                     end : new Date(d.data.end_dt),
9412                     time : d.data.start_time,
9413                     title : d.data.title,
9414                     description : d.data.description,
9415                     venue : d.data.venue
9416                 });
9417             });
9418         }
9419         
9420         this.renderEvents();
9421         
9422         if(this.loadMask){
9423             this.maskEl.hide();
9424         }
9425     },
9426     
9427     onBeforeLoad: function()
9428     {
9429         this.clearEvents();
9430         
9431         if(this.loadMask){
9432             this.maskEl.show();
9433         }
9434     }
9435 });
9436
9437  
9438  /*
9439  * - LGPL
9440  *
9441  * element
9442  * 
9443  */
9444
9445 /**
9446  * @class Roo.bootstrap.Popover
9447  * @extends Roo.bootstrap.Component
9448  * Bootstrap Popover class
9449  * @cfg {String} html contents of the popover   (or false to use children..)
9450  * @cfg {String} title of popover (or false to hide)
9451  * @cfg {String} placement how it is placed
9452  * @cfg {String} trigger click || hover (or false to trigger manually)
9453  * @cfg {String} over what (parent or false to trigger manually.)
9454  * 
9455  * @constructor
9456  * Create a new Popover
9457  * @param {Object} config The config object
9458  */
9459
9460 Roo.bootstrap.Popover = function(config){
9461     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
9462 };
9463
9464 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
9465     
9466     title: 'Fill in a title',
9467     html: false,
9468     
9469     placement : 'right',
9470     trigger : 'hover', // hover
9471     
9472     over: 'parent',
9473     
9474     can_build_overlaid : false,
9475     
9476     getChildContainer : function()
9477     {
9478         return this.el.select('.popover-content',true).first();
9479     },
9480     
9481     getAutoCreate : function(){
9482          Roo.log('make popover?');
9483         var cfg = {
9484            cls : 'popover roo-dynamic',
9485            style: 'display:block',
9486            cn : [
9487                 {
9488                     cls : 'arrow'
9489                 },
9490                 {
9491                     cls : 'popover-inner',
9492                     cn : [
9493                         {
9494                             tag: 'h3',
9495                             cls: 'popover-title',
9496                             html : this.title
9497                         },
9498                         {
9499                             cls : 'popover-content',
9500                             html : this.html
9501                         }
9502                     ]
9503                     
9504                 }
9505            ]
9506         };
9507         
9508         return cfg;
9509     },
9510     setTitle: function(str)
9511     {
9512         this.el.select('.popover-title',true).first().dom.innerHTML = str;
9513     },
9514     setContent: function(str)
9515     {
9516         this.el.select('.popover-content',true).first().dom.innerHTML = str;
9517     },
9518     // as it get's added to the bottom of the page.
9519     onRender : function(ct, position)
9520     {
9521         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
9522         if(!this.el){
9523             var cfg = Roo.apply({},  this.getAutoCreate());
9524             cfg.id = Roo.id();
9525             
9526             if (this.cls) {
9527                 cfg.cls += ' ' + this.cls;
9528             }
9529             if (this.style) {
9530                 cfg.style = this.style;
9531             }
9532             Roo.log("adding to ")
9533             this.el = Roo.get(document.body).createChild(cfg, position);
9534             Roo.log(this.el);
9535         }
9536         this.initEvents();
9537     },
9538     
9539     initEvents : function()
9540     {
9541         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
9542         this.el.enableDisplayMode('block');
9543         this.el.hide();
9544         if (this.over === false) {
9545             return; 
9546         }
9547         if (this.triggers === false) {
9548             return;
9549         }
9550         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
9551         var triggers = this.trigger ? this.trigger.split(' ') : [];
9552         Roo.each(triggers, function(trigger) {
9553         
9554             if (trigger == 'click') {
9555                 on_el.on('click', this.toggle, this);
9556             } else if (trigger != 'manual') {
9557                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
9558                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
9559       
9560                 on_el.on(eventIn  ,this.enter, this);
9561                 on_el.on(eventOut, this.leave, this);
9562             }
9563         }, this);
9564         
9565     },
9566     
9567     
9568     // private
9569     timeout : null,
9570     hoverState : null,
9571     
9572     toggle : function () {
9573         this.hoverState == 'in' ? this.leave() : this.enter();
9574     },
9575     
9576     enter : function () {
9577        
9578     
9579         clearTimeout(this.timeout);
9580     
9581         this.hoverState = 'in'
9582     
9583         if (!this.delay || !this.delay.show) {
9584             this.show();
9585             return 
9586         }
9587         var _t = this;
9588         this.timeout = setTimeout(function () {
9589             if (_t.hoverState == 'in') {
9590                 _t.show();
9591             }
9592         }, this.delay.show)
9593     },
9594     leave : function() {
9595         clearTimeout(this.timeout);
9596     
9597         this.hoverState = 'out'
9598     
9599         if (!this.delay || !this.delay.hide) {
9600             this.hide();
9601             return 
9602         }
9603         var _t = this;
9604         this.timeout = setTimeout(function () {
9605             if (_t.hoverState == 'out') {
9606                 _t.hide();
9607             }
9608         }, this.delay.hide)
9609     },
9610     
9611     show : function (on_el)
9612     {
9613         if (!on_el) {
9614             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
9615         }
9616         // set content.
9617         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
9618         if (this.html !== false) {
9619             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
9620         }
9621         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
9622         if (!this.title.length) {
9623             this.el.select('.popover-title',true).hide();
9624         }
9625         
9626         var placement = typeof this.placement == 'function' ?
9627             this.placement.call(this, this.el, on_el) :
9628             this.placement;
9629             
9630         var autoToken = /\s?auto?\s?/i;
9631         var autoPlace = autoToken.test(placement);
9632         if (autoPlace) {
9633             placement = placement.replace(autoToken, '') || 'top';
9634         }
9635         
9636         //this.el.detach()
9637         //this.el.setXY([0,0]);
9638         this.el.show();
9639         this.el.dom.style.display='block';
9640         this.el.addClass(placement);
9641         
9642         //this.el.appendTo(on_el);
9643         
9644         var p = this.getPosition();
9645         var box = this.el.getBox();
9646         
9647         if (autoPlace) {
9648             // fixme..
9649         }
9650         var align = Roo.bootstrap.Popover.alignment[placement]
9651         this.el.alignTo(on_el, align[0],align[1]);
9652         //var arrow = this.el.select('.arrow',true).first();
9653         //arrow.set(align[2], 
9654         
9655         this.el.addClass('in');
9656         this.hoverState = null;
9657         
9658         if (this.el.hasClass('fade')) {
9659             // fade it?
9660         }
9661         
9662     },
9663     hide : function()
9664     {
9665         this.el.setXY([0,0]);
9666         this.el.removeClass('in');
9667         this.el.hide();
9668         
9669     }
9670     
9671 });
9672
9673 Roo.bootstrap.Popover.alignment = {
9674     'left' : ['r-l', [-10,0], 'right'],
9675     'right' : ['l-r', [10,0], 'left'],
9676     'bottom' : ['t-b', [0,10], 'top'],
9677     'top' : [ 'b-t', [0,-10], 'bottom']
9678 };
9679
9680  /*
9681  * - LGPL
9682  *
9683  * Progress
9684  * 
9685  */
9686
9687 /**
9688  * @class Roo.bootstrap.Progress
9689  * @extends Roo.bootstrap.Component
9690  * Bootstrap Progress class
9691  * @cfg {Boolean} striped striped of the progress bar
9692  * @cfg {Boolean} active animated of the progress bar
9693  * 
9694  * 
9695  * @constructor
9696  * Create a new Progress
9697  * @param {Object} config The config object
9698  */
9699
9700 Roo.bootstrap.Progress = function(config){
9701     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
9702 };
9703
9704 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
9705     
9706     striped : false,
9707     active: false,
9708     
9709     getAutoCreate : function(){
9710         var cfg = {
9711             tag: 'div',
9712             cls: 'progress'
9713         };
9714         
9715         
9716         if(this.striped){
9717             cfg.cls += ' progress-striped';
9718         }
9719       
9720         if(this.active){
9721             cfg.cls += ' active';
9722         }
9723         
9724         
9725         return cfg;
9726     }
9727    
9728 });
9729
9730  
9731
9732  /*
9733  * - LGPL
9734  *
9735  * ProgressBar
9736  * 
9737  */
9738
9739 /**
9740  * @class Roo.bootstrap.ProgressBar
9741  * @extends Roo.bootstrap.Component
9742  * Bootstrap ProgressBar class
9743  * @cfg {Number} aria_valuenow aria-value now
9744  * @cfg {Number} aria_valuemin aria-value min
9745  * @cfg {Number} aria_valuemax aria-value max
9746  * @cfg {String} label label for the progress bar
9747  * @cfg {String} panel (success | info | warning | danger )
9748  * @cfg {String} role role of the progress bar
9749  * @cfg {String} sr_only text
9750  * 
9751  * 
9752  * @constructor
9753  * Create a new ProgressBar
9754  * @param {Object} config The config object
9755  */
9756
9757 Roo.bootstrap.ProgressBar = function(config){
9758     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
9759 };
9760
9761 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
9762     
9763     aria_valuenow : 0,
9764     aria_valuemin : 0,
9765     aria_valuemax : 100,
9766     label : false,
9767     panel : false,
9768     role : false,
9769     sr_only: false,
9770     
9771     getAutoCreate : function()
9772     {
9773         
9774         var cfg = {
9775             tag: 'div',
9776             cls: 'progress-bar',
9777             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
9778         };
9779         
9780         if(this.sr_only){
9781             cfg.cn = {
9782                 tag: 'span',
9783                 cls: 'sr-only',
9784                 html: this.sr_only
9785             }
9786         }
9787         
9788         if(this.role){
9789             cfg.role = this.role;
9790         }
9791         
9792         if(this.aria_valuenow){
9793             cfg['aria-valuenow'] = this.aria_valuenow;
9794         }
9795         
9796         if(this.aria_valuemin){
9797             cfg['aria-valuemin'] = this.aria_valuemin;
9798         }
9799         
9800         if(this.aria_valuemax){
9801             cfg['aria-valuemax'] = this.aria_valuemax;
9802         }
9803         
9804         if(this.label && !this.sr_only){
9805             cfg.html = this.label;
9806         }
9807         
9808         if(this.panel){
9809             cfg.cls += ' progress-bar-' + this.panel;
9810         }
9811         
9812         return cfg;
9813     },
9814     
9815     update : function(aria_valuenow)
9816     {
9817         this.aria_valuenow = aria_valuenow;
9818         
9819         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
9820     }
9821    
9822 });
9823
9824  
9825
9826  /*
9827  * - LGPL
9828  *
9829  * TabPanel
9830  * 
9831  */
9832
9833 /**
9834  * @class Roo.bootstrap.TabPanel
9835  * @extends Roo.bootstrap.Component
9836  * Bootstrap TabPanel class
9837  * @cfg {Boolean} active panel active
9838  * @cfg {String} html panel content
9839  * @cfg {String} tabId tab relate id
9840  * 
9841  * 
9842  * @constructor
9843  * Create a new TabPanel
9844  * @param {Object} config The config object
9845  */
9846
9847 Roo.bootstrap.TabPanel = function(config){
9848     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
9849 };
9850
9851 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
9852     
9853     active: false,
9854     html: false,
9855     tabId: false,
9856     
9857     getAutoCreate : function(){
9858         var cfg = {
9859             tag: 'div',
9860             cls: 'tab-pane',
9861             html: this.html || ''
9862         };
9863         
9864         if(this.active){
9865             cfg.cls += ' active';
9866         }
9867         
9868         if(this.tabId){
9869             cfg.tabId = this.tabId;
9870         }
9871         
9872         return cfg;
9873     }
9874    
9875 });
9876
9877  
9878
9879  /*
9880  * - LGPL
9881  *
9882  * DateField
9883  * 
9884  */
9885
9886 /**
9887  * @class Roo.bootstrap.DateField
9888  * @extends Roo.bootstrap.Input
9889  * Bootstrap DateField class
9890  * @cfg {Number} weekStart default 0
9891  * @cfg {Number} weekStart default 0
9892  * @cfg {Number} viewMode default empty, (months|years)
9893  * @cfg {Number} minViewMode default empty, (months|years)
9894  * @cfg {Number} startDate default -Infinity
9895  * @cfg {Number} endDate default Infinity
9896  * @cfg {Boolean} todayHighlight default false
9897  * @cfg {Boolean} todayBtn default false
9898  * @cfg {Boolean} calendarWeeks default false
9899  * @cfg {Object} daysOfWeekDisabled default empty
9900  * 
9901  * @cfg {Boolean} keyboardNavigation default true
9902  * @cfg {String} language default en
9903  * 
9904  * @constructor
9905  * Create a new DateField
9906  * @param {Object} config The config object
9907  */
9908
9909 Roo.bootstrap.DateField = function(config){
9910     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
9911 };
9912
9913 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
9914     
9915     /**
9916      * @cfg {String} format
9917      * The default date format string which can be overriden for localization support.  The format must be
9918      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9919      */
9920     format : "m/d/y",
9921     /**
9922      * @cfg {String} altFormats
9923      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
9924      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
9925      */
9926     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
9927     
9928     weekStart : 0,
9929     
9930     viewMode : '',
9931     
9932     minViewMode : '',
9933     
9934     todayHighlight : false,
9935     
9936     todayBtn: false,
9937     
9938     language: 'en',
9939     
9940     keyboardNavigation: true,
9941     
9942     calendarWeeks: false,
9943     
9944     startDate: -Infinity,
9945     
9946     endDate: Infinity,
9947     
9948     daysOfWeekDisabled: [],
9949     
9950     _events: [],
9951     
9952     UTCDate: function()
9953     {
9954         return new Date(Date.UTC.apply(Date, arguments));
9955     },
9956     
9957     UTCToday: function()
9958     {
9959         var today = new Date();
9960         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
9961     },
9962     
9963     getDate: function() {
9964             var d = this.getUTCDate();
9965             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
9966     },
9967     
9968     getUTCDate: function() {
9969             return this.date;
9970     },
9971     
9972     setDate: function(d) {
9973             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
9974     },
9975     
9976     setUTCDate: function(d) {
9977             this.date = d;
9978             this.setValue(this.formatDate(this.date));
9979     },
9980         
9981     onRender: function(ct, position)
9982     {
9983         
9984         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
9985         
9986         this.language = this.language || 'en';
9987         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
9988         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
9989         
9990         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
9991         this.format = this.format || 'm/d/y';
9992         this.isInline = false;
9993         this.isInput = true;
9994         this.component = this.el.select('.add-on', true).first() || false;
9995         this.component = (this.component && this.component.length === 0) ? false : this.component;
9996         this.hasInput = this.component && this.inputEL().length;
9997         
9998         if (typeof(this.minViewMode === 'string')) {
9999             switch (this.minViewMode) {
10000                 case 'months':
10001                     this.minViewMode = 1;
10002                     break;
10003                 case 'years':
10004                     this.minViewMode = 2;
10005                     break;
10006                 default:
10007                     this.minViewMode = 0;
10008                     break;
10009             }
10010         }
10011         
10012         if (typeof(this.viewMode === 'string')) {
10013             switch (this.viewMode) {
10014                 case 'months':
10015                     this.viewMode = 1;
10016                     break;
10017                 case 'years':
10018                     this.viewMode = 2;
10019                     break;
10020                 default:
10021                     this.viewMode = 0;
10022                     break;
10023             }
10024         }
10025                 
10026         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
10027         
10028         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10029         
10030         this.picker().on('mousedown', this.onMousedown, this);
10031         this.picker().on('click', this.onClick, this);
10032         
10033         this.picker().addClass('datepicker-dropdown');
10034         
10035         this.startViewMode = this.viewMode;
10036         
10037         
10038         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
10039             if(!this.calendarWeeks){
10040                 v.remove();
10041                 return;
10042             };
10043             
10044             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
10045             v.attr('colspan', function(i, val){
10046                 return parseInt(val) + 1;
10047             });
10048         })
10049                         
10050         
10051         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
10052         
10053         this.setStartDate(this.startDate);
10054         this.setEndDate(this.endDate);
10055         
10056         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
10057         
10058         this.fillDow();
10059         this.fillMonths();
10060         this.update();
10061         this.showMode();
10062         
10063         if(this.isInline) {
10064             this.show();
10065         }
10066     },
10067     
10068     picker : function()
10069     {
10070         return this.el.select('.datepicker', true).first();
10071     },
10072     
10073     fillDow: function()
10074     {
10075         var dowCnt = this.weekStart;
10076         
10077         var dow = {
10078             tag: 'tr',
10079             cn: [
10080                 
10081             ]
10082         };
10083         
10084         if(this.calendarWeeks){
10085             dow.cn.push({
10086                 tag: 'th',
10087                 cls: 'cw',
10088                 html: '&nbsp;'
10089             })
10090         }
10091         
10092         while (dowCnt < this.weekStart + 7) {
10093             dow.cn.push({
10094                 tag: 'th',
10095                 cls: 'dow',
10096                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
10097             });
10098         }
10099         
10100         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
10101     },
10102     
10103     fillMonths: function()
10104     {    
10105         var i = 0
10106         var months = this.picker().select('>.datepicker-months td', true).first();
10107         
10108         months.dom.innerHTML = '';
10109         
10110         while (i < 12) {
10111             var month = {
10112                 tag: 'span',
10113                 cls: 'month',
10114                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
10115             }
10116             
10117             months.createChild(month);
10118         }
10119         
10120     },
10121     
10122     update: function(){
10123         
10124         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
10125         
10126         if (this.date < this.startDate) {
10127             this.viewDate = new Date(this.startDate);
10128         } else if (this.date > this.endDate) {
10129             this.viewDate = new Date(this.endDate);
10130         } else {
10131             this.viewDate = new Date(this.date);
10132         }
10133         
10134         this.fill();
10135     },
10136     
10137     fill: function() {
10138         var d = new Date(this.viewDate),
10139                 year = d.getUTCFullYear(),
10140                 month = d.getUTCMonth(),
10141                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
10142                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
10143                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
10144                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
10145                 currentDate = this.date && this.date.valueOf(),
10146                 today = this.UTCToday();
10147         
10148         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
10149         
10150 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
10151         
10152 //        this.picker.select('>tfoot th.today').
10153 //                                              .text(dates[this.language].today)
10154 //                                              .toggle(this.todayBtn !== false);
10155     
10156         this.updateNavArrows();
10157         this.fillMonths();
10158                                                 
10159         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
10160         
10161         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
10162          
10163         prevMonth.setUTCDate(day);
10164         
10165         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
10166         
10167         var nextMonth = new Date(prevMonth);
10168         
10169         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
10170         
10171         nextMonth = nextMonth.valueOf();
10172         
10173         var fillMonths = false;
10174         
10175         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
10176         
10177         while(prevMonth.valueOf() < nextMonth) {
10178             var clsName = '';
10179             
10180             if (prevMonth.getUTCDay() === this.weekStart) {
10181                 if(fillMonths){
10182                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
10183                 }
10184                     
10185                 fillMonths = {
10186                     tag: 'tr',
10187                     cn: []
10188                 };
10189                 
10190                 if(this.calendarWeeks){
10191                     // ISO 8601: First week contains first thursday.
10192                     // ISO also states week starts on Monday, but we can be more abstract here.
10193                     var
10194                     // Start of current week: based on weekstart/current date
10195                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
10196                     // Thursday of this week
10197                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
10198                     // First Thursday of year, year from thursday
10199                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
10200                     // Calendar week: ms between thursdays, div ms per day, div 7 days
10201                     calWeek =  (th - yth) / 864e5 / 7 + 1;
10202                     
10203                     fillMonths.cn.push({
10204                         tag: 'td',
10205                         cls: 'cw',
10206                         html: calWeek
10207                     });
10208                 }
10209             }
10210             
10211             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
10212                 clsName += ' old';
10213             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
10214                 clsName += ' new';
10215             }
10216             if (this.todayHighlight &&
10217                 prevMonth.getUTCFullYear() == today.getFullYear() &&
10218                 prevMonth.getUTCMonth() == today.getMonth() &&
10219                 prevMonth.getUTCDate() == today.getDate()) {
10220                 clsName += ' today';
10221             }
10222             
10223             if (currentDate && prevMonth.valueOf() === currentDate) {
10224                 clsName += ' active';
10225             }
10226             
10227             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
10228                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
10229                     clsName += ' disabled';
10230             }
10231             
10232             fillMonths.cn.push({
10233                 tag: 'td',
10234                 cls: 'day ' + clsName,
10235                 html: prevMonth.getDate()
10236             })
10237             
10238             prevMonth.setDate(prevMonth.getDate()+1);
10239         }
10240           
10241         var currentYear = this.date && this.date.getUTCFullYear();
10242         var currentMonth = this.date && this.date.getUTCMonth();
10243         
10244         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
10245         
10246         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
10247             v.removeClass('active');
10248             
10249             if(currentYear === year && k === currentMonth){
10250                 v.addClass('active');
10251             }
10252             
10253             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
10254                 v.addClass('disabled');
10255             }
10256             
10257         });
10258         
10259         
10260         year = parseInt(year/10, 10) * 10;
10261         
10262         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
10263         
10264         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
10265         
10266         year -= 1;
10267         for (var i = -1; i < 11; i++) {
10268             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
10269                 tag: 'span',
10270                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
10271                 html: year
10272             })
10273             
10274             year += 1;
10275         }
10276     },
10277     
10278     showMode: function(dir) {
10279         if (dir) {
10280             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
10281         }
10282         Roo.each(this.picker().select('>div',true).elements, function(v){
10283             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10284             v.hide();
10285         });
10286         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
10287     },
10288     
10289     place: function()
10290     {
10291         if(this.isInline) return;
10292         
10293         this.picker().removeClass(['bottom', 'top']);
10294         
10295         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
10296             /*
10297              * place to the top of element!
10298              *
10299              */
10300             
10301             this.picker().addClass('top');
10302             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
10303             
10304             return;
10305         }
10306         
10307         this.picker().addClass('bottom');
10308         
10309         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
10310     },
10311     
10312     parseDate : function(value){
10313         if(!value || value instanceof Date){
10314             return value;
10315         }
10316         var v = Date.parseDate(value, this.format);
10317         if (!v && this.useIso) {
10318             v = Date.parseDate(value, 'Y-m-d');
10319         }
10320         if(!v && this.altFormats){
10321             if(!this.altFormatsArray){
10322                 this.altFormatsArray = this.altFormats.split("|");
10323             }
10324             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
10325                 v = Date.parseDate(value, this.altFormatsArray[i]);
10326             }
10327         }
10328         return v;
10329     },
10330     
10331     formatDate : function(date, fmt){
10332         return (!date || !(date instanceof Date)) ?
10333         date : date.dateFormat(fmt || this.format);
10334     },
10335     
10336     onFocus : function()
10337     {
10338         Roo.bootstrap.DateField.superclass.onFocus.call(this);
10339         this.show();
10340     },
10341     
10342     onBlur : function()
10343     {
10344         Roo.bootstrap.DateField.superclass.onBlur.call(this);
10345         this.hide();
10346     },
10347     
10348     show : function()
10349     {
10350         this.picker().show();
10351         this.update();
10352         this.place();
10353     },
10354     
10355     hide : function()
10356     {
10357         if(this.isInline) return;
10358         this.picker().hide();
10359         this.viewMode = this.startViewMode;
10360         this.showMode();
10361         
10362     },
10363     
10364     onMousedown: function(e){
10365         e.stopPropagation();
10366         e.preventDefault();
10367     },
10368     
10369     keyup: function(e){
10370         Roo.bootstrap.DateField.superclass.keyup.call(this);
10371         this.update();
10372         
10373     },
10374     
10375     fireKey: function(e){
10376         if (!this.picker().isVisible()){
10377             if (e.keyCode == 27) // allow escape to hide and re-show picker
10378                 this.show();
10379             return;
10380         }
10381         var dateChanged = false,
10382         dir, day, month,
10383         newDate, newViewDate;
10384         switch(e.keyCode){
10385             case 27: // escape
10386                 this.hide();
10387                 e.preventDefault();
10388                 break;
10389             case 37: // left
10390             case 39: // right
10391                 if (!this.keyboardNavigation) break;
10392                 dir = e.keyCode == 37 ? -1 : 1;
10393                 
10394                 if (e.ctrlKey){
10395                     newDate = this.moveYear(this.date, dir);
10396                     newViewDate = this.moveYear(this.viewDate, dir);
10397                 } else if (e.shiftKey){
10398                     newDate = this.moveMonth(this.date, dir);
10399                     newViewDate = this.moveMonth(this.viewDate, dir);
10400                 } else {
10401                     newDate = new Date(this.date);
10402                     newDate.setUTCDate(this.date.getUTCDate() + dir);
10403                     newViewDate = new Date(this.viewDate);
10404                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
10405                 }
10406                 if (this.dateWithinRange(newDate)){
10407                     this.date = newDate;
10408                     this.viewDate = newViewDate;
10409                     this.setValue(this.formatDate(this.date));
10410                     this.update();
10411                     e.preventDefault();
10412                     dateChanged = true;
10413                 }
10414                 break;
10415             case 38: // up
10416             case 40: // down
10417                 if (!this.keyboardNavigation) break;
10418                 dir = e.keyCode == 38 ? -1 : 1;
10419                 if (e.ctrlKey){
10420                     newDate = this.moveYear(this.date, dir);
10421                     newViewDate = this.moveYear(this.viewDate, dir);
10422                 } else if (e.shiftKey){
10423                     newDate = this.moveMonth(this.date, dir);
10424                     newViewDate = this.moveMonth(this.viewDate, dir);
10425                 } else {
10426                     newDate = new Date(this.date);
10427                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
10428                     newViewDate = new Date(this.viewDate);
10429                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
10430                 }
10431                 if (this.dateWithinRange(newDate)){
10432                     this.date = newDate;
10433                     this.viewDate = newViewDate;
10434                     this.setValue(this.formatDate(this.date));
10435                     this.update();
10436                     e.preventDefault();
10437                     dateChanged = true;
10438                 }
10439                 break;
10440             case 13: // enter
10441                 this.setValue(this.formatDate(this.date));
10442                 this.hide();
10443                 e.preventDefault();
10444                 break;
10445             case 9: // tab
10446                 this.setValue(this.formatDate(this.date));
10447                 this.hide();
10448                 break;
10449         }
10450     },
10451     
10452     
10453     onClick: function(e) {
10454         e.stopPropagation();
10455         e.preventDefault();
10456         
10457         var target = e.getTarget();
10458         
10459         if(target.nodeName.toLowerCase() === 'i'){
10460             target = Roo.get(target).dom.parentNode;
10461         }
10462         
10463         var nodeName = target.nodeName;
10464         var className = target.className;
10465         var html = target.innerHTML;
10466         
10467         switch(nodeName.toLowerCase()) {
10468             case 'th':
10469                 switch(className) {
10470                     case 'switch':
10471                         this.showMode(1);
10472                         break;
10473                     case 'prev':
10474                     case 'next':
10475                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
10476                         switch(this.viewMode){
10477                                 case 0:
10478                                         this.viewDate = this.moveMonth(this.viewDate, dir);
10479                                         break;
10480                                 case 1:
10481                                 case 2:
10482                                         this.viewDate = this.moveYear(this.viewDate, dir);
10483                                         break;
10484                         }
10485                         this.fill();
10486                         break;
10487                     case 'today':
10488                         var date = new Date();
10489                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
10490                         this.fill()
10491                         this.setValue(this.formatDate(this.date));
10492                         this.hide();
10493                         break;
10494                 }
10495                 break;
10496             case 'span':
10497                 if (className.indexOf('disabled') === -1) {
10498                     this.viewDate.setUTCDate(1);
10499                     if (className.indexOf('month') !== -1) {
10500                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
10501                     } else {
10502                         var year = parseInt(html, 10) || 0;
10503                         this.viewDate.setUTCFullYear(year);
10504                         
10505                     }
10506                     this.showMode(-1);
10507                     this.fill();
10508                 }
10509                 break;
10510                 
10511             case 'td':
10512                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
10513                     var day = parseInt(html, 10) || 1;
10514                     var year = this.viewDate.getUTCFullYear(),
10515                         month = this.viewDate.getUTCMonth();
10516
10517                     if (className.indexOf('old') !== -1) {
10518                         if(month === 0 ){
10519                             month = 11;
10520                             year -= 1;
10521                         }else{
10522                             month -= 1;
10523                         }
10524                     } else if (className.indexOf('new') !== -1) {
10525                         if (month == 11) {
10526                             month = 0;
10527                             year += 1;
10528                         } else {
10529                             month += 1;
10530                         }
10531                     }
10532                     this.date = this.UTCDate(year, month, day,0,0,0,0);
10533                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
10534                     this.fill();
10535                     this.setValue(this.formatDate(this.date));
10536                     this.hide();
10537                 }
10538                 break;
10539         }
10540     },
10541     
10542     setStartDate: function(startDate){
10543         this.startDate = startDate || -Infinity;
10544         if (this.startDate !== -Infinity) {
10545             this.startDate = this.parseDate(this.startDate);
10546         }
10547         this.update();
10548         this.updateNavArrows();
10549     },
10550
10551     setEndDate: function(endDate){
10552         this.endDate = endDate || Infinity;
10553         if (this.endDate !== Infinity) {
10554             this.endDate = this.parseDate(this.endDate);
10555         }
10556         this.update();
10557         this.updateNavArrows();
10558     },
10559     
10560     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
10561         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
10562         if (typeof(this.daysOfWeekDisabled) !== 'object') {
10563             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
10564         }
10565         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
10566             return parseInt(d, 10);
10567         });
10568         this.update();
10569         this.updateNavArrows();
10570     },
10571     
10572     updateNavArrows: function() {
10573         var d = new Date(this.viewDate),
10574         year = d.getUTCFullYear(),
10575         month = d.getUTCMonth();
10576         
10577         Roo.each(this.picker().select('.prev', true).elements, function(v){
10578             v.show();
10579             switch (this.viewMode) {
10580                 case 0:
10581
10582                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
10583                         v.hide();
10584                     }
10585                     break;
10586                 case 1:
10587                 case 2:
10588                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
10589                         v.hide();
10590                     }
10591                     break;
10592             }
10593         });
10594         
10595         Roo.each(this.picker().select('.next', true).elements, function(v){
10596             v.show();
10597             switch (this.viewMode) {
10598                 case 0:
10599
10600                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
10601                         v.hide();
10602                     }
10603                     break;
10604                 case 1:
10605                 case 2:
10606                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
10607                         v.hide();
10608                     }
10609                     break;
10610             }
10611         })
10612     },
10613     
10614     moveMonth: function(date, dir){
10615         if (!dir) return date;
10616         var new_date = new Date(date.valueOf()),
10617         day = new_date.getUTCDate(),
10618         month = new_date.getUTCMonth(),
10619         mag = Math.abs(dir),
10620         new_month, test;
10621         dir = dir > 0 ? 1 : -1;
10622         if (mag == 1){
10623             test = dir == -1
10624             // If going back one month, make sure month is not current month
10625             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
10626             ? function(){
10627                 return new_date.getUTCMonth() == month;
10628             }
10629             // If going forward one month, make sure month is as expected
10630             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
10631             : function(){
10632                 return new_date.getUTCMonth() != new_month;
10633             };
10634             new_month = month + dir;
10635             new_date.setUTCMonth(new_month);
10636             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
10637             if (new_month < 0 || new_month > 11)
10638                 new_month = (new_month + 12) % 12;
10639         } else {
10640             // For magnitudes >1, move one month at a time...
10641             for (var i=0; i<mag; i++)
10642                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
10643                 new_date = this.moveMonth(new_date, dir);
10644             // ...then reset the day, keeping it in the new month
10645             new_month = new_date.getUTCMonth();
10646             new_date.setUTCDate(day);
10647             test = function(){
10648                 return new_month != new_date.getUTCMonth();
10649             };
10650         }
10651         // Common date-resetting loop -- if date is beyond end of month, make it
10652         // end of month
10653         while (test()){
10654             new_date.setUTCDate(--day);
10655             new_date.setUTCMonth(new_month);
10656         }
10657         return new_date;
10658     },
10659
10660     moveYear: function(date, dir){
10661         return this.moveMonth(date, dir*12);
10662     },
10663
10664     dateWithinRange: function(date){
10665         return date >= this.startDate && date <= this.endDate;
10666     },
10667
10668     
10669     remove: function() {
10670         this.picker().remove();
10671     }
10672    
10673 });
10674
10675 Roo.apply(Roo.bootstrap.DateField,  {
10676     
10677     head : {
10678         tag: 'thead',
10679         cn: [
10680         {
10681             tag: 'tr',
10682             cn: [
10683             {
10684                 tag: 'th',
10685                 cls: 'prev',
10686                 html: '<i class="icon-arrow-left"/>'
10687             },
10688             {
10689                 tag: 'th',
10690                 cls: 'switch',
10691                 colspan: '5'
10692             },
10693             {
10694                 tag: 'th',
10695                 cls: 'next',
10696                 html: '<i class="icon-arrow-right"/>'
10697             }
10698
10699             ]
10700         }
10701         ]
10702     },
10703     
10704     content : {
10705         tag: 'tbody',
10706         cn: [
10707         {
10708             tag: 'tr',
10709             cn: [
10710             {
10711                 tag: 'td',
10712                 colspan: '7'
10713             }
10714             ]
10715         }
10716         ]
10717     },
10718     
10719     footer : {
10720         tag: 'tfoot',
10721         cn: [
10722         {
10723             tag: 'tr',
10724             cn: [
10725             {
10726                 tag: 'th',
10727                 colspan: '7',
10728                 cls: 'today'
10729             }
10730                     
10731             ]
10732         }
10733         ]
10734     },
10735     
10736     dates:{
10737         en: {
10738             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
10739             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
10740             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
10741             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
10742             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
10743             today: "Today"
10744         }
10745     },
10746     
10747     modes: [
10748     {
10749         clsName: 'days',
10750         navFnc: 'Month',
10751         navStep: 1
10752     },
10753     {
10754         clsName: 'months',
10755         navFnc: 'FullYear',
10756         navStep: 1
10757     },
10758     {
10759         clsName: 'years',
10760         navFnc: 'FullYear',
10761         navStep: 10
10762     }]
10763 });
10764
10765 Roo.apply(Roo.bootstrap.DateField,  {
10766   
10767     template : {
10768         tag: 'div',
10769         cls: 'datepicker dropdown-menu',
10770         cn: [
10771         {
10772             tag: 'div',
10773             cls: 'datepicker-days',
10774             cn: [
10775             {
10776                 tag: 'table',
10777                 cls: 'table-condensed',
10778                 cn:[
10779                 Roo.bootstrap.DateField.head,
10780                 {
10781                     tag: 'tbody'
10782                 },
10783                 Roo.bootstrap.DateField.footer
10784                 ]
10785             }
10786             ]
10787         },
10788         {
10789             tag: 'div',
10790             cls: 'datepicker-months',
10791             cn: [
10792             {
10793                 tag: 'table',
10794                 cls: 'table-condensed',
10795                 cn:[
10796                 Roo.bootstrap.DateField.head,
10797                 Roo.bootstrap.DateField.content,
10798                 Roo.bootstrap.DateField.footer
10799                 ]
10800             }
10801             ]
10802         },
10803         {
10804             tag: 'div',
10805             cls: 'datepicker-years',
10806             cn: [
10807             {
10808                 tag: 'table',
10809                 cls: 'table-condensed',
10810                 cn:[
10811                 Roo.bootstrap.DateField.head,
10812                 Roo.bootstrap.DateField.content,
10813                 Roo.bootstrap.DateField.footer
10814                 ]
10815             }
10816             ]
10817         }
10818         ]
10819     }
10820 });
10821
10822  
10823
10824  /*
10825  * - LGPL
10826  *
10827  * CheckBox
10828  * 
10829  */
10830
10831 /**
10832  * @class Roo.bootstrap.CheckBox
10833  * @extends Roo.bootstrap.Input
10834  * Bootstrap CheckBox class
10835  * 
10836  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
10837  * @cfg {String} boxLabel The text that appears beside the checkbox
10838  * @cfg {Boolean} checked initnal the element
10839  * 
10840  * @constructor
10841  * Create a new CheckBox
10842  * @param {Object} config The config object
10843  */
10844
10845 Roo.bootstrap.CheckBox = function(config){
10846     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
10847    
10848         this.addEvents({
10849             /**
10850             * @event check
10851             * Fires when the element is checked or unchecked.
10852             * @param {Roo.bootstrap.CheckBox} this This input
10853             * @param {Boolean} checked The new checked value
10854             */
10855            check : true
10856         });
10857 };
10858
10859 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
10860     
10861     inputType: 'checkbox',
10862     value: 1,
10863     valueOff: 0,
10864     boxLabel: false,
10865     checked: false,
10866     
10867     getAutoCreate : function()
10868     {
10869         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10870         
10871         var id = Roo.id();
10872         
10873         var cfg = {};
10874         
10875         cfg.cls = 'form-group' //input-group
10876         
10877         var input =  {
10878             tag: 'input',
10879             id : id,
10880             type : this.inputType,
10881             value : (!this.checked) ? this.valueOff : this.value,
10882             cls : 'form-box',
10883             placeholder : this.placeholder || ''
10884             
10885         };
10886         
10887         if (this.disabled) {
10888             input.disabled=true;
10889         }
10890         
10891         if(this.checked){
10892             input.checked = this.checked;
10893         }
10894         
10895         if (this.name) {
10896             input.name = this.name;
10897         }
10898         
10899         if (this.size) {
10900             input.cls += ' input-' + this.size;
10901         }
10902         
10903         var settings=this;
10904         ['xs','sm','md','lg'].map(function(size){
10905             if (settings[size]) {
10906                 cfg.cls += ' col-' + size + '-' + settings[size];
10907             }
10908         });
10909         
10910         var inputblock = input;
10911         
10912         if (this.before || this.after) {
10913             
10914             inputblock = {
10915                 cls : 'input-group',
10916                 cn :  [] 
10917             };
10918             if (this.before) {
10919                 inputblock.cn.push({
10920                     tag :'span',
10921                     cls : 'input-group-addon',
10922                     html : this.before
10923                 });
10924             }
10925             inputblock.cn.push(input);
10926             if (this.after) {
10927                 inputblock.cn.push({
10928                     tag :'span',
10929                     cls : 'input-group-addon',
10930                     html : this.after
10931                 });
10932             }
10933             
10934         };
10935         
10936         if (align ==='left' && this.fieldLabel.length) {
10937                 Roo.log("left and has label");
10938                 cfg.cn = [
10939                     
10940                     {
10941                         tag: 'label',
10942                         'for' :  id,
10943                         cls : 'control-label col-md-' + this.labelWidth,
10944                         html : this.fieldLabel
10945                         
10946                     },
10947                     {
10948                         cls : "col-md-" + (12 - this.labelWidth), 
10949                         cn: [
10950                             inputblock
10951                         ]
10952                     }
10953                     
10954                 ];
10955         } else if ( this.fieldLabel.length) {
10956                 Roo.log(" label");
10957                  cfg.cn = [
10958                    
10959                     {
10960                         tag: 'label',
10961                         'for': id,
10962                         cls: 'control-label box-input-label',
10963                         //cls : 'input-group-addon',
10964                         html : this.fieldLabel
10965                         
10966                     },
10967                     
10968                     inputblock
10969                     
10970                 ];
10971
10972         } else {
10973             
10974                    Roo.log(" no label && no align");
10975                 cfg.cn = [
10976                     
10977                         inputblock
10978                     
10979                 ];
10980                 
10981                 
10982         };
10983         
10984         if(this.boxLabel){
10985             cfg.cn.push({
10986                 tag: 'span',
10987                 'for': id,
10988                 cls: 'box-label',
10989                 html: this.boxLabel
10990             })
10991         }
10992         
10993         return cfg;
10994         
10995     },
10996     
10997     /**
10998      * return the real input element.
10999      */
11000     inputEl: function ()
11001     {
11002         return this.el.select('input.form-box',true).first();
11003     },
11004     
11005     initEvents : function()
11006     {
11007         Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
11008         
11009         this.inputEl().on('click', this.onClick,  this);
11010         
11011     },
11012     
11013     onClick : function()
11014     {   
11015         this.setChecked(!this.checked);
11016     },
11017     
11018     setChecked : function(state,suppressEvent)
11019     {
11020         this.checked = state;
11021         
11022         if(suppressEvent !== true){
11023             this.fireEvent('check', this, state);
11024         }
11025         
11026         this.inputEl().dom.value = state ? this.value : this.valueOff;
11027         
11028     }
11029 });
11030
11031  
11032 /*
11033  * - LGPL
11034  *
11035  * Radio
11036  * 
11037  */
11038
11039 /**
11040  * @class Roo.bootstrap.Radio
11041  * @extends Roo.bootstrap.CheckBox
11042  * Bootstrap Radio class
11043
11044  * @constructor
11045  * Create a new Radio
11046  * @param {Object} config The config object
11047  */
11048
11049 Roo.bootstrap.Radio = function(config){
11050     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
11051    
11052 };
11053
11054 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
11055     
11056     inputType: 'radio',
11057     
11058     getAutoCreate : function()
11059     {
11060         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11061         
11062         var id = Roo.id();
11063         
11064         var cfg = {};
11065         
11066         cfg.cls = 'form-group' //input-group
11067         
11068         var input =  {
11069             tag: 'input',
11070             id : id,
11071             type : this.inputType,
11072             value : (!this.checked) ? this.valueOff : this.value,
11073             cls : 'form-box',
11074             placeholder : this.placeholder || ''
11075             
11076         };
11077         
11078         if (this.disabled) {
11079             input.disabled=true;
11080         }
11081         
11082         if(this.checked){
11083             input.checked = this.checked;
11084         }
11085         
11086         if (this.name) {
11087             input.name = this.name;
11088         }
11089         
11090         if (this.size) {
11091             input.cls += ' input-' + this.size;
11092         }
11093         
11094         var settings=this;
11095         ['xs','sm','md','lg'].map(function(size){
11096             if (settings[size]) {
11097                 cfg.cls += ' col-' + size + '-' + settings[size];
11098             }
11099         });
11100         
11101         var inputblock = input;
11102         
11103         if (this.before || this.after) {
11104             
11105             inputblock = {
11106                 cls : 'input-group',
11107                 cn :  [] 
11108             };
11109             if (this.before) {
11110                 inputblock.cn.push({
11111                     tag :'span',
11112                     cls : 'input-group-addon',
11113                     html : this.before
11114                 });
11115             }
11116             inputblock.cn.push(input);
11117             if (this.after) {
11118                 inputblock.cn.push({
11119                     tag :'span',
11120                     cls : 'input-group-addon',
11121                     html : this.after
11122                 });
11123             }
11124             
11125         };
11126         
11127         if (align ==='left' && this.fieldLabel.length) {
11128                 Roo.log("left and has label");
11129                 cfg.cn = [
11130                     
11131                     {
11132                         tag: 'label',
11133                         'for' :  id,
11134                         cls : 'control-label col-md-' + this.labelWidth,
11135                         html : this.fieldLabel
11136                         
11137                     },
11138                     {
11139                         cls : "col-md-" + (12 - this.labelWidth), 
11140                         cn: [
11141                             inputblock
11142                         ]
11143                     }
11144                     
11145                 ];
11146         } else if ( this.fieldLabel.length) {
11147                 Roo.log(" label");
11148                  cfg.cn = [
11149                    
11150                     {
11151                         tag: 'label',
11152                         'for': id,
11153                         cls: 'control-label box-input-label',
11154                         //cls : 'input-group-addon',
11155                         html : this.fieldLabel
11156                         
11157                     },
11158                     
11159                     inputblock
11160                     
11161                 ];
11162
11163         } else {
11164             
11165                    Roo.log(" no label && no align");
11166                 cfg.cn = [
11167                     
11168                         inputblock
11169                     
11170                 ];
11171                 
11172                 
11173         };
11174         
11175         if(this.boxLabel){
11176             cfg.cn.push({
11177                 tag: 'span',
11178                 'for': id,
11179                 cls: 'box-label',
11180                 html: this.boxLabel
11181             })
11182         }
11183         
11184         return cfg;
11185         
11186     },
11187     
11188     onClick : function()
11189     {   
11190         this.setChecked(true);
11191     },
11192     
11193     setChecked : function(state,suppressEvent)
11194     {
11195         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
11196             v.checked = false;
11197         });
11198         
11199         this.checked = state;
11200         
11201         if(suppressEvent !== true){
11202             this.fireEvent('check', this, state);
11203         }
11204         
11205         this.inputEl().dom.value = state ? this.value : this.valueOff;
11206         
11207     },
11208     
11209     getGroupValue : function()
11210     {
11211         if(typeof(this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true)) == 'undefined'){
11212             return '';
11213         }
11214         
11215         return this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true).value;
11216     },
11217     
11218     /**
11219      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11220      * @return {Mixed} value The field value
11221      */
11222     getValue : function(){
11223         return this.getGroupValue();
11224     }
11225     
11226 });
11227
11228  
11229 /*
11230  * - LGPL
11231  *
11232  * HtmlEditor
11233  * 
11234  */
11235
11236 /**
11237  * @class Roo.bootstrap.HtmlEditor
11238  * @extends Roo.bootstrap.Component
11239  * Bootstrap HtmlEditor class
11240
11241  * @constructor
11242  * Create a new HtmlEditor
11243  * @param {Object} config The config object
11244  */
11245
11246 Roo.bootstrap.HtmlEditor = function(config){
11247     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
11248     
11249 };
11250
11251
11252 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.Component,  {
11253     
11254     getAutoCreate : function()
11255     {
11256 //        var cfg = {};
11257 //        
11258 //        var toolbar = new Roo.bootstrap.ButtonGroup({
11259 //            toolbar: true,
11260 //            cn: [
11261 //                {
11262 //                    new Roo.bootstrap.Button({
11263 //                        
11264 //                    })
11265 //                }
11266 //            ]
11267 //        });
11268 //        
11269 //        return cfg;
11270         
11271     }
11272 });
11273
11274 /**
11275  * @class Roo.bootstrap.Table.AbstractSelectionModel
11276  * @extends Roo.util.Observable
11277  * Abstract base class for grid SelectionModels.  It provides the interface that should be
11278  * implemented by descendant classes.  This class should not be directly instantiated.
11279  * @constructor
11280  */
11281 Roo.bootstrap.Table.AbstractSelectionModel = function(){
11282     this.locked = false;
11283     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
11284 };
11285
11286
11287 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
11288     /** @ignore Called by the grid automatically. Do not call directly. */
11289     init : function(grid){
11290         this.grid = grid;
11291         this.initEvents();
11292     },
11293
11294     /**
11295      * Locks the selections.
11296      */
11297     lock : function(){
11298         this.locked = true;
11299     },
11300
11301     /**
11302      * Unlocks the selections.
11303      */
11304     unlock : function(){
11305         this.locked = false;
11306     },
11307
11308     /**
11309      * Returns true if the selections are locked.
11310      * @return {Boolean}
11311      */
11312     isLocked : function(){
11313         return this.locked;
11314     }
11315 });
11316 /**
11317  * @class Roo.bootstrap.Table.ColumnModel
11318  * @extends Roo.util.Observable
11319  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
11320  * the columns in the table.
11321  
11322  * @constructor
11323  * @param {Object} config An Array of column config objects. See this class's
11324  * config objects for details.
11325 */
11326 Roo.bootstrap.Table.ColumnModel = function(config){
11327         /**
11328      * The config passed into the constructor
11329      */
11330     this.config = config;
11331     this.lookup = {};
11332
11333     // if no id, create one
11334     // if the column does not have a dataIndex mapping,
11335     // map it to the order it is in the config
11336     for(var i = 0, len = config.length; i < len; i++){
11337         var c = config[i];
11338         if(typeof c.dataIndex == "undefined"){
11339             c.dataIndex = i;
11340         }
11341         if(typeof c.renderer == "string"){
11342             c.renderer = Roo.util.Format[c.renderer];
11343         }
11344         if(typeof c.id == "undefined"){
11345             c.id = Roo.id();
11346         }
11347 //        if(c.editor && c.editor.xtype){
11348 //            c.editor  = Roo.factory(c.editor, Roo.grid);
11349 //        }
11350 //        if(c.editor && c.editor.isFormField){
11351 //            c.editor = new Roo.grid.GridEditor(c.editor);
11352 //        }
11353
11354         this.lookup[c.id] = c;
11355     }
11356
11357     /**
11358      * The width of columns which have no width specified (defaults to 100)
11359      * @type Number
11360      */
11361     this.defaultWidth = 100;
11362
11363     /**
11364      * Default sortable of columns which have no sortable specified (defaults to false)
11365      * @type Boolean
11366      */
11367     this.defaultSortable = false;
11368
11369     this.addEvents({
11370         /**
11371              * @event widthchange
11372              * Fires when the width of a column changes.
11373              * @param {ColumnModel} this
11374              * @param {Number} columnIndex The column index
11375              * @param {Number} newWidth The new width
11376              */
11377             "widthchange": true,
11378         /**
11379              * @event headerchange
11380              * Fires when the text of a header changes.
11381              * @param {ColumnModel} this
11382              * @param {Number} columnIndex The column index
11383              * @param {Number} newText The new header text
11384              */
11385             "headerchange": true,
11386         /**
11387              * @event hiddenchange
11388              * Fires when a column is hidden or "unhidden".
11389              * @param {ColumnModel} this
11390              * @param {Number} columnIndex The column index
11391              * @param {Boolean} hidden true if hidden, false otherwise
11392              */
11393             "hiddenchange": true,
11394             /**
11395          * @event columnmoved
11396          * Fires when a column is moved.
11397          * @param {ColumnModel} this
11398          * @param {Number} oldIndex
11399          * @param {Number} newIndex
11400          */
11401         "columnmoved" : true,
11402         /**
11403          * @event columlockchange
11404          * Fires when a column's locked state is changed
11405          * @param {ColumnModel} this
11406          * @param {Number} colIndex
11407          * @param {Boolean} locked true if locked
11408          */
11409         "columnlockchange" : true
11410     });
11411     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
11412 };
11413 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
11414     /**
11415      * @cfg {String} header The header text to display in the Grid view.
11416      */
11417     /**
11418      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
11419      * {@link Roo.data.Record} definition from which to draw the column's value. If not
11420      * specified, the column's index is used as an index into the Record's data Array.
11421      */
11422     /**
11423      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
11424      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
11425      */
11426     /**
11427      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
11428      * Defaults to the value of the {@link #defaultSortable} property.
11429      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
11430      */
11431     /**
11432      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
11433      */
11434     /**
11435      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
11436      */
11437     /**
11438      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
11439      */
11440     /**
11441      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
11442      */
11443     /**
11444      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
11445      * given the cell's data value. See {@link #setRenderer}. If not specified, the
11446      * default renderer uses the raw data value.
11447      */
11448     /**
11449      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
11450      */
11451
11452     /**
11453      * Returns the id of the column at the specified index.
11454      * @param {Number} index The column index
11455      * @return {String} the id
11456      */
11457     getColumnId : function(index){
11458         return this.config[index].id;
11459     },
11460
11461     /**
11462      * Returns the column for a specified id.
11463      * @param {String} id The column id
11464      * @return {Object} the column
11465      */
11466     getColumnById : function(id){
11467         return this.lookup[id];
11468     },
11469
11470     
11471     /**
11472      * Returns the column for a specified dataIndex.
11473      * @param {String} dataIndex The column dataIndex
11474      * @return {Object|Boolean} the column or false if not found
11475      */
11476     getColumnByDataIndex: function(dataIndex){
11477         var index = this.findColumnIndex(dataIndex);
11478         return index > -1 ? this.config[index] : false;
11479     },
11480     
11481     /**
11482      * Returns the index for a specified column id.
11483      * @param {String} id The column id
11484      * @return {Number} the index, or -1 if not found
11485      */
11486     getIndexById : function(id){
11487         for(var i = 0, len = this.config.length; i < len; i++){
11488             if(this.config[i].id == id){
11489                 return i;
11490             }
11491         }
11492         return -1;
11493     },
11494     
11495     /**
11496      * Returns the index for a specified column dataIndex.
11497      * @param {String} dataIndex The column dataIndex
11498      * @return {Number} the index, or -1 if not found
11499      */
11500     
11501     findColumnIndex : function(dataIndex){
11502         for(var i = 0, len = this.config.length; i < len; i++){
11503             if(this.config[i].dataIndex == dataIndex){
11504                 return i;
11505             }
11506         }
11507         return -1;
11508     },
11509     
11510     
11511     moveColumn : function(oldIndex, newIndex){
11512         var c = this.config[oldIndex];
11513         this.config.splice(oldIndex, 1);
11514         this.config.splice(newIndex, 0, c);
11515         this.dataMap = null;
11516         this.fireEvent("columnmoved", this, oldIndex, newIndex);
11517     },
11518
11519     isLocked : function(colIndex){
11520         return this.config[colIndex].locked === true;
11521     },
11522
11523     setLocked : function(colIndex, value, suppressEvent){
11524         if(this.isLocked(colIndex) == value){
11525             return;
11526         }
11527         this.config[colIndex].locked = value;
11528         if(!suppressEvent){
11529             this.fireEvent("columnlockchange", this, colIndex, value);
11530         }
11531     },
11532
11533     getTotalLockedWidth : function(){
11534         var totalWidth = 0;
11535         for(var i = 0; i < this.config.length; i++){
11536             if(this.isLocked(i) && !this.isHidden(i)){
11537                 this.totalWidth += this.getColumnWidth(i);
11538             }
11539         }
11540         return totalWidth;
11541     },
11542
11543     getLockedCount : function(){
11544         for(var i = 0, len = this.config.length; i < len; i++){
11545             if(!this.isLocked(i)){
11546                 return i;
11547             }
11548         }
11549     },
11550
11551     /**
11552      * Returns the number of columns.
11553      * @return {Number}
11554      */
11555     getColumnCount : function(visibleOnly){
11556         if(visibleOnly === true){
11557             var c = 0;
11558             for(var i = 0, len = this.config.length; i < len; i++){
11559                 if(!this.isHidden(i)){
11560                     c++;
11561                 }
11562             }
11563             return c;
11564         }
11565         return this.config.length;
11566     },
11567
11568     /**
11569      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
11570      * @param {Function} fn
11571      * @param {Object} scope (optional)
11572      * @return {Array} result
11573      */
11574     getColumnsBy : function(fn, scope){
11575         var r = [];
11576         for(var i = 0, len = this.config.length; i < len; i++){
11577             var c = this.config[i];
11578             if(fn.call(scope||this, c, i) === true){
11579                 r[r.length] = c;
11580             }
11581         }
11582         return r;
11583     },
11584
11585     /**
11586      * Returns true if the specified column is sortable.
11587      * @param {Number} col The column index
11588      * @return {Boolean}
11589      */
11590     isSortable : function(col){
11591         if(typeof this.config[col].sortable == "undefined"){
11592             return this.defaultSortable;
11593         }
11594         return this.config[col].sortable;
11595     },
11596
11597     /**
11598      * Returns the rendering (formatting) function defined for the column.
11599      * @param {Number} col The column index.
11600      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
11601      */
11602     getRenderer : function(col){
11603         if(!this.config[col].renderer){
11604             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
11605         }
11606         return this.config[col].renderer;
11607     },
11608
11609     /**
11610      * Sets the rendering (formatting) function for a column.
11611      * @param {Number} col The column index
11612      * @param {Function} fn The function to use to process the cell's raw data
11613      * to return HTML markup for the grid view. The render function is called with
11614      * the following parameters:<ul>
11615      * <li>Data value.</li>
11616      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
11617      * <li>css A CSS style string to apply to the table cell.</li>
11618      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
11619      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
11620      * <li>Row index</li>
11621      * <li>Column index</li>
11622      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
11623      */
11624     setRenderer : function(col, fn){
11625         this.config[col].renderer = fn;
11626     },
11627
11628     /**
11629      * Returns the width for the specified column.
11630      * @param {Number} col The column index
11631      * @return {Number}
11632      */
11633     getColumnWidth : function(col){
11634         return this.config[col].width * 1 || this.defaultWidth;
11635     },
11636
11637     /**
11638      * Sets the width for a column.
11639      * @param {Number} col The column index
11640      * @param {Number} width The new width
11641      */
11642     setColumnWidth : function(col, width, suppressEvent){
11643         this.config[col].width = width;
11644         this.totalWidth = null;
11645         if(!suppressEvent){
11646              this.fireEvent("widthchange", this, col, width);
11647         }
11648     },
11649
11650     /**
11651      * Returns the total width of all columns.
11652      * @param {Boolean} includeHidden True to include hidden column widths
11653      * @return {Number}
11654      */
11655     getTotalWidth : function(includeHidden){
11656         if(!this.totalWidth){
11657             this.totalWidth = 0;
11658             for(var i = 0, len = this.config.length; i < len; i++){
11659                 if(includeHidden || !this.isHidden(i)){
11660                     this.totalWidth += this.getColumnWidth(i);
11661                 }
11662             }
11663         }
11664         return this.totalWidth;
11665     },
11666
11667     /**
11668      * Returns the header for the specified column.
11669      * @param {Number} col The column index
11670      * @return {String}
11671      */
11672     getColumnHeader : function(col){
11673         return this.config[col].header;
11674     },
11675
11676     /**
11677      * Sets the header for a column.
11678      * @param {Number} col The column index
11679      * @param {String} header The new header
11680      */
11681     setColumnHeader : function(col, header){
11682         this.config[col].header = header;
11683         this.fireEvent("headerchange", this, col, header);
11684     },
11685
11686     /**
11687      * Returns the tooltip for the specified column.
11688      * @param {Number} col The column index
11689      * @return {String}
11690      */
11691     getColumnTooltip : function(col){
11692             return this.config[col].tooltip;
11693     },
11694     /**
11695      * Sets the tooltip for a column.
11696      * @param {Number} col The column index
11697      * @param {String} tooltip The new tooltip
11698      */
11699     setColumnTooltip : function(col, tooltip){
11700             this.config[col].tooltip = tooltip;
11701     },
11702
11703     /**
11704      * Returns the dataIndex for the specified column.
11705      * @param {Number} col The column index
11706      * @return {Number}
11707      */
11708     getDataIndex : function(col){
11709         return this.config[col].dataIndex;
11710     },
11711
11712     /**
11713      * Sets the dataIndex for a column.
11714      * @param {Number} col The column index
11715      * @param {Number} dataIndex The new dataIndex
11716      */
11717     setDataIndex : function(col, dataIndex){
11718         this.config[col].dataIndex = dataIndex;
11719     },
11720
11721     
11722     
11723     /**
11724      * Returns true if the cell is editable.
11725      * @param {Number} colIndex The column index
11726      * @param {Number} rowIndex The row index
11727      * @return {Boolean}
11728      */
11729     isCellEditable : function(colIndex, rowIndex){
11730         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
11731     },
11732
11733     /**
11734      * Returns the editor defined for the cell/column.
11735      * return false or null to disable editing.
11736      * @param {Number} colIndex The column index
11737      * @param {Number} rowIndex The row index
11738      * @return {Object}
11739      */
11740     getCellEditor : function(colIndex, rowIndex){
11741         return this.config[colIndex].editor;
11742     },
11743
11744     /**
11745      * Sets if a column is editable.
11746      * @param {Number} col The column index
11747      * @param {Boolean} editable True if the column is editable
11748      */
11749     setEditable : function(col, editable){
11750         this.config[col].editable = editable;
11751     },
11752
11753
11754     /**
11755      * Returns true if the column is hidden.
11756      * @param {Number} colIndex The column index
11757      * @return {Boolean}
11758      */
11759     isHidden : function(colIndex){
11760         return this.config[colIndex].hidden;
11761     },
11762
11763
11764     /**
11765      * Returns true if the column width cannot be changed
11766      */
11767     isFixed : function(colIndex){
11768         return this.config[colIndex].fixed;
11769     },
11770
11771     /**
11772      * Returns true if the column can be resized
11773      * @return {Boolean}
11774      */
11775     isResizable : function(colIndex){
11776         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
11777     },
11778     /**
11779      * Sets if a column is hidden.
11780      * @param {Number} colIndex The column index
11781      * @param {Boolean} hidden True if the column is hidden
11782      */
11783     setHidden : function(colIndex, hidden){
11784         this.config[colIndex].hidden = hidden;
11785         this.totalWidth = null;
11786         this.fireEvent("hiddenchange", this, colIndex, hidden);
11787     },
11788
11789     /**
11790      * Sets the editor for a column.
11791      * @param {Number} col The column index
11792      * @param {Object} editor The editor object
11793      */
11794     setEditor : function(col, editor){
11795         this.config[col].editor = editor;
11796     }
11797 });
11798
11799 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
11800         if(typeof value == "string" && value.length < 1){
11801             return "&#160;";
11802         }
11803         return value;
11804 };
11805
11806 // Alias for backwards compatibility
11807 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
11808
11809 /**
11810  * @extends Roo.bootstrap.Table.AbstractSelectionModel
11811  * @class Roo.bootstrap.Table.RowSelectionModel
11812  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
11813  * It supports multiple selections and keyboard selection/navigation. 
11814  * @constructor
11815  * @param {Object} config
11816  */
11817
11818 Roo.bootstrap.Table.RowSelectionModel = function(config){
11819     Roo.apply(this, config);
11820     this.selections = new Roo.util.MixedCollection(false, function(o){
11821         return o.id;
11822     });
11823
11824     this.last = false;
11825     this.lastActive = false;
11826
11827     this.addEvents({
11828         /**
11829              * @event selectionchange
11830              * Fires when the selection changes
11831              * @param {SelectionModel} this
11832              */
11833             "selectionchange" : true,
11834         /**
11835              * @event afterselectionchange
11836              * Fires after the selection changes (eg. by key press or clicking)
11837              * @param {SelectionModel} this
11838              */
11839             "afterselectionchange" : true,
11840         /**
11841              * @event beforerowselect
11842              * Fires when a row is selected being selected, return false to cancel.
11843              * @param {SelectionModel} this
11844              * @param {Number} rowIndex The selected index
11845              * @param {Boolean} keepExisting False if other selections will be cleared
11846              */
11847             "beforerowselect" : true,
11848         /**
11849              * @event rowselect
11850              * Fires when a row is selected.
11851              * @param {SelectionModel} this
11852              * @param {Number} rowIndex The selected index
11853              * @param {Roo.data.Record} r The record
11854              */
11855             "rowselect" : true,
11856         /**
11857              * @event rowdeselect
11858              * Fires when a row is deselected.
11859              * @param {SelectionModel} this
11860              * @param {Number} rowIndex The selected index
11861              */
11862         "rowdeselect" : true
11863     });
11864     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
11865     this.locked = false;
11866 };
11867
11868 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
11869     /**
11870      * @cfg {Boolean} singleSelect
11871      * True to allow selection of only one row at a time (defaults to false)
11872      */
11873     singleSelect : false,
11874
11875     // private
11876     initEvents : function(){
11877
11878         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
11879             this.grid.on("mousedown", this.handleMouseDown, this);
11880         }else{ // allow click to work like normal
11881             this.grid.on("rowclick", this.handleDragableRowClick, this);
11882         }
11883
11884         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
11885             "up" : function(e){
11886                 if(!e.shiftKey){
11887                     this.selectPrevious(e.shiftKey);
11888                 }else if(this.last !== false && this.lastActive !== false){
11889                     var last = this.last;
11890                     this.selectRange(this.last,  this.lastActive-1);
11891                     this.grid.getView().focusRow(this.lastActive);
11892                     if(last !== false){
11893                         this.last = last;
11894                     }
11895                 }else{
11896                     this.selectFirstRow();
11897                 }
11898                 this.fireEvent("afterselectionchange", this);
11899             },
11900             "down" : function(e){
11901                 if(!e.shiftKey){
11902                     this.selectNext(e.shiftKey);
11903                 }else if(this.last !== false && this.lastActive !== false){
11904                     var last = this.last;
11905                     this.selectRange(this.last,  this.lastActive+1);
11906                     this.grid.getView().focusRow(this.lastActive);
11907                     if(last !== false){
11908                         this.last = last;
11909                     }
11910                 }else{
11911                     this.selectFirstRow();
11912                 }
11913                 this.fireEvent("afterselectionchange", this);
11914             },
11915             scope: this
11916         });
11917
11918         var view = this.grid.view;
11919         view.on("refresh", this.onRefresh, this);
11920         view.on("rowupdated", this.onRowUpdated, this);
11921         view.on("rowremoved", this.onRemove, this);
11922     },
11923
11924     // private
11925     onRefresh : function(){
11926         var ds = this.grid.dataSource, i, v = this.grid.view;
11927         var s = this.selections;
11928         s.each(function(r){
11929             if((i = ds.indexOfId(r.id)) != -1){
11930                 v.onRowSelect(i);
11931             }else{
11932                 s.remove(r);
11933             }
11934         });
11935     },
11936
11937     // private
11938     onRemove : function(v, index, r){
11939         this.selections.remove(r);
11940     },
11941
11942     // private
11943     onRowUpdated : function(v, index, r){
11944         if(this.isSelected(r)){
11945             v.onRowSelect(index);
11946         }
11947     },
11948
11949     /**
11950      * Select records.
11951      * @param {Array} records The records to select
11952      * @param {Boolean} keepExisting (optional) True to keep existing selections
11953      */
11954     selectRecords : function(records, keepExisting){
11955         if(!keepExisting){
11956             this.clearSelections();
11957         }
11958         var ds = this.grid.dataSource;
11959         for(var i = 0, len = records.length; i < len; i++){
11960             this.selectRow(ds.indexOf(records[i]), true);
11961         }
11962     },
11963
11964     /**
11965      * Gets the number of selected rows.
11966      * @return {Number}
11967      */
11968     getCount : function(){
11969         return this.selections.length;
11970     },
11971
11972     /**
11973      * Selects the first row in the grid.
11974      */
11975     selectFirstRow : function(){
11976         this.selectRow(0);
11977     },
11978
11979     /**
11980      * Select the last row.
11981      * @param {Boolean} keepExisting (optional) True to keep existing selections
11982      */
11983     selectLastRow : function(keepExisting){
11984         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
11985     },
11986
11987     /**
11988      * Selects the row immediately following the last selected row.
11989      * @param {Boolean} keepExisting (optional) True to keep existing selections
11990      */
11991     selectNext : function(keepExisting){
11992         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
11993             this.selectRow(this.last+1, keepExisting);
11994             this.grid.getView().focusRow(this.last);
11995         }
11996     },
11997
11998     /**
11999      * Selects the row that precedes the last selected row.
12000      * @param {Boolean} keepExisting (optional) True to keep existing selections
12001      */
12002     selectPrevious : function(keepExisting){
12003         if(this.last){
12004             this.selectRow(this.last-1, keepExisting);
12005             this.grid.getView().focusRow(this.last);
12006         }
12007     },
12008
12009     /**
12010      * Returns the selected records
12011      * @return {Array} Array of selected records
12012      */
12013     getSelections : function(){
12014         return [].concat(this.selections.items);
12015     },
12016
12017     /**
12018      * Returns the first selected record.
12019      * @return {Record}
12020      */
12021     getSelected : function(){
12022         return this.selections.itemAt(0);
12023     },
12024
12025
12026     /**
12027      * Clears all selections.
12028      */
12029     clearSelections : function(fast){
12030         if(this.locked) return;
12031         if(fast !== true){
12032             var ds = this.grid.dataSource;
12033             var s = this.selections;
12034             s.each(function(r){
12035                 this.deselectRow(ds.indexOfId(r.id));
12036             }, this);
12037             s.clear();
12038         }else{
12039             this.selections.clear();
12040         }
12041         this.last = false;
12042     },
12043
12044
12045     /**
12046      * Selects all rows.
12047      */
12048     selectAll : function(){
12049         if(this.locked) return;
12050         this.selections.clear();
12051         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
12052             this.selectRow(i, true);
12053         }
12054     },
12055
12056     /**
12057      * Returns True if there is a selection.
12058      * @return {Boolean}
12059      */
12060     hasSelection : function(){
12061         return this.selections.length > 0;
12062     },
12063
12064     /**
12065      * Returns True if the specified row is selected.
12066      * @param {Number/Record} record The record or index of the record to check
12067      * @return {Boolean}
12068      */
12069     isSelected : function(index){
12070         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
12071         return (r && this.selections.key(r.id) ? true : false);
12072     },
12073
12074     /**
12075      * Returns True if the specified record id is selected.
12076      * @param {String} id The id of record to check
12077      * @return {Boolean}
12078      */
12079     isIdSelected : function(id){
12080         return (this.selections.key(id) ? true : false);
12081     },
12082
12083     // private
12084     handleMouseDown : function(e, t){
12085         var view = this.grid.getView(), rowIndex;
12086         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
12087             return;
12088         };
12089         if(e.shiftKey && this.last !== false){
12090             var last = this.last;
12091             this.selectRange(last, rowIndex, e.ctrlKey);
12092             this.last = last; // reset the last
12093             view.focusRow(rowIndex);
12094         }else{
12095             var isSelected = this.isSelected(rowIndex);
12096             if(e.button !== 0 && isSelected){
12097                 view.focusRow(rowIndex);
12098             }else if(e.ctrlKey && isSelected){
12099                 this.deselectRow(rowIndex);
12100             }else if(!isSelected){
12101                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
12102                 view.focusRow(rowIndex);
12103             }
12104         }
12105         this.fireEvent("afterselectionchange", this);
12106     },
12107     // private
12108     handleDragableRowClick :  function(grid, rowIndex, e) 
12109     {
12110         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
12111             this.selectRow(rowIndex, false);
12112             grid.view.focusRow(rowIndex);
12113              this.fireEvent("afterselectionchange", this);
12114         }
12115     },
12116     
12117     /**
12118      * Selects multiple rows.
12119      * @param {Array} rows Array of the indexes of the row to select
12120      * @param {Boolean} keepExisting (optional) True to keep existing selections
12121      */
12122     selectRows : function(rows, keepExisting){
12123         if(!keepExisting){
12124             this.clearSelections();
12125         }
12126         for(var i = 0, len = rows.length; i < len; i++){
12127             this.selectRow(rows[i], true);
12128         }
12129     },
12130
12131     /**
12132      * Selects a range of rows. All rows in between startRow and endRow are also selected.
12133      * @param {Number} startRow The index of the first row in the range
12134      * @param {Number} endRow The index of the last row in the range
12135      * @param {Boolean} keepExisting (optional) True to retain existing selections
12136      */
12137     selectRange : function(startRow, endRow, keepExisting){
12138         if(this.locked) return;
12139         if(!keepExisting){
12140             this.clearSelections();
12141         }
12142         if(startRow <= endRow){
12143             for(var i = startRow; i <= endRow; i++){
12144                 this.selectRow(i, true);
12145             }
12146         }else{
12147             for(var i = startRow; i >= endRow; i--){
12148                 this.selectRow(i, true);
12149             }
12150         }
12151     },
12152
12153     /**
12154      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
12155      * @param {Number} startRow The index of the first row in the range
12156      * @param {Number} endRow The index of the last row in the range
12157      */
12158     deselectRange : function(startRow, endRow, preventViewNotify){
12159         if(this.locked) return;
12160         for(var i = startRow; i <= endRow; i++){
12161             this.deselectRow(i, preventViewNotify);
12162         }
12163     },
12164
12165     /**
12166      * Selects a row.
12167      * @param {Number} row The index of the row to select
12168      * @param {Boolean} keepExisting (optional) True to keep existing selections
12169      */
12170     selectRow : function(index, keepExisting, preventViewNotify){
12171         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
12172         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
12173             if(!keepExisting || this.singleSelect){
12174                 this.clearSelections();
12175             }
12176             var r = this.grid.dataSource.getAt(index);
12177             this.selections.add(r);
12178             this.last = this.lastActive = index;
12179             if(!preventViewNotify){
12180                 this.grid.getView().onRowSelect(index);
12181             }
12182             this.fireEvent("rowselect", this, index, r);
12183             this.fireEvent("selectionchange", this);
12184         }
12185     },
12186
12187     /**
12188      * Deselects a row.
12189      * @param {Number} row The index of the row to deselect
12190      */
12191     deselectRow : function(index, preventViewNotify){
12192         if(this.locked) return;
12193         if(this.last == index){
12194             this.last = false;
12195         }
12196         if(this.lastActive == index){
12197             this.lastActive = false;
12198         }
12199         var r = this.grid.dataSource.getAt(index);
12200         this.selections.remove(r);
12201         if(!preventViewNotify){
12202             this.grid.getView().onRowDeselect(index);
12203         }
12204         this.fireEvent("rowdeselect", this, index);
12205         this.fireEvent("selectionchange", this);
12206     },
12207
12208     // private
12209     restoreLast : function(){
12210         if(this._last){
12211             this.last = this._last;
12212         }
12213     },
12214
12215     // private
12216     acceptsNav : function(row, col, cm){
12217         return !cm.isHidden(col) && cm.isCellEditable(col, row);
12218     },
12219
12220     // private
12221     onEditorKey : function(field, e){
12222         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
12223         if(k == e.TAB){
12224             e.stopEvent();
12225             ed.completeEdit();
12226             if(e.shiftKey){
12227                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
12228             }else{
12229                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
12230             }
12231         }else if(k == e.ENTER && !e.ctrlKey){
12232             e.stopEvent();
12233             ed.completeEdit();
12234             if(e.shiftKey){
12235                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
12236             }else{
12237                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
12238             }
12239         }else if(k == e.ESC){
12240             ed.cancelEdit();
12241         }
12242         if(newCell){
12243             g.startEditing(newCell[0], newCell[1]);
12244         }
12245     }
12246 });