roojs-bootstrap.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         if(this.fireEvent('beforeaction', this, action) !== false){
3236             this.beforeAction(action);
3237             action.run.defer(100, action);
3238         }
3239         return this;
3240     },
3241     
3242     // private
3243     beforeAction : function(action){
3244         var o = action.options;
3245         
3246         // not really supported yet.. ??
3247         
3248         //if(this.waitMsgTarget === true){
3249             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3250         //}else if(this.waitMsgTarget){
3251         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3252         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3253         //}else {
3254         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3255        // }
3256          
3257     },
3258
3259     // private
3260     afterAction : function(action, success){
3261         this.activeAction = null;
3262         var o = action.options;
3263         
3264         //if(this.waitMsgTarget === true){
3265             this.el.unmask();
3266         //}else if(this.waitMsgTarget){
3267         //    this.waitMsgTarget.unmask();
3268         //}else{
3269         //    Roo.MessageBox.updateProgress(1);
3270         //    Roo.MessageBox.hide();
3271        // }
3272         // 
3273         if(success){
3274             if(o.reset){
3275                 this.reset();
3276             }
3277             Roo.callback(o.success, o.scope, [this, action]);
3278             this.fireEvent('actioncomplete', this, action);
3279             
3280         }else{
3281             
3282             // failure condition..
3283             // we have a scenario where updates need confirming.
3284             // eg. if a locking scenario exists..
3285             // we look for { errors : { needs_confirm : true }} in the response.
3286             if (
3287                 (typeof(action.result) != 'undefined')  &&
3288                 (typeof(action.result.errors) != 'undefined')  &&
3289                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3290            ){
3291                 var _t = this;
3292                 Roo.log("not supported yet");
3293                  /*
3294                 
3295                 Roo.MessageBox.confirm(
3296                     "Change requires confirmation",
3297                     action.result.errorMsg,
3298                     function(r) {
3299                         if (r != 'yes') {
3300                             return;
3301                         }
3302                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3303                     }
3304                     
3305                 );
3306                 */
3307                 
3308                 
3309                 return;
3310             }
3311             
3312             Roo.callback(o.failure, o.scope, [this, action]);
3313             // show an error message if no failed handler is set..
3314             if (!this.hasListener('actionfailed')) {
3315                 Roo.log("need to add dialog support");
3316                 /*
3317                 Roo.MessageBox.alert("Error",
3318                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
3319                         action.result.errorMsg :
3320                         "Saving Failed, please check your entries or try again"
3321                 );
3322                 */
3323             }
3324             
3325             this.fireEvent('actionfailed', this, action);
3326         }
3327         
3328     },
3329     /**
3330      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
3331      * @param {String} id The value to search for
3332      * @return Field
3333      */
3334     findField : function(id){
3335         var items = this.getItems();
3336         var field = items.get(id);
3337         if(!field){
3338              items.each(function(f){
3339                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
3340                     field = f;
3341                     return false;
3342                 }
3343                 return true;
3344             });
3345         }
3346         return field || null;
3347     },
3348      /**
3349      * Mark fields in this form invalid in bulk.
3350      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
3351      * @return {BasicForm} this
3352      */
3353     markInvalid : function(errors){
3354         if(errors instanceof Array){
3355             for(var i = 0, len = errors.length; i < len; i++){
3356                 var fieldError = errors[i];
3357                 var f = this.findField(fieldError.id);
3358                 if(f){
3359                     f.markInvalid(fieldError.msg);
3360                 }
3361             }
3362         }else{
3363             var field, id;
3364             for(id in errors){
3365                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
3366                     field.markInvalid(errors[id]);
3367                 }
3368             }
3369         }
3370         //Roo.each(this.childForms || [], function (f) {
3371         //    f.markInvalid(errors);
3372         //});
3373         
3374         return this;
3375     },
3376
3377     /**
3378      * Set values for fields in this form in bulk.
3379      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
3380      * @return {BasicForm} this
3381      */
3382     setValues : function(values){
3383         if(values instanceof Array){ // array of objects
3384             for(var i = 0, len = values.length; i < len; i++){
3385                 var v = values[i];
3386                 var f = this.findField(v.id);
3387                 if(f){
3388                     f.setValue(v.value);
3389                     if(this.trackResetOnLoad){
3390                         f.originalValue = f.getValue();
3391                     }
3392                 }
3393             }
3394         }else{ // object hash
3395             var field, id;
3396             for(id in values){
3397                 if(typeof values[id] != 'function' && (field = this.findField(id))){
3398                     
3399                     if (field.setFromData && 
3400                         field.valueField && 
3401                         field.displayField &&
3402                         // combos' with local stores can 
3403                         // be queried via setValue()
3404                         // to set their value..
3405                         (field.store && !field.store.isLocal)
3406                         ) {
3407                         // it's a combo
3408                         var sd = { };
3409                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
3410                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
3411                         field.setFromData(sd);
3412                         
3413                     } else {
3414                         field.setValue(values[id]);
3415                     }
3416                     
3417                     
3418                     if(this.trackResetOnLoad){
3419                         field.originalValue = field.getValue();
3420                     }
3421                 }
3422             }
3423         }
3424          
3425         //Roo.each(this.childForms || [], function (f) {
3426         //    f.setValues(values);
3427         //});
3428                 
3429         return this;
3430     },
3431
3432     /**
3433      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
3434      * they are returned as an array.
3435      * @param {Boolean} asString
3436      * @return {Object}
3437      */
3438     getValues : function(asString){
3439         //if (this.childForms) {
3440             // copy values from the child forms
3441         //    Roo.each(this.childForms, function (f) {
3442         //        this.setValues(f.getValues());
3443         //    }, this);
3444         //}
3445         
3446         
3447         
3448         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
3449         if(asString === true){
3450             return fs;
3451         }
3452         return Roo.urlDecode(fs);
3453     },
3454     
3455     /**
3456      * Returns the fields in this form as an object with key/value pairs. 
3457      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
3458      * @return {Object}
3459      */
3460     getFieldValues : function(with_hidden)
3461     {
3462         var items = this.getItems();
3463         var ret = {};
3464         items.each(function(f){
3465             if (!f.getName()) {
3466                 return;
3467             }
3468             var v = f.getValue();
3469             if (f.inputType =='radio') {
3470                 if (typeof(ret[f.getName()]) == 'undefined') {
3471                     ret[f.getName()] = ''; // empty..
3472                 }
3473                 
3474                 if (!f.el.dom.checked) {
3475                     return;
3476                     
3477                 }
3478                 v = f.el.dom.value;
3479                 
3480             }
3481             
3482             // not sure if this supported any more..
3483             if ((typeof(v) == 'object') && f.getRawValue) {
3484                 v = f.getRawValue() ; // dates..
3485             }
3486             // combo boxes where name != hiddenName...
3487             if (f.name != f.getName()) {
3488                 ret[f.name] = f.getRawValue();
3489             }
3490             ret[f.getName()] = v;
3491         });
3492         
3493         return ret;
3494     },
3495
3496     /**
3497      * Clears all invalid messages in this form.
3498      * @return {BasicForm} this
3499      */
3500     clearInvalid : function(){
3501         var items = this.getItems();
3502         
3503         items.each(function(f){
3504            f.clearInvalid();
3505         });
3506         
3507         
3508         
3509         return this;
3510     },
3511
3512     /**
3513      * Resets this form.
3514      * @return {BasicForm} this
3515      */
3516     reset : function(){
3517         var items = this.getItems();
3518         items.each(function(f){
3519             f.reset();
3520         });
3521         
3522         Roo.each(this.childForms || [], function (f) {
3523             f.reset();
3524         });
3525        
3526         
3527         return this;
3528     },
3529     getItems : function()
3530     {
3531         var r=new Roo.util.MixedCollection(false, function(o){
3532             return o.id || (o.id = Roo.id());
3533         });
3534         var iter = function(el) {
3535             if (el.inputEl) {
3536                 r.add(el);
3537             }
3538             if (!el.items) {
3539                 return;
3540             }
3541             Roo.each(el.items,function(e) {
3542                 iter(e);
3543             });
3544             
3545             
3546         };
3547         iter(this);
3548         return r;
3549         
3550         
3551         
3552         
3553     }
3554     
3555 });
3556
3557  
3558 /*
3559  * Based on:
3560  * Ext JS Library 1.1.1
3561  * Copyright(c) 2006-2007, Ext JS, LLC.
3562  *
3563  * Originally Released Under LGPL - original licence link has changed is not relivant.
3564  *
3565  * Fork - LGPL
3566  * <script type="text/javascript">
3567  */
3568 /**
3569  * @class Roo.form.VTypes
3570  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
3571  * @singleton
3572  */
3573 Roo.form.VTypes = function(){
3574     // closure these in so they are only created once.
3575     var alpha = /^[a-zA-Z_]+$/;
3576     var alphanum = /^[a-zA-Z0-9_]+$/;
3577     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
3578     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
3579
3580     // All these messages and functions are configurable
3581     return {
3582         /**
3583          * The function used to validate email addresses
3584          * @param {String} value The email address
3585          */
3586         'email' : function(v){
3587             return email.test(v);
3588         },
3589         /**
3590          * The error text to display when the email validation function returns false
3591          * @type String
3592          */
3593         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
3594         /**
3595          * The keystroke filter mask to be applied on email input
3596          * @type RegExp
3597          */
3598         'emailMask' : /[a-z0-9_\.\-@]/i,
3599
3600         /**
3601          * The function used to validate URLs
3602          * @param {String} value The URL
3603          */
3604         'url' : function(v){
3605             return url.test(v);
3606         },
3607         /**
3608          * The error text to display when the url validation function returns false
3609          * @type String
3610          */
3611         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
3612         
3613         /**
3614          * The function used to validate alpha values
3615          * @param {String} value The value
3616          */
3617         'alpha' : function(v){
3618             return alpha.test(v);
3619         },
3620         /**
3621          * The error text to display when the alpha validation function returns false
3622          * @type String
3623          */
3624         'alphaText' : 'This field should only contain letters and _',
3625         /**
3626          * The keystroke filter mask to be applied on alpha input
3627          * @type RegExp
3628          */
3629         'alphaMask' : /[a-z_]/i,
3630
3631         /**
3632          * The function used to validate alphanumeric values
3633          * @param {String} value The value
3634          */
3635         'alphanum' : function(v){
3636             return alphanum.test(v);
3637         },
3638         /**
3639          * The error text to display when the alphanumeric validation function returns false
3640          * @type String
3641          */
3642         'alphanumText' : 'This field should only contain letters, numbers and _',
3643         /**
3644          * The keystroke filter mask to be applied on alphanumeric input
3645          * @type RegExp
3646          */
3647         'alphanumMask' : /[a-z0-9_]/i
3648     };
3649 }();/*
3650  * - LGPL
3651  *
3652  * Input
3653  * 
3654  */
3655
3656 /**
3657  * @class Roo.bootstrap.Input
3658  * @extends Roo.bootstrap.Component
3659  * Bootstrap Input class
3660  * @cfg {Boolean} disabled is it disabled
3661  * @cfg {String} fieldLabel - the label associated
3662  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
3663  * @cfg {String} name name of the input
3664  * @cfg {string} fieldLabel - the label associated
3665  * @cfg {string}  inputType - input / file submit ...
3666  * @cfg {string} placeholder - placeholder to put in text.
3667  * @cfg {string}  before - input group add on before
3668  * @cfg {string} after - input group add on after
3669  * @cfg {string} size - (lg|sm) or leave empty..
3670  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
3671  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
3672  * @cfg {Number} md colspan out of 12 for computer-sized screens
3673  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
3674  * @cfg {string} value default value of the input
3675  * @cfg {Number} labelWidth set the width of label (0-12)
3676  * @cfg {String} labelAlign (top|left)
3677  * 
3678  * 
3679  * @constructor
3680  * Create a new Input
3681  * @param {Object} config The config object
3682  */
3683
3684 Roo.bootstrap.Input = function(config){
3685     Roo.bootstrap.Input.superclass.constructor.call(this, config);
3686    
3687         this.addEvents({
3688             /**
3689              * @event focus
3690              * Fires when this field receives input focus.
3691              * @param {Roo.form.Field} this
3692              */
3693             focus : true,
3694             /**
3695              * @event blur
3696              * Fires when this field loses input focus.
3697              * @param {Roo.form.Field} this
3698              */
3699             blur : true,
3700             /**
3701              * @event specialkey
3702              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
3703              * {@link Roo.EventObject#getKey} to determine which key was pressed.
3704              * @param {Roo.form.Field} this
3705              * @param {Roo.EventObject} e The event object
3706              */
3707             specialkey : true,
3708             /**
3709              * @event change
3710              * Fires just before the field blurs if the field value has changed.
3711              * @param {Roo.form.Field} this
3712              * @param {Mixed} newValue The new value
3713              * @param {Mixed} oldValue The original value
3714              */
3715             change : true,
3716             /**
3717              * @event invalid
3718              * Fires after the field has been marked as invalid.
3719              * @param {Roo.form.Field} this
3720              * @param {String} msg The validation message
3721              */
3722             invalid : true,
3723             /**
3724              * @event valid
3725              * Fires after the field has been validated with no errors.
3726              * @param {Roo.form.Field} this
3727              */
3728             valid : true,
3729              /**
3730              * @event keyup
3731              * Fires after the key up
3732              * @param {Roo.form.Field} this
3733              * @param {Roo.EventObject}  e The event Object
3734              */
3735             keyup : true
3736         });
3737 };
3738
3739 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
3740      /**
3741      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
3742       automatic validation (defaults to "keyup").
3743      */
3744     validationEvent : "keyup",
3745      /**
3746      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
3747      */
3748     validateOnBlur : true,
3749     /**
3750      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
3751      */
3752     validationDelay : 250,
3753      /**
3754      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
3755      */
3756     focusClass : "x-form-focus",  // not needed???
3757     
3758        
3759     /**
3760      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
3761      */
3762     invalidClass : "has-error",
3763     
3764     /**
3765      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
3766      */
3767     selectOnFocus : false,
3768     
3769      /**
3770      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
3771      */
3772     maskRe : null,
3773        /**
3774      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
3775      */
3776     vtype : null,
3777     
3778       /**
3779      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
3780      */
3781     disableKeyFilter : false,
3782     
3783        /**
3784      * @cfg {Boolean} disabled True to disable the field (defaults to false).
3785      */
3786     disabled : false,
3787      /**
3788      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
3789      */
3790     allowBlank : true,
3791     /**
3792      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
3793      */
3794     blankText : "This field is required",
3795     
3796      /**
3797      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
3798      */
3799     minLength : 0,
3800     /**
3801      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
3802      */
3803     maxLength : Number.MAX_VALUE,
3804     /**
3805      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
3806      */
3807     minLengthText : "The minimum length for this field is {0}",
3808     /**
3809      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
3810      */
3811     maxLengthText : "The maximum length for this field is {0}",
3812   
3813     
3814     /**
3815      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
3816      * If available, this function will be called only after the basic validators all return true, and will be passed the
3817      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
3818      */
3819     validator : null,
3820     /**
3821      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
3822      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
3823      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
3824      */
3825     regex : null,
3826     /**
3827      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
3828      */
3829     regexText : "",
3830     
3831     
3832     
3833     fieldLabel : '',
3834     inputType : 'text',
3835     
3836     name : false,
3837     placeholder: false,
3838     before : false,
3839     after : false,
3840     size : false,
3841     // private
3842     hasFocus : false,
3843     preventMark: false,
3844     isFormField : true,
3845     value : '',
3846     labelWidth : 2,
3847     labelAlign : false,
3848     
3849     parentLabelAlign : function()
3850     {
3851         var parent = this;
3852         while (parent.parent()) {
3853             parent = parent.parent();
3854             if (typeof(parent.labelAlign) !='undefined') {
3855                 return parent.labelAlign;
3856             }
3857         }
3858         return 'left';
3859         
3860     },
3861     
3862     getAutoCreate : function(){
3863         
3864         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
3865         
3866         var id = Roo.id();
3867         
3868         var cfg = {};
3869         
3870         if(this.inputType != 'hidden'){
3871             cfg.cls = 'form-group' //input-group
3872         }
3873         
3874         var input =  {
3875             tag: 'input',
3876             id : id,
3877             type : this.inputType,
3878             value : this.value,
3879             cls : 'form-control',
3880             placeholder : this.placeholder || ''
3881             
3882         };
3883         
3884         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
3885             input.maxLength = this.maxLength;
3886         }
3887         
3888         if (this.disabled) {
3889             input.disabled=true;
3890         }
3891         
3892         if (this.name) {
3893             input.name = this.name;
3894         }
3895         if (this.size) {
3896             input.cls += ' input-' + this.size;
3897         }
3898         var settings=this;
3899         ['xs','sm','md','lg'].map(function(size){
3900             if (settings[size]) {
3901                 cfg.cls += ' col-' + size + '-' + settings[size];
3902             }
3903         });
3904         
3905         var inputblock = input;
3906         
3907         if (this.before || this.after) {
3908             
3909             inputblock = {
3910                 cls : 'input-group',
3911                 cn :  [] 
3912             };
3913             if (this.before) {
3914                 inputblock.cn.push({
3915                     tag :'span',
3916                     cls : 'input-group-addon',
3917                     html : this.before
3918                 });
3919             }
3920             inputblock.cn.push(input);
3921             if (this.after) {
3922                 inputblock.cn.push({
3923                     tag :'span',
3924                     cls : 'input-group-addon',
3925                     html : this.after
3926                 });
3927             }
3928             
3929         };
3930         
3931         if (align ==='left' && this.fieldLabel.length) {
3932                 Roo.log("left and has label");
3933                 cfg.cn = [
3934                     
3935                     {
3936                         tag: 'label',
3937                         'for' :  id,
3938                         cls : 'control-label col-sm-' + this.labelWidth,
3939                         html : this.fieldLabel
3940                         
3941                     },
3942                     {
3943                         cls : "col-sm-" + (12 - this.labelWidth), 
3944                         cn: [
3945                             inputblock
3946                         ]
3947                     }
3948                     
3949                 ];
3950         } else if ( this.fieldLabel.length) {
3951                 Roo.log(" label");
3952                  cfg.cn = [
3953                    
3954                     {
3955                         tag: 'label',
3956                         //cls : 'input-group-addon',
3957                         html : this.fieldLabel
3958                         
3959                     },
3960                     
3961                     inputblock
3962                     
3963                 ];
3964
3965         } else {
3966             
3967                    Roo.log(" no label && no align");
3968                 cfg.cn = [
3969                     
3970                         inputblock
3971                     
3972                 ];
3973                 
3974                 
3975         };
3976         
3977         return cfg;
3978         
3979     },
3980     /**
3981      * return the real input element.
3982      */
3983     inputEl: function ()
3984     {
3985         return this.el.select('input.form-control',true).first();
3986     },
3987     setDisabled : function(v)
3988     {
3989         var i  = this.inputEl().dom;
3990         if (!v) {
3991             i.removeAttribute('disabled');
3992             return;
3993             
3994         }
3995         i.setAttribute('disabled','true');
3996     },
3997     initEvents : function()
3998     {
3999         
4000         this.inputEl().on("keydown" , this.fireKey,  this);
4001         this.inputEl().on("focus", this.onFocus,  this);
4002         this.inputEl().on("blur", this.onBlur,  this);
4003         
4004         this.inputEl().relayEvent('keyup', this);
4005
4006         // reference to original value for reset
4007         this.originalValue = this.getValue();
4008         //Roo.form.TextField.superclass.initEvents.call(this);
4009         if(this.validationEvent == 'keyup'){
4010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4011             this.inputEl().on('keyup', this.filterValidation, this);
4012         }
4013         else if(this.validationEvent !== false){
4014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4015         }
4016         
4017         if(this.selectOnFocus){
4018             this.on("focus", this.preFocus, this);
4019             
4020         }
4021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4022             this.inputEl().on("keypress", this.filterKeys, this);
4023         }
4024        /* if(this.grow){
4025             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4026             this.el.on("click", this.autoSize,  this);
4027         }
4028         */
4029         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4030             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4031         }
4032         
4033     },
4034     filterValidation : function(e){
4035         if(!e.isNavKeyPress()){
4036             this.validationTask.delay(this.validationDelay);
4037         }
4038     },
4039      /**
4040      * Validates the field value
4041      * @return {Boolean} True if the value is valid, else false
4042      */
4043     validate : function(){
4044         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4045         if(this.disabled || this.validateValue(this.getRawValue())){
4046             this.clearInvalid();
4047             return true;
4048         }
4049         return false;
4050     },
4051     
4052     
4053     /**
4054      * Validates a value according to the field's validation rules and marks the field as invalid
4055      * if the validation fails
4056      * @param {Mixed} value The value to validate
4057      * @return {Boolean} True if the value is valid, else false
4058      */
4059     validateValue : function(value){
4060         if(value.length < 1)  { // if it's blank
4061              if(this.allowBlank){
4062                 this.clearInvalid();
4063                 return true;
4064              }else{
4065                 this.markInvalid(this.blankText);
4066                 return false;
4067              }
4068         }
4069         if(value.length < this.minLength){
4070             this.markInvalid(String.format(this.minLengthText, this.minLength));
4071             return false;
4072         }
4073         if(value.length > this.maxLength){
4074             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4075             return false;
4076         }
4077         if(this.vtype){
4078             var vt = Roo.form.VTypes;
4079             if(!vt[this.vtype](value, this)){
4080                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4081                 return false;
4082             }
4083         }
4084         if(typeof this.validator == "function"){
4085             var msg = this.validator(value);
4086             if(msg !== true){
4087                 this.markInvalid(msg);
4088                 return false;
4089             }
4090         }
4091         if(this.regex && !this.regex.test(value)){
4092             this.markInvalid(this.regexText);
4093             return false;
4094         }
4095         return true;
4096     },
4097
4098     
4099     
4100      // private
4101     fireKey : function(e){
4102         //Roo.log('field ' + e.getKey());
4103         if(e.isNavKeyPress()){
4104             this.fireEvent("specialkey", this, e);
4105         }
4106     },
4107     focus : function (selectText){
4108         if(this.rendered){
4109             this.inputEl().focus();
4110             if(selectText === true){
4111                 this.inputEl().dom.select();
4112             }
4113         }
4114         return this;
4115     } ,
4116     
4117     onFocus : function(){
4118         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4119            // this.el.addClass(this.focusClass);
4120         }
4121         if(!this.hasFocus){
4122             this.hasFocus = true;
4123             this.startValue = this.getValue();
4124             this.fireEvent("focus", this);
4125         }
4126     },
4127     
4128     beforeBlur : Roo.emptyFn,
4129
4130     
4131     // private
4132     onBlur : function(){
4133         this.beforeBlur();
4134         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4135             //this.el.removeClass(this.focusClass);
4136         }
4137         this.hasFocus = false;
4138         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4139             this.validate();
4140         }
4141         var v = this.getValue();
4142         if(String(v) !== String(this.startValue)){
4143             this.fireEvent('change', this, v, this.startValue);
4144         }
4145         this.fireEvent("blur", this);
4146     },
4147     
4148     /**
4149      * Resets the current field value to the originally loaded value and clears any validation messages
4150      */
4151     reset : function(){
4152         this.setValue(this.originalValue);
4153         this.clearInvalid();
4154     },
4155      /**
4156      * Returns the name of the field
4157      * @return {Mixed} name The name field
4158      */
4159     getName: function(){
4160         return this.name;
4161     },
4162      /**
4163      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4164      * @return {Mixed} value The field value
4165      */
4166     getValue : function(){
4167         return this.inputEl().getValue();
4168     },
4169     /**
4170      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4171      * @return {Mixed} value The field value
4172      */
4173     getRawValue : function(){
4174         var v = this.inputEl().getValue();
4175         
4176         return v;
4177     },
4178     
4179     /**
4180      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4181      * @param {Mixed} value The value to set
4182      */
4183     setRawValue : function(v){
4184         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4185     },
4186     
4187     selectText : function(start, end){
4188         var v = this.getRawValue();
4189         if(v.length > 0){
4190             start = start === undefined ? 0 : start;
4191             end = end === undefined ? v.length : end;
4192             var d = this.inputEl().dom;
4193             if(d.setSelectionRange){
4194                 d.setSelectionRange(start, end);
4195             }else if(d.createTextRange){
4196                 var range = d.createTextRange();
4197                 range.moveStart("character", start);
4198                 range.moveEnd("character", v.length-end);
4199                 range.select();
4200             }
4201         }
4202     },
4203     
4204     /**
4205      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4206      * @param {Mixed} value The value to set
4207      */
4208     setValue : function(v){
4209         this.value = v;
4210         if(this.rendered){
4211             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4212             this.validate();
4213         }
4214     },
4215     
4216     /*
4217     processValue : function(value){
4218         if(this.stripCharsRe){
4219             var newValue = value.replace(this.stripCharsRe, '');
4220             if(newValue !== value){
4221                 this.setRawValue(newValue);
4222                 return newValue;
4223             }
4224         }
4225         return value;
4226     },
4227   */
4228     preFocus : function(){
4229         
4230         if(this.selectOnFocus){
4231             this.inputEl().dom.select();
4232         }
4233     },
4234     filterKeys : function(e){
4235         var k = e.getKey();
4236         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4237             return;
4238         }
4239         var c = e.getCharCode(), cc = String.fromCharCode(c);
4240         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4241             return;
4242         }
4243         if(!this.maskRe.test(cc)){
4244             e.stopEvent();
4245         }
4246     },
4247      /**
4248      * Clear any invalid styles/messages for this field
4249      */
4250     clearInvalid : function(){
4251         
4252         if(!this.el || this.preventMark){ // not rendered
4253             return;
4254         }
4255         this.el.removeClass(this.invalidClass);
4256         /*
4257         switch(this.msgTarget){
4258             case 'qtip':
4259                 this.el.dom.qtip = '';
4260                 break;
4261             case 'title':
4262                 this.el.dom.title = '';
4263                 break;
4264             case 'under':
4265                 if(this.errorEl){
4266                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4267                 }
4268                 break;
4269             case 'side':
4270                 if(this.errorIcon){
4271                     this.errorIcon.dom.qtip = '';
4272                     this.errorIcon.hide();
4273                     this.un('resize', this.alignErrorIcon, this);
4274                 }
4275                 break;
4276             default:
4277                 var t = Roo.getDom(this.msgTarget);
4278                 t.innerHTML = '';
4279                 t.style.display = 'none';
4280                 break;
4281         }
4282         */
4283         this.fireEvent('valid', this);
4284     },
4285      /**
4286      * Mark this field as invalid
4287      * @param {String} msg The validation message
4288      */
4289     markInvalid : function(msg){
4290         if(!this.el  || this.preventMark){ // not rendered
4291             return;
4292         }
4293         this.el.addClass(this.invalidClass);
4294         /*
4295         msg = msg || this.invalidText;
4296         switch(this.msgTarget){
4297             case 'qtip':
4298                 this.el.dom.qtip = msg;
4299                 this.el.dom.qclass = 'x-form-invalid-tip';
4300                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
4301                     Roo.QuickTips.enable();
4302                 }
4303                 break;
4304             case 'title':
4305                 this.el.dom.title = msg;
4306                 break;
4307             case 'under':
4308                 if(!this.errorEl){
4309                     var elp = this.el.findParent('.x-form-element', 5, true);
4310                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
4311                     this.errorEl.setWidth(elp.getWidth(true)-20);
4312                 }
4313                 this.errorEl.update(msg);
4314                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
4315                 break;
4316             case 'side':
4317                 if(!this.errorIcon){
4318                     var elp = this.el.findParent('.x-form-element', 5, true);
4319                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
4320                 }
4321                 this.alignErrorIcon();
4322                 this.errorIcon.dom.qtip = msg;
4323                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
4324                 this.errorIcon.show();
4325                 this.on('resize', this.alignErrorIcon, this);
4326                 break;
4327             default:
4328                 var t = Roo.getDom(this.msgTarget);
4329                 t.innerHTML = msg;
4330                 t.style.display = this.msgDisplay;
4331                 break;
4332         }
4333         */
4334         this.fireEvent('invalid', this, msg);
4335     },
4336     // private
4337     SafariOnKeyDown : function(event)
4338     {
4339         // this is a workaround for a password hang bug on chrome/ webkit.
4340         
4341         var isSelectAll = false;
4342         
4343         if(this.inputEl().dom.selectionEnd > 0){
4344             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
4345         }
4346         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
4347             event.preventDefault();
4348             this.setValue('');
4349             return;
4350         }
4351         
4352         if(isSelectAll){ // backspace and delete key
4353             
4354             event.preventDefault();
4355             // this is very hacky as keydown always get's upper case.
4356             //
4357             var cc = String.fromCharCode(event.getCharCode());
4358             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
4359             
4360         }
4361     }
4362     
4363 });
4364
4365  
4366 /*
4367  * - LGPL
4368  *
4369  * Input
4370  * 
4371  */
4372
4373 /**
4374  * @class Roo.bootstrap.TextArea
4375  * @extends Roo.bootstrap.Input
4376  * Bootstrap TextArea class
4377  * @cfg {Number} cols Specifies the visible width of a text area
4378  * @cfg {Number} rows Specifies the visible number of lines in a text area
4379  * @cfg {Number} readOnly Specifies that a text area should be read-only
4380  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
4381  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
4382  * @cfg {string} html text
4383  * 
4384  * @constructor
4385  * Create a new TextArea
4386  * @param {Object} config The config object
4387  */
4388
4389 Roo.bootstrap.TextArea = function(config){
4390     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
4391    
4392 };
4393
4394 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
4395      
4396     cols : false,
4397     rows : 5,
4398     readOnly : false,
4399     warp : 'soft',
4400     resize : false,
4401     value: false,
4402     html: false,
4403     
4404     getAutoCreate : function(){
4405         
4406         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4407         
4408         var id = Roo.id();
4409         
4410         var cfg = {};
4411         
4412         var input =  {
4413             tag: 'textarea',
4414             id : id,
4415             warp : this.warp,
4416             rows : this.rows,
4417             value : this.value || '',
4418             html: this.html || '',
4419             cls : 'form-control',
4420             placeholder : this.placeholder || '' 
4421             
4422         };
4423         
4424         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4425             input.maxLength = this.maxLength;
4426         }
4427         
4428         if(this.resize){
4429             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
4430         }
4431         
4432         if(this.cols){
4433             input.cols = this.cols;
4434         }
4435         
4436         if (this.readOnly) {
4437             input.readonly = true;
4438         }
4439         
4440         if (this.name) {
4441             input.name = this.name;
4442         }
4443         
4444         if (this.size) {
4445             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
4446         }
4447         
4448         var settings=this;
4449         ['xs','sm','md','lg'].map(function(size){
4450             if (settings[size]) {
4451                 cfg.cls += ' col-' + size + '-' + settings[size];
4452             }
4453         });
4454         
4455         var inputblock = input;
4456         
4457         if (this.before || this.after) {
4458             
4459             inputblock = {
4460                 cls : 'input-group',
4461                 cn :  [] 
4462             };
4463             if (this.before) {
4464                 inputblock.cn.push({
4465                     tag :'span',
4466                     cls : 'input-group-addon',
4467                     html : this.before
4468                 });
4469             }
4470             inputblock.cn.push(input);
4471             if (this.after) {
4472                 inputblock.cn.push({
4473                     tag :'span',
4474                     cls : 'input-group-addon',
4475                     html : this.after
4476                 });
4477             }
4478             
4479         }
4480         
4481         if (align ==='left' && this.fieldLabel.length) {
4482                 Roo.log("left and has label");
4483                 cfg.cn = [
4484                     
4485                     {
4486                         tag: 'label',
4487                         'for' :  id,
4488                         cls : 'control-label col-sm-' + this.labelWidth,
4489                         html : this.fieldLabel
4490                         
4491                     },
4492                     {
4493                         cls : "col-sm-" + (12 - this.labelWidth), 
4494                         cn: [
4495                             inputblock
4496                         ]
4497                     }
4498                     
4499                 ];
4500         } else if ( this.fieldLabel.length) {
4501                 Roo.log(" label");
4502                  cfg.cn = [
4503                    
4504                     {
4505                         tag: 'label',
4506                         //cls : 'input-group-addon',
4507                         html : this.fieldLabel
4508                         
4509                     },
4510                     
4511                     inputblock
4512                     
4513                 ];
4514
4515         } else {
4516             
4517                    Roo.log(" no label && no align");
4518                 cfg.cn = [
4519                     
4520                         inputblock
4521                     
4522                 ];
4523                 
4524                 
4525         }
4526         
4527         if (this.disabled) {
4528             input.disabled=true;
4529         }
4530         
4531         return cfg;
4532         
4533     },
4534     /**
4535      * return the real textarea element.
4536      */
4537     inputEl: function ()
4538     {
4539         return this.el.select('textarea.form-control',true).first();
4540     }
4541 });
4542
4543  
4544 /*
4545  * - LGPL
4546  *
4547  * trigger field - base class for combo..
4548  * 
4549  */
4550  
4551 /**
4552  * @class Roo.bootstrap.TriggerField
4553  * @extends Roo.bootstrap.Input
4554  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
4555  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
4556  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
4557  * for which you can provide a custom implementation.  For example:
4558  * <pre><code>
4559 var trigger = new Roo.bootstrap.TriggerField();
4560 trigger.onTriggerClick = myTriggerFn;
4561 trigger.applyTo('my-field');
4562 </code></pre>
4563  *
4564  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
4565  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
4566  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
4567  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
4568  * @constructor
4569  * Create a new TriggerField.
4570  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
4571  * to the base TextField)
4572  */
4573 Roo.bootstrap.TriggerField = function(config){
4574     this.mimicing = false;
4575     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
4576 };
4577
4578 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
4579     /**
4580      * @cfg {String} triggerClass A CSS class to apply to the trigger
4581      */
4582      /**
4583      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
4584      */
4585     hideTrigger:false,
4586
4587     /** @cfg {Boolean} grow @hide */
4588     /** @cfg {Number} growMin @hide */
4589     /** @cfg {Number} growMax @hide */
4590
4591     /**
4592      * @hide 
4593      * @method
4594      */
4595     autoSize: Roo.emptyFn,
4596     // private
4597     monitorTab : true,
4598     // private
4599     deferHeight : true,
4600
4601     
4602     actionMode : 'wrap',
4603     
4604     
4605     
4606     getAutoCreate : function(){
4607        
4608         var parent = this.parent();
4609         
4610         var align = this.parentLabelAlign();
4611         
4612         var id = Roo.id();
4613         
4614         var cfg = {
4615             cls: 'form-group' //input-group
4616         };
4617         
4618         
4619         var input =  {
4620             tag: 'input',
4621             id : id,
4622             type : this.inputType,
4623             cls : 'form-control',
4624             autocomplete: 'off',
4625             placeholder : this.placeholder || '' 
4626             
4627         };
4628         if (this.name) {
4629             input.name = this.name;
4630         }
4631         if (this.size) {
4632             input.cls += ' input-' + this.size;
4633         }
4634         var inputblock = {
4635             cls: 'combobox-container input-group',
4636             cn: [
4637                 {
4638                     tag: 'input',
4639                     type : 'hidden',
4640                     cls: 'form-hidden-field'
4641                 },
4642                 input,
4643                 {
4644                     tag: 'ul',
4645                     cls : 'typeahead typeahead-long dropdown-menu',
4646                     style : 'display:none'
4647                 },
4648                 {
4649                     tag :'span',
4650                     cls : 'input-group-addon btn dropdown-toggle',
4651                     cn : [
4652                         {
4653                             tag: 'span',
4654                             cls: 'caret'
4655                         },
4656                         {
4657                             tag: 'span',
4658                             cls: 'combobox-clear',
4659                             cn  : [
4660                                 {
4661                                     tag : 'i',
4662                                     cls: 'icon-remove'
4663                                 }
4664                             ]
4665                         }
4666                     ]
4667                         
4668                 }
4669             ]
4670         };
4671         
4672         
4673         
4674         
4675         if (align ==='left' && this.fieldLabel.length) {
4676                 
4677             
4678             
4679                 Roo.log("left and has label");
4680                 cfg.cn = [
4681                     
4682                     {
4683                         tag: 'label',
4684                         'for' :  id,
4685                         cls : 'col-sm-2 control-label',
4686                         html : this.fieldLabel
4687                         
4688                     },
4689                     {
4690                         cls : "col-sm-10", 
4691                         cn: [
4692                             inputblock
4693                         ]
4694                     }
4695                     
4696                 ];
4697         } else if ( this.fieldLabel.length) {
4698                 Roo.log(" label");
4699                  cfg.cn = [
4700                    
4701                     {
4702                         tag: 'label',
4703                         //cls : 'input-group-addon',
4704                         html : this.fieldLabel
4705                         
4706                     },
4707                     
4708                     inputblock
4709                     
4710                 ];
4711
4712         } else {
4713             
4714                 Roo.log(" no label && no align");
4715                 cfg = inputblock
4716                      
4717                 
4718         }
4719          
4720         var settings=this;
4721         ['xs','sm','md','lg'].map(function(size){
4722             if (settings[size]) {
4723                 cfg.cls += ' col-' + size + '-' + settings[size];
4724             }
4725         });
4726         
4727         
4728         
4729         if (this.disabled) {
4730             input.disabled=true;
4731         }
4732         return cfg;
4733         
4734     },
4735     
4736     
4737     
4738     // private
4739     onResize : function(w, h){
4740 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
4741 //        if(typeof w == 'number'){
4742 //            var x = w - this.trigger.getWidth();
4743 //            this.inputEl().setWidth(this.adjustWidth('input', x));
4744 //            this.trigger.setStyle('left', x+'px');
4745 //        }
4746     },
4747
4748     // private
4749     adjustSize : Roo.BoxComponent.prototype.adjustSize,
4750
4751     // private
4752     getResizeEl : function(){
4753         return this.inputEl();
4754     },
4755
4756     // private
4757     getPositionEl : function(){
4758         return this.inputEl();
4759     },
4760
4761     // private
4762     alignErrorIcon : function(){
4763         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
4764     },
4765
4766     // private
4767     initEvents : function(){
4768         
4769         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
4770         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
4771         
4772         this.trigger = this.el.select('span.dropdown-toggle',true).first();
4773         if(this.hideTrigger){
4774             this.trigger.setDisplayed(false);
4775         }
4776         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
4777         //this.trigger.addClassOnOver('x-form-trigger-over');
4778         //this.trigger.addClassOnClick('x-form-trigger-click');
4779         
4780         //if(!this.width){
4781         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
4782         //}
4783     },
4784
4785     // private
4786     initTrigger : function(){
4787        
4788     },
4789
4790     // private
4791     onDestroy : function(){
4792         if(this.trigger){
4793             this.trigger.removeAllListeners();
4794           //  this.trigger.remove();
4795         }
4796         //if(this.wrap){
4797         //    this.wrap.remove();
4798         //}
4799         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
4800     },
4801
4802     // private
4803     onFocus : function(){
4804         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
4805         /*
4806         if(!this.mimicing){
4807             this.wrap.addClass('x-trigger-wrap-focus');
4808             this.mimicing = true;
4809             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
4810             if(this.monitorTab){
4811                 this.el.on("keydown", this.checkTab, this);
4812             }
4813         }
4814         */
4815     },
4816
4817     // private
4818     checkTab : function(e){
4819         if(e.getKey() == e.TAB){
4820             this.triggerBlur();
4821         }
4822     },
4823
4824     // private
4825     onBlur : function(){
4826         // do nothing
4827     },
4828
4829     // private
4830     mimicBlur : function(e, t){
4831         /*
4832         if(!this.wrap.contains(t) && this.validateBlur()){
4833             this.triggerBlur();
4834         }
4835         */
4836     },
4837
4838     // private
4839     triggerBlur : function(){
4840         this.mimicing = false;
4841         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
4842         if(this.monitorTab){
4843             this.el.un("keydown", this.checkTab, this);
4844         }
4845         //this.wrap.removeClass('x-trigger-wrap-focus');
4846         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
4847     },
4848
4849     // private
4850     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
4851     validateBlur : function(e, t){
4852         return true;
4853     },
4854
4855     // private
4856     onDisable : function(){
4857         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
4858         //if(this.wrap){
4859         //    this.wrap.addClass('x-item-disabled');
4860         //}
4861     },
4862
4863     // private
4864     onEnable : function(){
4865         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
4866         //if(this.wrap){
4867         //    this.el.removeClass('x-item-disabled');
4868         //}
4869     },
4870
4871     // private
4872     onShow : function(){
4873         var ae = this.getActionEl();
4874         
4875         if(ae){
4876             ae.dom.style.display = '';
4877             ae.dom.style.visibility = 'visible';
4878         }
4879     },
4880
4881     // private
4882     
4883     onHide : function(){
4884         var ae = this.getActionEl();
4885         ae.dom.style.display = 'none';
4886     },
4887
4888     /**
4889      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
4890      * by an implementing function.
4891      * @method
4892      * @param {EventObject} e
4893      */
4894     onTriggerClick : Roo.emptyFn
4895 });
4896  /*
4897  * Based on:
4898  * Ext JS Library 1.1.1
4899  * Copyright(c) 2006-2007, Ext JS, LLC.
4900  *
4901  * Originally Released Under LGPL - original licence link has changed is not relivant.
4902  *
4903  * Fork - LGPL
4904  * <script type="text/javascript">
4905  */
4906
4907
4908 /**
4909  * @class Roo.data.SortTypes
4910  * @singleton
4911  * Defines the default sorting (casting?) comparison functions used when sorting data.
4912  */
4913 Roo.data.SortTypes = {
4914     /**
4915      * Default sort that does nothing
4916      * @param {Mixed} s The value being converted
4917      * @return {Mixed} The comparison value
4918      */
4919     none : function(s){
4920         return s;
4921     },
4922     
4923     /**
4924      * The regular expression used to strip tags
4925      * @type {RegExp}
4926      * @property
4927      */
4928     stripTagsRE : /<\/?[^>]+>/gi,
4929     
4930     /**
4931      * Strips all HTML tags to sort on text only
4932      * @param {Mixed} s The value being converted
4933      * @return {String} The comparison value
4934      */
4935     asText : function(s){
4936         return String(s).replace(this.stripTagsRE, "");
4937     },
4938     
4939     /**
4940      * Strips all HTML tags to sort on text only - Case insensitive
4941      * @param {Mixed} s The value being converted
4942      * @return {String} The comparison value
4943      */
4944     asUCText : function(s){
4945         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4946     },
4947     
4948     /**
4949      * Case insensitive string
4950      * @param {Mixed} s The value being converted
4951      * @return {String} The comparison value
4952      */
4953     asUCString : function(s) {
4954         return String(s).toUpperCase();
4955     },
4956     
4957     /**
4958      * Date sorting
4959      * @param {Mixed} s The value being converted
4960      * @return {Number} The comparison value
4961      */
4962     asDate : function(s) {
4963         if(!s){
4964             return 0;
4965         }
4966         if(s instanceof Date){
4967             return s.getTime();
4968         }
4969         return Date.parse(String(s));
4970     },
4971     
4972     /**
4973      * Float sorting
4974      * @param {Mixed} s The value being converted
4975      * @return {Float} The comparison value
4976      */
4977     asFloat : function(s) {
4978         var val = parseFloat(String(s).replace(/,/g, ""));
4979         if(isNaN(val)) val = 0;
4980         return val;
4981     },
4982     
4983     /**
4984      * Integer sorting
4985      * @param {Mixed} s The value being converted
4986      * @return {Number} The comparison value
4987      */
4988     asInt : function(s) {
4989         var val = parseInt(String(s).replace(/,/g, ""));
4990         if(isNaN(val)) val = 0;
4991         return val;
4992     }
4993 };/*
4994  * Based on:
4995  * Ext JS Library 1.1.1
4996  * Copyright(c) 2006-2007, Ext JS, LLC.
4997  *
4998  * Originally Released Under LGPL - original licence link has changed is not relivant.
4999  *
5000  * Fork - LGPL
5001  * <script type="text/javascript">
5002  */
5003
5004 /**
5005 * @class Roo.data.Record
5006  * Instances of this class encapsulate both record <em>definition</em> information, and record
5007  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5008  * to access Records cached in an {@link Roo.data.Store} object.<br>
5009  * <p>
5010  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5011  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5012  * objects.<br>
5013  * <p>
5014  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5015  * @constructor
5016  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5017  * {@link #create}. The parameters are the same.
5018  * @param {Array} data An associative Array of data values keyed by the field name.
5019  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5020  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5021  * not specified an integer id is generated.
5022  */
5023 Roo.data.Record = function(data, id){
5024     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5025     this.data = data;
5026 };
5027
5028 /**
5029  * Generate a constructor for a specific record layout.
5030  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5031  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5032  * Each field definition object may contain the following properties: <ul>
5033  * <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,
5034  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5035  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5036  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5037  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5038  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5039  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5040  * this may be omitted.</p></li>
5041  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5042  * <ul><li>auto (Default, implies no conversion)</li>
5043  * <li>string</li>
5044  * <li>int</li>
5045  * <li>float</li>
5046  * <li>boolean</li>
5047  * <li>date</li></ul></p></li>
5048  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5049  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5050  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5051  * by the Reader into an object that will be stored in the Record. It is passed the
5052  * following parameters:<ul>
5053  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5054  * </ul></p></li>
5055  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5056  * </ul>
5057  * <br>usage:<br><pre><code>
5058 var TopicRecord = Roo.data.Record.create(
5059     {name: 'title', mapping: 'topic_title'},
5060     {name: 'author', mapping: 'username'},
5061     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5062     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5063     {name: 'lastPoster', mapping: 'user2'},
5064     {name: 'excerpt', mapping: 'post_text'}
5065 );
5066
5067 var myNewRecord = new TopicRecord({
5068     title: 'Do my job please',
5069     author: 'noobie',
5070     totalPosts: 1,
5071     lastPost: new Date(),
5072     lastPoster: 'Animal',
5073     excerpt: 'No way dude!'
5074 });
5075 myStore.add(myNewRecord);
5076 </code></pre>
5077  * @method create
5078  * @static
5079  */
5080 Roo.data.Record.create = function(o){
5081     var f = function(){
5082         f.superclass.constructor.apply(this, arguments);
5083     };
5084     Roo.extend(f, Roo.data.Record);
5085     var p = f.prototype;
5086     p.fields = new Roo.util.MixedCollection(false, function(field){
5087         return field.name;
5088     });
5089     for(var i = 0, len = o.length; i < len; i++){
5090         p.fields.add(new Roo.data.Field(o[i]));
5091     }
5092     f.getField = function(name){
5093         return p.fields.get(name);  
5094     };
5095     return f;
5096 };
5097
5098 Roo.data.Record.AUTO_ID = 1000;
5099 Roo.data.Record.EDIT = 'edit';
5100 Roo.data.Record.REJECT = 'reject';
5101 Roo.data.Record.COMMIT = 'commit';
5102
5103 Roo.data.Record.prototype = {
5104     /**
5105      * Readonly flag - true if this record has been modified.
5106      * @type Boolean
5107      */
5108     dirty : false,
5109     editing : false,
5110     error: null,
5111     modified: null,
5112
5113     // private
5114     join : function(store){
5115         this.store = store;
5116     },
5117
5118     /**
5119      * Set the named field to the specified value.
5120      * @param {String} name The name of the field to set.
5121      * @param {Object} value The value to set the field to.
5122      */
5123     set : function(name, value){
5124         if(this.data[name] == value){
5125             return;
5126         }
5127         this.dirty = true;
5128         if(!this.modified){
5129             this.modified = {};
5130         }
5131         if(typeof this.modified[name] == 'undefined'){
5132             this.modified[name] = this.data[name];
5133         }
5134         this.data[name] = value;
5135         if(!this.editing && this.store){
5136             this.store.afterEdit(this);
5137         }       
5138     },
5139
5140     /**
5141      * Get the value of the named field.
5142      * @param {String} name The name of the field to get the value of.
5143      * @return {Object} The value of the field.
5144      */
5145     get : function(name){
5146         return this.data[name]; 
5147     },
5148
5149     // private
5150     beginEdit : function(){
5151         this.editing = true;
5152         this.modified = {}; 
5153     },
5154
5155     // private
5156     cancelEdit : function(){
5157         this.editing = false;
5158         delete this.modified;
5159     },
5160
5161     // private
5162     endEdit : function(){
5163         this.editing = false;
5164         if(this.dirty && this.store){
5165             this.store.afterEdit(this);
5166         }
5167     },
5168
5169     /**
5170      * Usually called by the {@link Roo.data.Store} which owns the Record.
5171      * Rejects all changes made to the Record since either creation, or the last commit operation.
5172      * Modified fields are reverted to their original values.
5173      * <p>
5174      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5175      * of reject operations.
5176      */
5177     reject : function(){
5178         var m = this.modified;
5179         for(var n in m){
5180             if(typeof m[n] != "function"){
5181                 this.data[n] = m[n];
5182             }
5183         }
5184         this.dirty = false;
5185         delete this.modified;
5186         this.editing = false;
5187         if(this.store){
5188             this.store.afterReject(this);
5189         }
5190     },
5191
5192     /**
5193      * Usually called by the {@link Roo.data.Store} which owns the Record.
5194      * Commits all changes made to the Record since either creation, or the last commit operation.
5195      * <p>
5196      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5197      * of commit operations.
5198      */
5199     commit : function(){
5200         this.dirty = false;
5201         delete this.modified;
5202         this.editing = false;
5203         if(this.store){
5204             this.store.afterCommit(this);
5205         }
5206     },
5207
5208     // private
5209     hasError : function(){
5210         return this.error != null;
5211     },
5212
5213     // private
5214     clearError : function(){
5215         this.error = null;
5216     },
5217
5218     /**
5219      * Creates a copy of this record.
5220      * @param {String} id (optional) A new record id if you don't want to use this record's id
5221      * @return {Record}
5222      */
5223     copy : function(newId) {
5224         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
5225     }
5226 };/*
5227  * Based on:
5228  * Ext JS Library 1.1.1
5229  * Copyright(c) 2006-2007, Ext JS, LLC.
5230  *
5231  * Originally Released Under LGPL - original licence link has changed is not relivant.
5232  *
5233  * Fork - LGPL
5234  * <script type="text/javascript">
5235  */
5236
5237
5238
5239 /**
5240  * @class Roo.data.Store
5241  * @extends Roo.util.Observable
5242  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
5243  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
5244  * <p>
5245  * 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
5246  * has no knowledge of the format of the data returned by the Proxy.<br>
5247  * <p>
5248  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
5249  * instances from the data object. These records are cached and made available through accessor functions.
5250  * @constructor
5251  * Creates a new Store.
5252  * @param {Object} config A config object containing the objects needed for the Store to access data,
5253  * and read the data into Records.
5254  */
5255 Roo.data.Store = function(config){
5256     this.data = new Roo.util.MixedCollection(false);
5257     this.data.getKey = function(o){
5258         return o.id;
5259     };
5260     this.baseParams = {};
5261     // private
5262     this.paramNames = {
5263         "start" : "start",
5264         "limit" : "limit",
5265         "sort" : "sort",
5266         "dir" : "dir",
5267         "multisort" : "_multisort"
5268     };
5269
5270     if(config && config.data){
5271         this.inlineData = config.data;
5272         delete config.data;
5273     }
5274
5275     Roo.apply(this, config);
5276     
5277     if(this.reader){ // reader passed
5278         this.reader = Roo.factory(this.reader, Roo.data);
5279         this.reader.xmodule = this.xmodule || false;
5280         if(!this.recordType){
5281             this.recordType = this.reader.recordType;
5282         }
5283         if(this.reader.onMetaChange){
5284             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
5285         }
5286     }
5287
5288     if(this.recordType){
5289         this.fields = this.recordType.prototype.fields;
5290     }
5291     this.modified = [];
5292
5293     this.addEvents({
5294         /**
5295          * @event datachanged
5296          * Fires when the data cache has changed, and a widget which is using this Store
5297          * as a Record cache should refresh its view.
5298          * @param {Store} this
5299          */
5300         datachanged : true,
5301         /**
5302          * @event metachange
5303          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
5304          * @param {Store} this
5305          * @param {Object} meta The JSON metadata
5306          */
5307         metachange : true,
5308         /**
5309          * @event add
5310          * Fires when Records have been added to the Store
5311          * @param {Store} this
5312          * @param {Roo.data.Record[]} records The array of Records added
5313          * @param {Number} index The index at which the record(s) were added
5314          */
5315         add : true,
5316         /**
5317          * @event remove
5318          * Fires when a Record has been removed from the Store
5319          * @param {Store} this
5320          * @param {Roo.data.Record} record The Record that was removed
5321          * @param {Number} index The index at which the record was removed
5322          */
5323         remove : true,
5324         /**
5325          * @event update
5326          * Fires when a Record has been updated
5327          * @param {Store} this
5328          * @param {Roo.data.Record} record The Record that was updated
5329          * @param {String} operation The update operation being performed.  Value may be one of:
5330          * <pre><code>
5331  Roo.data.Record.EDIT
5332  Roo.data.Record.REJECT
5333  Roo.data.Record.COMMIT
5334          * </code></pre>
5335          */
5336         update : true,
5337         /**
5338          * @event clear
5339          * Fires when the data cache has been cleared.
5340          * @param {Store} this
5341          */
5342         clear : true,
5343         /**
5344          * @event beforeload
5345          * Fires before a request is made for a new data object.  If the beforeload handler returns false
5346          * the load action will be canceled.
5347          * @param {Store} this
5348          * @param {Object} options The loading options that were specified (see {@link #load} for details)
5349          */
5350         beforeload : true,
5351         /**
5352          * @event beforeloadadd
5353          * Fires after a new set of Records has been loaded.
5354          * @param {Store} this
5355          * @param {Roo.data.Record[]} records The Records that were loaded
5356          * @param {Object} options The loading options that were specified (see {@link #load} for details)
5357          */
5358         beforeloadadd : true,
5359         /**
5360          * @event load
5361          * Fires after a new set of Records has been loaded, before they are added to the store.
5362          * @param {Store} this
5363          * @param {Roo.data.Record[]} records The Records that were loaded
5364          * @param {Object} options The loading options that were specified (see {@link #load} for details)
5365          * @params {Object} return from reader
5366          */
5367         load : true,
5368         /**
5369          * @event loadexception
5370          * Fires if an exception occurs in the Proxy during loading.
5371          * Called with the signature of the Proxy's "loadexception" event.
5372          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
5373          * 
5374          * @param {Proxy} 
5375          * @param {Object} return from JsonData.reader() - success, totalRecords, records
5376          * @param {Object} load options 
5377          * @param {Object} jsonData from your request (normally this contains the Exception)
5378          */
5379         loadexception : true
5380     });
5381     
5382     if(this.proxy){
5383         this.proxy = Roo.factory(this.proxy, Roo.data);
5384         this.proxy.xmodule = this.xmodule || false;
5385         this.relayEvents(this.proxy,  ["loadexception"]);
5386     }
5387     this.sortToggle = {};
5388     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
5389
5390     Roo.data.Store.superclass.constructor.call(this);
5391
5392     if(this.inlineData){
5393         this.loadData(this.inlineData);
5394         delete this.inlineData;
5395     }
5396 };
5397
5398 Roo.extend(Roo.data.Store, Roo.util.Observable, {
5399      /**
5400     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
5401     * without a remote query - used by combo/forms at present.
5402     */
5403     
5404     /**
5405     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
5406     */
5407     /**
5408     * @cfg {Array} data Inline data to be loaded when the store is initialized.
5409     */
5410     /**
5411     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
5412     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
5413     */
5414     /**
5415     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
5416     * on any HTTP request
5417     */
5418     /**
5419     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
5420     */
5421     /**
5422     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
5423     */
5424     multiSort: false,
5425     /**
5426     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
5427     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
5428     */
5429     remoteSort : false,
5430
5431     /**
5432     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
5433      * loaded or when a record is removed. (defaults to false).
5434     */
5435     pruneModifiedRecords : false,
5436
5437     // private
5438     lastOptions : null,
5439
5440     /**
5441      * Add Records to the Store and fires the add event.
5442      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
5443      */
5444     add : function(records){
5445         records = [].concat(records);
5446         for(var i = 0, len = records.length; i < len; i++){
5447             records[i].join(this);
5448         }
5449         var index = this.data.length;
5450         this.data.addAll(records);
5451         this.fireEvent("add", this, records, index);
5452     },
5453
5454     /**
5455      * Remove a Record from the Store and fires the remove event.
5456      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
5457      */
5458     remove : function(record){
5459         var index = this.data.indexOf(record);
5460         this.data.removeAt(index);
5461         if(this.pruneModifiedRecords){
5462             this.modified.remove(record);
5463         }
5464         this.fireEvent("remove", this, record, index);
5465     },
5466
5467     /**
5468      * Remove all Records from the Store and fires the clear event.
5469      */
5470     removeAll : function(){
5471         this.data.clear();
5472         if(this.pruneModifiedRecords){
5473             this.modified = [];
5474         }
5475         this.fireEvent("clear", this);
5476     },
5477
5478     /**
5479      * Inserts Records to the Store at the given index and fires the add event.
5480      * @param {Number} index The start index at which to insert the passed Records.
5481      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
5482      */
5483     insert : function(index, records){
5484         records = [].concat(records);
5485         for(var i = 0, len = records.length; i < len; i++){
5486             this.data.insert(index, records[i]);
5487             records[i].join(this);
5488         }
5489         this.fireEvent("add", this, records, index);
5490     },
5491
5492     /**
5493      * Get the index within the cache of the passed Record.
5494      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
5495      * @return {Number} The index of the passed Record. Returns -1 if not found.
5496      */
5497     indexOf : function(record){
5498         return this.data.indexOf(record);
5499     },
5500
5501     /**
5502      * Get the index within the cache of the Record with the passed id.
5503      * @param {String} id The id of the Record to find.
5504      * @return {Number} The index of the Record. Returns -1 if not found.
5505      */
5506     indexOfId : function(id){
5507         return this.data.indexOfKey(id);
5508     },
5509
5510     /**
5511      * Get the Record with the specified id.
5512      * @param {String} id The id of the Record to find.
5513      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
5514      */
5515     getById : function(id){
5516         return this.data.key(id);
5517     },
5518
5519     /**
5520      * Get the Record at the specified index.
5521      * @param {Number} index The index of the Record to find.
5522      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5523      */
5524     getAt : function(index){
5525         return this.data.itemAt(index);
5526     },
5527
5528     /**
5529      * Returns a range of Records between specified indices.
5530      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5531      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5532      * @return {Roo.data.Record[]} An array of Records
5533      */
5534     getRange : function(start, end){
5535         return this.data.getRange(start, end);
5536     },
5537
5538     // private
5539     storeOptions : function(o){
5540         o = Roo.apply({}, o);
5541         delete o.callback;
5542         delete o.scope;
5543         this.lastOptions = o;
5544     },
5545
5546     /**
5547      * Loads the Record cache from the configured Proxy using the configured Reader.
5548      * <p>
5549      * If using remote paging, then the first load call must specify the <em>start</em>
5550      * and <em>limit</em> properties in the options.params property to establish the initial
5551      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5552      * <p>
5553      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5554      * and this call will return before the new data has been loaded. Perform any post-processing
5555      * in a callback function, or in a "load" event handler.</strong>
5556      * <p>
5557      * @param {Object} options An object containing properties which control loading options:<ul>
5558      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5559      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5560      * passed the following arguments:<ul>
5561      * <li>r : Roo.data.Record[]</li>
5562      * <li>options: Options object from the load call</li>
5563      * <li>success: Boolean success indicator</li></ul></li>
5564      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5565      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5566      * </ul>
5567      */
5568     load : function(options){
5569         options = options || {};
5570         if(this.fireEvent("beforeload", this, options) !== false){
5571             this.storeOptions(options);
5572             var p = Roo.apply(options.params || {}, this.baseParams);
5573             // if meta was not loaded from remote source.. try requesting it.
5574             if (!this.reader.metaFromRemote) {
5575                 p._requestMeta = 1;
5576             }
5577             if(this.sortInfo && this.remoteSort){
5578                 var pn = this.paramNames;
5579                 p[pn["sort"]] = this.sortInfo.field;
5580                 p[pn["dir"]] = this.sortInfo.direction;
5581             }
5582             if (this.multiSort) {
5583                 var pn = this.paramNames;
5584                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5585             }
5586             
5587             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5588         }
5589     },
5590
5591     /**
5592      * Reloads the Record cache from the configured Proxy using the configured Reader and
5593      * the options from the last load operation performed.
5594      * @param {Object} options (optional) An object containing properties which may override the options
5595      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5596      * the most recently used options are reused).
5597      */
5598     reload : function(options){
5599         this.load(Roo.applyIf(options||{}, this.lastOptions));
5600     },
5601
5602     // private
5603     // Called as a callback by the Reader during a load operation.
5604     loadRecords : function(o, options, success){
5605         if(!o || success === false){
5606             if(success !== false){
5607                 this.fireEvent("load", this, [], options, o);
5608             }
5609             if(options.callback){
5610                 options.callback.call(options.scope || this, [], options, false);
5611             }
5612             return;
5613         }
5614         // if data returned failure - throw an exception.
5615         if (o.success === false) {
5616             // show a message if no listener is registered.
5617             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5618                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5619             }
5620             // loadmask wil be hooked into this..
5621             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5622             return;
5623         }
5624         var r = o.records, t = o.totalRecords || r.length;
5625         
5626         this.fireEvent("beforeloadadd", this, r, options, o);
5627         
5628         if(!options || options.add !== true){
5629             if(this.pruneModifiedRecords){
5630                 this.modified = [];
5631             }
5632             for(var i = 0, len = r.length; i < len; i++){
5633                 r[i].join(this);
5634             }
5635             if(this.snapshot){
5636                 this.data = this.snapshot;
5637                 delete this.snapshot;
5638             }
5639             this.data.clear();
5640             this.data.addAll(r);
5641             this.totalLength = t;
5642             this.applySort();
5643             this.fireEvent("datachanged", this);
5644         }else{
5645             this.totalLength = Math.max(t, this.data.length+r.length);
5646             this.add(r);
5647         }
5648         this.fireEvent("load", this, r, options, o);
5649         if(options.callback){
5650             options.callback.call(options.scope || this, r, options, true);
5651         }
5652     },
5653
5654
5655     /**
5656      * Loads data from a passed data block. A Reader which understands the format of the data
5657      * must have been configured in the constructor.
5658      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5659      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5660      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5661      */
5662     loadData : function(o, append){
5663         var r = this.reader.readRecords(o);
5664         this.loadRecords(r, {add: append}, true);
5665     },
5666
5667     /**
5668      * Gets the number of cached records.
5669      * <p>
5670      * <em>If using paging, this may not be the total size of the dataset. If the data object
5671      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5672      * the data set size</em>
5673      */
5674     getCount : function(){
5675         return this.data.length || 0;
5676     },
5677
5678     /**
5679      * Gets the total number of records in the dataset as returned by the server.
5680      * <p>
5681      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5682      * the dataset size</em>
5683      */
5684     getTotalCount : function(){
5685         return this.totalLength || 0;
5686     },
5687
5688     /**
5689      * Returns the sort state of the Store as an object with two properties:
5690      * <pre><code>
5691  field {String} The name of the field by which the Records are sorted
5692  direction {String} The sort order, "ASC" or "DESC"
5693      * </code></pre>
5694      */
5695     getSortState : function(){
5696         return this.sortInfo;
5697     },
5698
5699     // private
5700     applySort : function(){
5701         if(this.sortInfo && !this.remoteSort){
5702             var s = this.sortInfo, f = s.field;
5703             var st = this.fields.get(f).sortType;
5704             var fn = function(r1, r2){
5705                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5706                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5707             };
5708             this.data.sort(s.direction, fn);
5709             if(this.snapshot && this.snapshot != this.data){
5710                 this.snapshot.sort(s.direction, fn);
5711             }
5712         }
5713     },
5714
5715     /**
5716      * Sets the default sort column and order to be used by the next load operation.
5717      * @param {String} fieldName The name of the field to sort by.
5718      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5719      */
5720     setDefaultSort : function(field, dir){
5721         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5722     },
5723
5724     /**
5725      * Sort the Records.
5726      * If remote sorting is used, the sort is performed on the server, and the cache is
5727      * reloaded. If local sorting is used, the cache is sorted internally.
5728      * @param {String} fieldName The name of the field to sort by.
5729      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5730      */
5731     sort : function(fieldName, dir){
5732         var f = this.fields.get(fieldName);
5733         if(!dir){
5734             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5735             
5736             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5737                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5738             }else{
5739                 dir = f.sortDir;
5740             }
5741         }
5742         this.sortToggle[f.name] = dir;
5743         this.sortInfo = {field: f.name, direction: dir};
5744         if(!this.remoteSort){
5745             this.applySort();
5746             this.fireEvent("datachanged", this);
5747         }else{
5748             this.load(this.lastOptions);
5749         }
5750     },
5751
5752     /**
5753      * Calls the specified function for each of the Records in the cache.
5754      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5755      * Returning <em>false</em> aborts and exits the iteration.
5756      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5757      */
5758     each : function(fn, scope){
5759         this.data.each(fn, scope);
5760     },
5761
5762     /**
5763      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5764      * (e.g., during paging).
5765      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5766      */
5767     getModifiedRecords : function(){
5768         return this.modified;
5769     },
5770
5771     // private
5772     createFilterFn : function(property, value, anyMatch){
5773         if(!value.exec){ // not a regex
5774             value = String(value);
5775             if(value.length == 0){
5776                 return false;
5777             }
5778             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5779         }
5780         return function(r){
5781             return value.test(r.data[property]);
5782         };
5783     },
5784
5785     /**
5786      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5787      * @param {String} property A field on your records
5788      * @param {Number} start The record index to start at (defaults to 0)
5789      * @param {Number} end The last record index to include (defaults to length - 1)
5790      * @return {Number} The sum
5791      */
5792     sum : function(property, start, end){
5793         var rs = this.data.items, v = 0;
5794         start = start || 0;
5795         end = (end || end === 0) ? end : rs.length-1;
5796
5797         for(var i = start; i <= end; i++){
5798             v += (rs[i].data[property] || 0);
5799         }
5800         return v;
5801     },
5802
5803     /**
5804      * Filter the records by a specified property.
5805      * @param {String} field A field on your records
5806      * @param {String/RegExp} value Either a string that the field
5807      * should start with or a RegExp to test against the field
5808      * @param {Boolean} anyMatch True to match any part not just the beginning
5809      */
5810     filter : function(property, value, anyMatch){
5811         var fn = this.createFilterFn(property, value, anyMatch);
5812         return fn ? this.filterBy(fn) : this.clearFilter();
5813     },
5814
5815     /**
5816      * Filter by a function. The specified function will be called with each
5817      * record in this data source. If the function returns true the record is included,
5818      * otherwise it is filtered.
5819      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5820      * @param {Object} scope (optional) The scope of the function (defaults to this)
5821      */
5822     filterBy : function(fn, scope){
5823         this.snapshot = this.snapshot || this.data;
5824         this.data = this.queryBy(fn, scope||this);
5825         this.fireEvent("datachanged", this);
5826     },
5827
5828     /**
5829      * Query the records by a specified property.
5830      * @param {String} field A field on your records
5831      * @param {String/RegExp} value Either a string that the field
5832      * should start with or a RegExp to test against the field
5833      * @param {Boolean} anyMatch True to match any part not just the beginning
5834      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5835      */
5836     query : function(property, value, anyMatch){
5837         var fn = this.createFilterFn(property, value, anyMatch);
5838         return fn ? this.queryBy(fn) : this.data.clone();
5839     },
5840
5841     /**
5842      * Query by a function. The specified function will be called with each
5843      * record in this data source. If the function returns true the record is included
5844      * in the results.
5845      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5846      * @param {Object} scope (optional) The scope of the function (defaults to this)
5847       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5848      **/
5849     queryBy : function(fn, scope){
5850         var data = this.snapshot || this.data;
5851         return data.filterBy(fn, scope||this);
5852     },
5853
5854     /**
5855      * Collects unique values for a particular dataIndex from this store.
5856      * @param {String} dataIndex The property to collect
5857      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5858      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5859      * @return {Array} An array of the unique values
5860      **/
5861     collect : function(dataIndex, allowNull, bypassFilter){
5862         var d = (bypassFilter === true && this.snapshot) ?
5863                 this.snapshot.items : this.data.items;
5864         var v, sv, r = [], l = {};
5865         for(var i = 0, len = d.length; i < len; i++){
5866             v = d[i].data[dataIndex];
5867             sv = String(v);
5868             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5869                 l[sv] = true;
5870                 r[r.length] = v;
5871             }
5872         }
5873         return r;
5874     },
5875
5876     /**
5877      * Revert to a view of the Record cache with no filtering applied.
5878      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5879      */
5880     clearFilter : function(suppressEvent){
5881         if(this.snapshot && this.snapshot != this.data){
5882             this.data = this.snapshot;
5883             delete this.snapshot;
5884             if(suppressEvent !== true){
5885                 this.fireEvent("datachanged", this);
5886             }
5887         }
5888     },
5889
5890     // private
5891     afterEdit : function(record){
5892         if(this.modified.indexOf(record) == -1){
5893             this.modified.push(record);
5894         }
5895         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5896     },
5897     
5898     // private
5899     afterReject : function(record){
5900         this.modified.remove(record);
5901         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5902     },
5903
5904     // private
5905     afterCommit : function(record){
5906         this.modified.remove(record);
5907         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5908     },
5909
5910     /**
5911      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5912      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5913      */
5914     commitChanges : function(){
5915         var m = this.modified.slice(0);
5916         this.modified = [];
5917         for(var i = 0, len = m.length; i < len; i++){
5918             m[i].commit();
5919         }
5920     },
5921
5922     /**
5923      * Cancel outstanding changes on all changed records.
5924      */
5925     rejectChanges : function(){
5926         var m = this.modified.slice(0);
5927         this.modified = [];
5928         for(var i = 0, len = m.length; i < len; i++){
5929             m[i].reject();
5930         }
5931     },
5932
5933     onMetaChange : function(meta, rtype, o){
5934         this.recordType = rtype;
5935         this.fields = rtype.prototype.fields;
5936         delete this.snapshot;
5937         this.sortInfo = meta.sortInfo || this.sortInfo;
5938         this.modified = [];
5939         this.fireEvent('metachange', this, this.reader.meta);
5940     }
5941 });/*
5942  * Based on:
5943  * Ext JS Library 1.1.1
5944  * Copyright(c) 2006-2007, Ext JS, LLC.
5945  *
5946  * Originally Released Under LGPL - original licence link has changed is not relivant.
5947  *
5948  * Fork - LGPL
5949  * <script type="text/javascript">
5950  */
5951
5952 /**
5953  * @class Roo.data.SimpleStore
5954  * @extends Roo.data.Store
5955  * Small helper class to make creating Stores from Array data easier.
5956  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5957  * @cfg {Array} fields An array of field definition objects, or field name strings.
5958  * @cfg {Array} data The multi-dimensional array of data
5959  * @constructor
5960  * @param {Object} config
5961  */
5962 Roo.data.SimpleStore = function(config){
5963     Roo.data.SimpleStore.superclass.constructor.call(this, {
5964         isLocal : true,
5965         reader: new Roo.data.ArrayReader({
5966                 id: config.id
5967             },
5968             Roo.data.Record.create(config.fields)
5969         ),
5970         proxy : new Roo.data.MemoryProxy(config.data)
5971     });
5972     this.load();
5973 };
5974 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5975  * Based on:
5976  * Ext JS Library 1.1.1
5977  * Copyright(c) 2006-2007, Ext JS, LLC.
5978  *
5979  * Originally Released Under LGPL - original licence link has changed is not relivant.
5980  *
5981  * Fork - LGPL
5982  * <script type="text/javascript">
5983  */
5984
5985 /**
5986 /**
5987  * @extends Roo.data.Store
5988  * @class Roo.data.JsonStore
5989  * Small helper class to make creating Stores for JSON data easier. <br/>
5990 <pre><code>
5991 var store = new Roo.data.JsonStore({
5992     url: 'get-images.php',
5993     root: 'images',
5994     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5995 });
5996 </code></pre>
5997  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5998  * JsonReader and HttpProxy (unless inline data is provided).</b>
5999  * @cfg {Array} fields An array of field definition objects, or field name strings.
6000  * @constructor
6001  * @param {Object} config
6002  */
6003 Roo.data.JsonStore = function(c){
6004     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6005         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6006         reader: new Roo.data.JsonReader(c, c.fields)
6007     }));
6008 };
6009 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6010  * Based on:
6011  * Ext JS Library 1.1.1
6012  * Copyright(c) 2006-2007, Ext JS, LLC.
6013  *
6014  * Originally Released Under LGPL - original licence link has changed is not relivant.
6015  *
6016  * Fork - LGPL
6017  * <script type="text/javascript">
6018  */
6019
6020  
6021 Roo.data.Field = function(config){
6022     if(typeof config == "string"){
6023         config = {name: config};
6024     }
6025     Roo.apply(this, config);
6026     
6027     if(!this.type){
6028         this.type = "auto";
6029     }
6030     
6031     var st = Roo.data.SortTypes;
6032     // named sortTypes are supported, here we look them up
6033     if(typeof this.sortType == "string"){
6034         this.sortType = st[this.sortType];
6035     }
6036     
6037     // set default sortType for strings and dates
6038     if(!this.sortType){
6039         switch(this.type){
6040             case "string":
6041                 this.sortType = st.asUCString;
6042                 break;
6043             case "date":
6044                 this.sortType = st.asDate;
6045                 break;
6046             default:
6047                 this.sortType = st.none;
6048         }
6049     }
6050
6051     // define once
6052     var stripRe = /[\$,%]/g;
6053
6054     // prebuilt conversion function for this field, instead of
6055     // switching every time we're reading a value
6056     if(!this.convert){
6057         var cv, dateFormat = this.dateFormat;
6058         switch(this.type){
6059             case "":
6060             case "auto":
6061             case undefined:
6062                 cv = function(v){ return v; };
6063                 break;
6064             case "string":
6065                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6066                 break;
6067             case "int":
6068                 cv = function(v){
6069                     return v !== undefined && v !== null && v !== '' ?
6070                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6071                     };
6072                 break;
6073             case "float":
6074                 cv = function(v){
6075                     return v !== undefined && v !== null && v !== '' ?
6076                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6077                     };
6078                 break;
6079             case "bool":
6080             case "boolean":
6081                 cv = function(v){ return v === true || v === "true" || v == 1; };
6082                 break;
6083             case "date":
6084                 cv = function(v){
6085                     if(!v){
6086                         return '';
6087                     }
6088                     if(v instanceof Date){
6089                         return v;
6090                     }
6091                     if(dateFormat){
6092                         if(dateFormat == "timestamp"){
6093                             return new Date(v*1000);
6094                         }
6095                         return Date.parseDate(v, dateFormat);
6096                     }
6097                     var parsed = Date.parse(v);
6098                     return parsed ? new Date(parsed) : null;
6099                 };
6100              break;
6101             
6102         }
6103         this.convert = cv;
6104     }
6105 };
6106
6107 Roo.data.Field.prototype = {
6108     dateFormat: null,
6109     defaultValue: "",
6110     mapping: null,
6111     sortType : null,
6112     sortDir : "ASC"
6113 };/*
6114  * Based on:
6115  * Ext JS Library 1.1.1
6116  * Copyright(c) 2006-2007, Ext JS, LLC.
6117  *
6118  * Originally Released Under LGPL - original licence link has changed is not relivant.
6119  *
6120  * Fork - LGPL
6121  * <script type="text/javascript">
6122  */
6123  
6124 // Base class for reading structured data from a data source.  This class is intended to be
6125 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6126
6127 /**
6128  * @class Roo.data.DataReader
6129  * Base class for reading structured data from a data source.  This class is intended to be
6130  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6131  */
6132
6133 Roo.data.DataReader = function(meta, recordType){
6134     
6135     this.meta = meta;
6136     
6137     this.recordType = recordType instanceof Array ? 
6138         Roo.data.Record.create(recordType) : recordType;
6139 };
6140
6141 Roo.data.DataReader.prototype = {
6142      /**
6143      * Create an empty record
6144      * @param {Object} data (optional) - overlay some values
6145      * @return {Roo.data.Record} record created.
6146      */
6147     newRow :  function(d) {
6148         var da =  {};
6149         this.recordType.prototype.fields.each(function(c) {
6150             switch( c.type) {
6151                 case 'int' : da[c.name] = 0; break;
6152                 case 'date' : da[c.name] = new Date(); break;
6153                 case 'float' : da[c.name] = 0.0; break;
6154                 case 'boolean' : da[c.name] = false; break;
6155                 default : da[c.name] = ""; break;
6156             }
6157             
6158         });
6159         return new this.recordType(Roo.apply(da, d));
6160     }
6161     
6162 };/*
6163  * Based on:
6164  * Ext JS Library 1.1.1
6165  * Copyright(c) 2006-2007, Ext JS, LLC.
6166  *
6167  * Originally Released Under LGPL - original licence link has changed is not relivant.
6168  *
6169  * Fork - LGPL
6170  * <script type="text/javascript">
6171  */
6172
6173 /**
6174  * @class Roo.data.DataProxy
6175  * @extends Roo.data.Observable
6176  * This class is an abstract base class for implementations which provide retrieval of
6177  * unformatted data objects.<br>
6178  * <p>
6179  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6180  * (of the appropriate type which knows how to parse the data object) to provide a block of
6181  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6182  * <p>
6183  * Custom implementations must implement the load method as described in
6184  * {@link Roo.data.HttpProxy#load}.
6185  */
6186 Roo.data.DataProxy = function(){
6187     this.addEvents({
6188         /**
6189          * @event beforeload
6190          * Fires before a network request is made to retrieve a data object.
6191          * @param {Object} This DataProxy object.
6192          * @param {Object} params The params parameter to the load function.
6193          */
6194         beforeload : true,
6195         /**
6196          * @event load
6197          * Fires before the load method's callback is called.
6198          * @param {Object} This DataProxy object.
6199          * @param {Object} o The data object.
6200          * @param {Object} arg The callback argument object passed to the load function.
6201          */
6202         load : true,
6203         /**
6204          * @event loadexception
6205          * Fires if an Exception occurs during data retrieval.
6206          * @param {Object} This DataProxy object.
6207          * @param {Object} o The data object.
6208          * @param {Object} arg The callback argument object passed to the load function.
6209          * @param {Object} e The Exception.
6210          */
6211         loadexception : true
6212     });
6213     Roo.data.DataProxy.superclass.constructor.call(this);
6214 };
6215
6216 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
6217
6218     /**
6219      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
6220      */
6221 /*
6222  * Based on:
6223  * Ext JS Library 1.1.1
6224  * Copyright(c) 2006-2007, Ext JS, LLC.
6225  *
6226  * Originally Released Under LGPL - original licence link has changed is not relivant.
6227  *
6228  * Fork - LGPL
6229  * <script type="text/javascript">
6230  */
6231 /**
6232  * @class Roo.data.MemoryProxy
6233  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
6234  * to the Reader when its load method is called.
6235  * @constructor
6236  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
6237  */
6238 Roo.data.MemoryProxy = function(data){
6239     if (data.data) {
6240         data = data.data;
6241     }
6242     Roo.data.MemoryProxy.superclass.constructor.call(this);
6243     this.data = data;
6244 };
6245
6246 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
6247     /**
6248      * Load data from the requested source (in this case an in-memory
6249      * data object passed to the constructor), read the data object into
6250      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6251      * process that block using the passed callback.
6252      * @param {Object} params This parameter is not used by the MemoryProxy class.
6253      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6254      * object into a block of Roo.data.Records.
6255      * @param {Function} callback The function into which to pass the block of Roo.data.records.
6256      * The function must be passed <ul>
6257      * <li>The Record block object</li>
6258      * <li>The "arg" argument from the load function</li>
6259      * <li>A boolean success indicator</li>
6260      * </ul>
6261      * @param {Object} scope The scope in which to call the callback
6262      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6263      */
6264     load : function(params, reader, callback, scope, arg){
6265         params = params || {};
6266         var result;
6267         try {
6268             result = reader.readRecords(this.data);
6269         }catch(e){
6270             this.fireEvent("loadexception", this, arg, null, e);
6271             callback.call(scope, null, arg, false);
6272             return;
6273         }
6274         callback.call(scope, result, arg, true);
6275     },
6276     
6277     // private
6278     update : function(params, records){
6279         
6280     }
6281 });/*
6282  * Based on:
6283  * Ext JS Library 1.1.1
6284  * Copyright(c) 2006-2007, Ext JS, LLC.
6285  *
6286  * Originally Released Under LGPL - original licence link has changed is not relivant.
6287  *
6288  * Fork - LGPL
6289  * <script type="text/javascript">
6290  */
6291 /**
6292  * @class Roo.data.HttpProxy
6293  * @extends Roo.data.DataProxy
6294  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
6295  * configured to reference a certain URL.<br><br>
6296  * <p>
6297  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
6298  * from which the running page was served.<br><br>
6299  * <p>
6300  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
6301  * <p>
6302  * Be aware that to enable the browser to parse an XML document, the server must set
6303  * the Content-Type header in the HTTP response to "text/xml".
6304  * @constructor
6305  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
6306  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
6307  * will be used to make the request.
6308  */
6309 Roo.data.HttpProxy = function(conn){
6310     Roo.data.HttpProxy.superclass.constructor.call(this);
6311     // is conn a conn config or a real conn?
6312     this.conn = conn;
6313     this.useAjax = !conn || !conn.events;
6314   
6315 };
6316
6317 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
6318     // thse are take from connection...
6319     
6320     /**
6321      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
6322      */
6323     /**
6324      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
6325      * extra parameters to each request made by this object. (defaults to undefined)
6326      */
6327     /**
6328      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
6329      *  to each request made by this object. (defaults to undefined)
6330      */
6331     /**
6332      * @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)
6333      */
6334     /**
6335      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
6336      */
6337      /**
6338      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
6339      * @type Boolean
6340      */
6341   
6342
6343     /**
6344      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
6345      * @type Boolean
6346      */
6347     /**
6348      * Return the {@link Roo.data.Connection} object being used by this Proxy.
6349      * @return {Connection} The Connection object. This object may be used to subscribe to events on
6350      * a finer-grained basis than the DataProxy events.
6351      */
6352     getConnection : function(){
6353         return this.useAjax ? Roo.Ajax : this.conn;
6354     },
6355
6356     /**
6357      * Load data from the configured {@link Roo.data.Connection}, read the data object into
6358      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
6359      * process that block using the passed callback.
6360      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6361      * for the request to the remote server.
6362      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6363      * object into a block of Roo.data.Records.
6364      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6365      * The function must be passed <ul>
6366      * <li>The Record block object</li>
6367      * <li>The "arg" argument from the load function</li>
6368      * <li>A boolean success indicator</li>
6369      * </ul>
6370      * @param {Object} scope The scope in which to call the callback
6371      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6372      */
6373     load : function(params, reader, callback, scope, arg){
6374         if(this.fireEvent("beforeload", this, params) !== false){
6375             var  o = {
6376                 params : params || {},
6377                 request: {
6378                     callback : callback,
6379                     scope : scope,
6380                     arg : arg
6381                 },
6382                 reader: reader,
6383                 callback : this.loadResponse,
6384                 scope: this
6385             };
6386             if(this.useAjax){
6387                 Roo.applyIf(o, this.conn);
6388                 if(this.activeRequest){
6389                     Roo.Ajax.abort(this.activeRequest);
6390                 }
6391                 this.activeRequest = Roo.Ajax.request(o);
6392             }else{
6393                 this.conn.request(o);
6394             }
6395         }else{
6396             callback.call(scope||this, null, arg, false);
6397         }
6398     },
6399
6400     // private
6401     loadResponse : function(o, success, response){
6402         delete this.activeRequest;
6403         if(!success){
6404             this.fireEvent("loadexception", this, o, response);
6405             o.request.callback.call(o.request.scope, null, o.request.arg, false);
6406             return;
6407         }
6408         var result;
6409         try {
6410             result = o.reader.read(response);
6411         }catch(e){
6412             this.fireEvent("loadexception", this, o, response, e);
6413             o.request.callback.call(o.request.scope, null, o.request.arg, false);
6414             return;
6415         }
6416         
6417         this.fireEvent("load", this, o, o.request.arg);
6418         o.request.callback.call(o.request.scope, result, o.request.arg, true);
6419     },
6420
6421     // private
6422     update : function(dataSet){
6423
6424     },
6425
6426     // private
6427     updateResponse : function(dataSet){
6428
6429     }
6430 });/*
6431  * Based on:
6432  * Ext JS Library 1.1.1
6433  * Copyright(c) 2006-2007, Ext JS, LLC.
6434  *
6435  * Originally Released Under LGPL - original licence link has changed is not relivant.
6436  *
6437  * Fork - LGPL
6438  * <script type="text/javascript">
6439  */
6440
6441 /**
6442  * @class Roo.data.ScriptTagProxy
6443  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
6444  * other than the originating domain of the running page.<br><br>
6445  * <p>
6446  * <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
6447  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
6448  * <p>
6449  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
6450  * source code that is used as the source inside a &lt;script> tag.<br><br>
6451  * <p>
6452  * In order for the browser to process the returned data, the server must wrap the data object
6453  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
6454  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
6455  * depending on whether the callback name was passed:
6456  * <p>
6457  * <pre><code>
6458 boolean scriptTag = false;
6459 String cb = request.getParameter("callback");
6460 if (cb != null) {
6461     scriptTag = true;
6462     response.setContentType("text/javascript");
6463 } else {
6464     response.setContentType("application/x-json");
6465 }
6466 Writer out = response.getWriter();
6467 if (scriptTag) {
6468     out.write(cb + "(");
6469 }
6470 out.print(dataBlock.toJsonString());
6471 if (scriptTag) {
6472     out.write(");");
6473 }
6474 </pre></code>
6475  *
6476  * @constructor
6477  * @param {Object} config A configuration object.
6478  */
6479 Roo.data.ScriptTagProxy = function(config){
6480     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
6481     Roo.apply(this, config);
6482     this.head = document.getElementsByTagName("head")[0];
6483 };
6484
6485 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
6486
6487 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
6488     /**
6489      * @cfg {String} url The URL from which to request the data object.
6490      */
6491     /**
6492      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
6493      */
6494     timeout : 30000,
6495     /**
6496      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
6497      * the server the name of the callback function set up by the load call to process the returned data object.
6498      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
6499      * javascript output which calls this named function passing the data object as its only parameter.
6500      */
6501     callbackParam : "callback",
6502     /**
6503      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6504      * name to the request.
6505      */
6506     nocache : true,
6507
6508     /**
6509      * Load data from the configured URL, read the data object into
6510      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6511      * process that block using the passed callback.
6512      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6513      * for the request to the remote server.
6514      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6515      * object into a block of Roo.data.Records.
6516      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6517      * The function must be passed <ul>
6518      * <li>The Record block object</li>
6519      * <li>The "arg" argument from the load function</li>
6520      * <li>A boolean success indicator</li>
6521      * </ul>
6522      * @param {Object} scope The scope in which to call the callback
6523      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6524      */
6525     load : function(params, reader, callback, scope, arg){
6526         if(this.fireEvent("beforeload", this, params) !== false){
6527
6528             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6529
6530             var url = this.url;
6531             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6532             if(this.nocache){
6533                 url += "&_dc=" + (new Date().getTime());
6534             }
6535             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6536             var trans = {
6537                 id : transId,
6538                 cb : "stcCallback"+transId,
6539                 scriptId : "stcScript"+transId,
6540                 params : params,
6541                 arg : arg,
6542                 url : url,
6543                 callback : callback,
6544                 scope : scope,
6545                 reader : reader
6546             };
6547             var conn = this;
6548
6549             window[trans.cb] = function(o){
6550                 conn.handleResponse(o, trans);
6551             };
6552
6553             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6554
6555             if(this.autoAbort !== false){
6556                 this.abort();
6557             }
6558
6559             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6560
6561             var script = document.createElement("script");
6562             script.setAttribute("src", url);
6563             script.setAttribute("type", "text/javascript");
6564             script.setAttribute("id", trans.scriptId);
6565             this.head.appendChild(script);
6566
6567             this.trans = trans;
6568         }else{
6569             callback.call(scope||this, null, arg, false);
6570         }
6571     },
6572
6573     // private
6574     isLoading : function(){
6575         return this.trans ? true : false;
6576     },
6577
6578     /**
6579      * Abort the current server request.
6580      */
6581     abort : function(){
6582         if(this.isLoading()){
6583             this.destroyTrans(this.trans);
6584         }
6585     },
6586
6587     // private
6588     destroyTrans : function(trans, isLoaded){
6589         this.head.removeChild(document.getElementById(trans.scriptId));
6590         clearTimeout(trans.timeoutId);
6591         if(isLoaded){
6592             window[trans.cb] = undefined;
6593             try{
6594                 delete window[trans.cb];
6595             }catch(e){}
6596         }else{
6597             // if hasn't been loaded, wait for load to remove it to prevent script error
6598             window[trans.cb] = function(){
6599                 window[trans.cb] = undefined;
6600                 try{
6601                     delete window[trans.cb];
6602                 }catch(e){}
6603             };
6604         }
6605     },
6606
6607     // private
6608     handleResponse : function(o, trans){
6609         this.trans = false;
6610         this.destroyTrans(trans, true);
6611         var result;
6612         try {
6613             result = trans.reader.readRecords(o);
6614         }catch(e){
6615             this.fireEvent("loadexception", this, o, trans.arg, e);
6616             trans.callback.call(trans.scope||window, null, trans.arg, false);
6617             return;
6618         }
6619         this.fireEvent("load", this, o, trans.arg);
6620         trans.callback.call(trans.scope||window, result, trans.arg, true);
6621     },
6622
6623     // private
6624     handleFailure : function(trans){
6625         this.trans = false;
6626         this.destroyTrans(trans, false);
6627         this.fireEvent("loadexception", this, null, trans.arg);
6628         trans.callback.call(trans.scope||window, null, trans.arg, false);
6629     }
6630 });/*
6631  * Based on:
6632  * Ext JS Library 1.1.1
6633  * Copyright(c) 2006-2007, Ext JS, LLC.
6634  *
6635  * Originally Released Under LGPL - original licence link has changed is not relivant.
6636  *
6637  * Fork - LGPL
6638  * <script type="text/javascript">
6639  */
6640
6641 /**
6642  * @class Roo.data.JsonReader
6643  * @extends Roo.data.DataReader
6644  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6645  * based on mappings in a provided Roo.data.Record constructor.
6646  * 
6647  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6648  * in the reply previously. 
6649  * 
6650  * <p>
6651  * Example code:
6652  * <pre><code>
6653 var RecordDef = Roo.data.Record.create([
6654     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6655     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6656 ]);
6657 var myReader = new Roo.data.JsonReader({
6658     totalProperty: "results",    // The property which contains the total dataset size (optional)
6659     root: "rows",                // The property which contains an Array of row objects
6660     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6661 }, RecordDef);
6662 </code></pre>
6663  * <p>
6664  * This would consume a JSON file like this:
6665  * <pre><code>
6666 { 'results': 2, 'rows': [
6667     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6668     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6669 }
6670 </code></pre>
6671  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6672  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6673  * paged from the remote server.
6674  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6675  * @cfg {String} root name of the property which contains the Array of row objects.
6676  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6677  * @constructor
6678  * Create a new JsonReader
6679  * @param {Object} meta Metadata configuration options
6680  * @param {Object} recordType Either an Array of field definition objects,
6681  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6682  */
6683 Roo.data.JsonReader = function(meta, recordType){
6684     
6685     meta = meta || {};
6686     // set some defaults:
6687     Roo.applyIf(meta, {
6688         totalProperty: 'total',
6689         successProperty : 'success',
6690         root : 'data',
6691         id : 'id'
6692     });
6693     
6694     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6695 };
6696 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6697     
6698     /**
6699      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6700      * Used by Store query builder to append _requestMeta to params.
6701      * 
6702      */
6703     metaFromRemote : false,
6704     /**
6705      * This method is only used by a DataProxy which has retrieved data from a remote server.
6706      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6707      * @return {Object} data A data block which is used by an Roo.data.Store object as
6708      * a cache of Roo.data.Records.
6709      */
6710     read : function(response){
6711         var json = response.responseText;
6712        
6713         var o = /* eval:var:o */ eval("("+json+")");
6714         if(!o) {
6715             throw {message: "JsonReader.read: Json object not found"};
6716         }
6717         
6718         if(o.metaData){
6719             
6720             delete this.ef;
6721             this.metaFromRemote = true;
6722             this.meta = o.metaData;
6723             this.recordType = Roo.data.Record.create(o.metaData.fields);
6724             this.onMetaChange(this.meta, this.recordType, o);
6725         }
6726         return this.readRecords(o);
6727     },
6728
6729     // private function a store will implement
6730     onMetaChange : function(meta, recordType, o){
6731
6732     },
6733
6734     /**
6735          * @ignore
6736          */
6737     simpleAccess: function(obj, subsc) {
6738         return obj[subsc];
6739     },
6740
6741         /**
6742          * @ignore
6743          */
6744     getJsonAccessor: function(){
6745         var re = /[\[\.]/;
6746         return function(expr) {
6747             try {
6748                 return(re.test(expr))
6749                     ? new Function("obj", "return obj." + expr)
6750                     : function(obj){
6751                         return obj[expr];
6752                     };
6753             } catch(e){}
6754             return Roo.emptyFn;
6755         };
6756     }(),
6757
6758     /**
6759      * Create a data block containing Roo.data.Records from an XML document.
6760      * @param {Object} o An object which contains an Array of row objects in the property specified
6761      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6762      * which contains the total size of the dataset.
6763      * @return {Object} data A data block which is used by an Roo.data.Store object as
6764      * a cache of Roo.data.Records.
6765      */
6766     readRecords : function(o){
6767         /**
6768          * After any data loads, the raw JSON data is available for further custom processing.
6769          * @type Object
6770          */
6771         this.o = o;
6772         var s = this.meta, Record = this.recordType,
6773             f = Record.prototype.fields, fi = f.items, fl = f.length;
6774
6775 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6776         if (!this.ef) {
6777             if(s.totalProperty) {
6778                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6779                 }
6780                 if(s.successProperty) {
6781                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6782                 }
6783                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6784                 if (s.id) {
6785                         var g = this.getJsonAccessor(s.id);
6786                         this.getId = function(rec) {
6787                                 var r = g(rec);
6788                                 return (r === undefined || r === "") ? null : r;
6789                         };
6790                 } else {
6791                         this.getId = function(){return null;};
6792                 }
6793             this.ef = [];
6794             for(var jj = 0; jj < fl; jj++){
6795                 f = fi[jj];
6796                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6797                 this.ef[jj] = this.getJsonAccessor(map);
6798             }
6799         }
6800
6801         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6802         if(s.totalProperty){
6803             var vt = parseInt(this.getTotal(o), 10);
6804             if(!isNaN(vt)){
6805                 totalRecords = vt;
6806             }
6807         }
6808         if(s.successProperty){
6809             var vs = this.getSuccess(o);
6810             if(vs === false || vs === 'false'){
6811                 success = false;
6812             }
6813         }
6814         var records = [];
6815             for(var i = 0; i < c; i++){
6816                     var n = root[i];
6817                 var values = {};
6818                 var id = this.getId(n);
6819                 for(var j = 0; j < fl; j++){
6820                     f = fi[j];
6821                 var v = this.ef[j](n);
6822                 if (!f.convert) {
6823                     Roo.log('missing convert for ' + f.name);
6824                     Roo.log(f);
6825                     continue;
6826                 }
6827                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6828                 }
6829                 var record = new Record(values, id);
6830                 record.json = n;
6831                 records[i] = record;
6832             }
6833             return {
6834             raw : o,
6835                 success : success,
6836                 records : records,
6837                 totalRecords : totalRecords
6838             };
6839     }
6840 });/*
6841  * Based on:
6842  * Ext JS Library 1.1.1
6843  * Copyright(c) 2006-2007, Ext JS, LLC.
6844  *
6845  * Originally Released Under LGPL - original licence link has changed is not relivant.
6846  *
6847  * Fork - LGPL
6848  * <script type="text/javascript">
6849  */
6850
6851 /**
6852  * @class Roo.data.ArrayReader
6853  * @extends Roo.data.DataReader
6854  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6855  * Each element of that Array represents a row of data fields. The
6856  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6857  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6858  * <p>
6859  * Example code:.
6860  * <pre><code>
6861 var RecordDef = Roo.data.Record.create([
6862     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6863     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6864 ]);
6865 var myReader = new Roo.data.ArrayReader({
6866     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6867 }, RecordDef);
6868 </code></pre>
6869  * <p>
6870  * This would consume an Array like this:
6871  * <pre><code>
6872 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6873   </code></pre>
6874  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6875  * @constructor
6876  * Create a new JsonReader
6877  * @param {Object} meta Metadata configuration options.
6878  * @param {Object} recordType Either an Array of field definition objects
6879  * as specified to {@link Roo.data.Record#create},
6880  * or an {@link Roo.data.Record} object
6881  * created using {@link Roo.data.Record#create}.
6882  */
6883 Roo.data.ArrayReader = function(meta, recordType){
6884     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6885 };
6886
6887 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6888     /**
6889      * Create a data block containing Roo.data.Records from an XML document.
6890      * @param {Object} o An Array of row objects which represents the dataset.
6891      * @return {Object} data A data block which is used by an Roo.data.Store object as
6892      * a cache of Roo.data.Records.
6893      */
6894     readRecords : function(o){
6895         var sid = this.meta ? this.meta.id : null;
6896         var recordType = this.recordType, fields = recordType.prototype.fields;
6897         var records = [];
6898         var root = o;
6899             for(var i = 0; i < root.length; i++){
6900                     var n = root[i];
6901                 var values = {};
6902                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6903                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6904                 var f = fields.items[j];
6905                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6906                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6907                 v = f.convert(v);
6908                 values[f.name] = v;
6909             }
6910                 var record = new recordType(values, id);
6911                 record.json = n;
6912                 records[records.length] = record;
6913             }
6914             return {
6915                 records : records,
6916                 totalRecords : records.length
6917             };
6918     }
6919 });/*
6920  * - LGPL
6921  * * 
6922  */
6923
6924 /**
6925  * @class Roo.bootstrap.ComboBox
6926  * @extends Roo.bootstrap.TriggerField
6927  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
6928  * @constructor
6929  * Create a new ComboBox.
6930  * @param {Object} config Configuration options
6931  */
6932 Roo.bootstrap.ComboBox = function(config){
6933     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
6934     this.addEvents({
6935         /**
6936          * @event expand
6937          * Fires when the dropdown list is expanded
6938              * @param {Roo.bootstrap.ComboBox} combo This combo box
6939              */
6940         'expand' : true,
6941         /**
6942          * @event collapse
6943          * Fires when the dropdown list is collapsed
6944              * @param {Roo.bootstrap.ComboBox} combo This combo box
6945              */
6946         'collapse' : true,
6947         /**
6948          * @event beforeselect
6949          * Fires before a list item is selected. Return false to cancel the selection.
6950              * @param {Roo.bootstrap.ComboBox} combo This combo box
6951              * @param {Roo.data.Record} record The data record returned from the underlying store
6952              * @param {Number} index The index of the selected item in the dropdown list
6953              */
6954         'beforeselect' : true,
6955         /**
6956          * @event select
6957          * Fires when a list item is selected
6958              * @param {Roo.bootstrap.ComboBox} combo This combo box
6959              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
6960              * @param {Number} index The index of the selected item in the dropdown list
6961              */
6962         'select' : true,
6963         /**
6964          * @event beforequery
6965          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
6966          * The event object passed has these properties:
6967              * @param {Roo.bootstrap.ComboBox} combo This combo box
6968              * @param {String} query The query
6969              * @param {Boolean} forceAll true to force "all" query
6970              * @param {Boolean} cancel true to cancel the query
6971              * @param {Object} e The query event object
6972              */
6973         'beforequery': true,
6974          /**
6975          * @event add
6976          * Fires when the 'add' icon is pressed (add a listener to enable add button)
6977              * @param {Roo.bootstrap.ComboBox} combo This combo box
6978              */
6979         'add' : true,
6980         /**
6981          * @event edit
6982          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
6983              * @param {Roo.bootstrap.ComboBox} combo This combo box
6984              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
6985              */
6986         'edit' : true
6987         
6988         
6989     });
6990     
6991     
6992     this.selectedIndex = -1;
6993     if(this.mode == 'local'){
6994         if(config.queryDelay === undefined){
6995             this.queryDelay = 10;
6996         }
6997         if(config.minChars === undefined){
6998             this.minChars = 0;
6999         }
7000     }
7001 };
7002
7003 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7004      
7005     /**
7006      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7007      * rendering into an Roo.Editor, defaults to false)
7008      */
7009     /**
7010      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7011      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7012      */
7013     /**
7014      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7015      */
7016     /**
7017      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7018      * the dropdown list (defaults to undefined, with no header element)
7019      */
7020
7021      /**
7022      * @cfg {String/Roo.Template} tpl The template to use to render the output
7023      */
7024      
7025      /**
7026      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7027      */
7028     listWidth: undefined,
7029     /**
7030      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7031      * mode = 'remote' or 'text' if mode = 'local')
7032      */
7033     displayField: undefined,
7034     /**
7035      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7036      * mode = 'remote' or 'value' if mode = 'local'). 
7037      * Note: use of a valueField requires the user make a selection
7038      * in order for a value to be mapped.
7039      */
7040     valueField: undefined,
7041     
7042     
7043     /**
7044      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7045      * field's data value (defaults to the underlying DOM element's name)
7046      */
7047     hiddenName: undefined,
7048     /**
7049      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7050      */
7051     listClass: '',
7052     /**
7053      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7054      */
7055     selectedClass: 'active',
7056     
7057     /**
7058      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7059      */
7060     shadow:'sides',
7061     /**
7062      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7063      * anchor positions (defaults to 'tl-bl')
7064      */
7065     listAlign: 'tl-bl?',
7066     /**
7067      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7068      */
7069     maxHeight: 300,
7070     /**
7071      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7072      * query specified by the allQuery config option (defaults to 'query')
7073      */
7074     triggerAction: 'query',
7075     /**
7076      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7077      * (defaults to 4, does not apply if editable = false)
7078      */
7079     minChars : 4,
7080     /**
7081      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7082      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7083      */
7084     typeAhead: false,
7085     /**
7086      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7087      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7088      */
7089     queryDelay: 500,
7090     /**
7091      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7092      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7093      */
7094     pageSize: 0,
7095     /**
7096      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7097      * when editable = true (defaults to false)
7098      */
7099     selectOnFocus:false,
7100     /**
7101      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7102      */
7103     queryParam: 'query',
7104     /**
7105      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7106      * when mode = 'remote' (defaults to 'Loading...')
7107      */
7108     loadingText: 'Loading...',
7109     /**
7110      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7111      */
7112     resizable: false,
7113     /**
7114      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7115      */
7116     handleHeight : 8,
7117     /**
7118      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7119      * traditional select (defaults to true)
7120      */
7121     editable: true,
7122     /**
7123      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7124      */
7125     allQuery: '',
7126     /**
7127      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7128      */
7129     mode: 'remote',
7130     /**
7131      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7132      * listWidth has a higher value)
7133      */
7134     minListWidth : 70,
7135     /**
7136      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7137      * allow the user to set arbitrary text into the field (defaults to false)
7138      */
7139     forceSelection:false,
7140     /**
7141      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7142      * if typeAhead = true (defaults to 250)
7143      */
7144     typeAheadDelay : 250,
7145     /**
7146      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7147      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7148      */
7149     valueNotFoundText : undefined,
7150     /**
7151      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7152      */
7153     blockFocus : false,
7154     
7155     /**
7156      * @cfg {Boolean} disableClear Disable showing of clear button.
7157      */
7158     disableClear : false,
7159     /**
7160      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7161      */
7162     alwaysQuery : false,
7163     
7164     //private
7165     addicon : false,
7166     editicon: false,
7167     
7168     // element that contains real text value.. (when hidden is used..)
7169      
7170     // private
7171     initEvents: function(){
7172         
7173         if (!this.store) {
7174             throw "can not find store for combo";
7175         }
7176         this.store = Roo.factory(this.store, Roo.data);
7177         
7178         
7179         
7180         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
7181         
7182         
7183         if(this.hiddenName){
7184             
7185             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
7186             
7187             this.hiddenField.dom.value =
7188                 this.hiddenValue !== undefined ? this.hiddenValue :
7189                 this.value !== undefined ? this.value : '';
7190
7191             // prevent input submission
7192             this.el.dom.removeAttribute('name');
7193             this.hiddenField.dom.setAttribute('name', this.hiddenName);
7194              
7195              
7196         }
7197         //if(Roo.isGecko){
7198         //    this.el.dom.setAttribute('autocomplete', 'off');
7199         //}
7200
7201         var cls = 'x-combo-list';
7202         this.list = this.el.select('ul',true).first();
7203
7204         //this.list = new Roo.Layer({
7205         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
7206         //});
7207         
7208         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
7209         this.list.setWidth(lw);
7210         
7211         this.list.on('mouseover', this.onViewOver, this);
7212         this.list.on('mousemove', this.onViewMove, this);
7213         
7214         /*
7215         this.list.swallowEvent('mousewheel');
7216         this.assetHeight = 0;
7217
7218         if(this.title){
7219             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
7220             this.assetHeight += this.header.getHeight();
7221         }
7222
7223         this.innerList = this.list.createChild({cls:cls+'-inner'});
7224         this.innerList.on('mouseover', this.onViewOver, this);
7225         this.innerList.on('mousemove', this.onViewMove, this);
7226         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
7227         
7228         if(this.allowBlank && !this.pageSize && !this.disableClear){
7229             this.footer = this.list.createChild({cls:cls+'-ft'});
7230             this.pageTb = new Roo.Toolbar(this.footer);
7231            
7232         }
7233         if(this.pageSize){
7234             this.footer = this.list.createChild({cls:cls+'-ft'});
7235             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
7236                     {pageSize: this.pageSize});
7237             
7238         }
7239         
7240         if (this.pageTb && this.allowBlank && !this.disableClear) {
7241             var _this = this;
7242             this.pageTb.add(new Roo.Toolbar.Fill(), {
7243                 cls: 'x-btn-icon x-btn-clear',
7244                 text: '&#160;',
7245                 handler: function()
7246                 {
7247                     _this.collapse();
7248                     _this.clearValue();
7249                     _this.onSelect(false, -1);
7250                 }
7251             });
7252         }
7253         if (this.footer) {
7254             this.assetHeight += this.footer.getHeight();
7255         }
7256         */
7257             
7258         if(!this.tpl){
7259             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
7260         }
7261
7262         this.view = new Roo.View(this.el.select('ul',true).first(), this.tpl, {
7263             singleSelect:true, store: this.store, selectedClass: this.selectedClass
7264         });
7265         //this.view.wrapEl.setDisplayed(false);
7266         this.view.on('click', this.onViewClick, this);
7267         
7268         
7269         
7270         this.store.on('beforeload', this.onBeforeLoad, this);
7271         this.store.on('load', this.onLoad, this);
7272         this.store.on('loadexception', this.onLoadException, this);
7273         /*
7274         if(this.resizable){
7275             this.resizer = new Roo.Resizable(this.list,  {
7276                pinned:true, handles:'se'
7277             });
7278             this.resizer.on('resize', function(r, w, h){
7279                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
7280                 this.listWidth = w;
7281                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
7282                 this.restrictHeight();
7283             }, this);
7284             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
7285         }
7286         */
7287         if(!this.editable){
7288             this.editable = true;
7289             this.setEditable(false);
7290         }
7291         
7292         /*
7293         
7294         if (typeof(this.events.add.listeners) != 'undefined') {
7295             
7296             this.addicon = this.wrap.createChild(
7297                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
7298        
7299             this.addicon.on('click', function(e) {
7300                 this.fireEvent('add', this);
7301             }, this);
7302         }
7303         if (typeof(this.events.edit.listeners) != 'undefined') {
7304             
7305             this.editicon = this.wrap.createChild(
7306                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
7307             if (this.addicon) {
7308                 this.editicon.setStyle('margin-left', '40px');
7309             }
7310             this.editicon.on('click', function(e) {
7311                 
7312                 // we fire even  if inothing is selected..
7313                 this.fireEvent('edit', this, this.lastData );
7314                 
7315             }, this);
7316         }
7317         */
7318         
7319  
7320         this.keyNav = new Roo.KeyNav(this.inputEl(), {
7321             "up" : function(e){
7322                 this.inKeyMode = true;
7323                 this.selectPrev();
7324             },
7325
7326             "down" : function(e){
7327                 if(!this.isExpanded()){
7328                     this.onTriggerClick();
7329                 }else{
7330                     this.inKeyMode = true;
7331                     this.selectNext();
7332                 }
7333             },
7334
7335             "enter" : function(e){
7336                 this.onViewClick();
7337                 //return true;
7338             },
7339
7340             "esc" : function(e){
7341                 this.collapse();
7342             },
7343
7344             "tab" : function(e){
7345                 this.collapse();
7346                 
7347                 if(this.fireEvent("specialkey", this, e)){
7348                     this.onViewClick(false);
7349                 }
7350                 
7351                 return true;
7352             },
7353
7354             scope : this,
7355
7356             doRelay : function(foo, bar, hname){
7357                 if(hname == 'down' || this.scope.isExpanded()){
7358                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
7359                 }
7360                 return true;
7361             },
7362
7363             forceKeyDown: true
7364         });
7365         
7366         
7367         this.queryDelay = Math.max(this.queryDelay || 10,
7368                 this.mode == 'local' ? 10 : 250);
7369         
7370         
7371         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
7372         
7373         if(this.typeAhead){
7374             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
7375         }
7376         if(this.editable !== false){
7377             this.inputEl().on("keyup", this.onKeyUp, this);
7378         }
7379         if(this.forceSelection){
7380             this.on('blur', this.doForce, this);
7381         }
7382     },
7383
7384     onDestroy : function(){
7385         if(this.view){
7386             this.view.setStore(null);
7387             this.view.el.removeAllListeners();
7388             this.view.el.remove();
7389             this.view.purgeListeners();
7390         }
7391         if(this.list){
7392             this.list.dom.innerHTML  = '';
7393         }
7394         if(this.store){
7395             this.store.un('beforeload', this.onBeforeLoad, this);
7396             this.store.un('load', this.onLoad, this);
7397             this.store.un('loadexception', this.onLoadException, this);
7398         }
7399         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
7400     },
7401
7402     // private
7403     fireKey : function(e){
7404         if(e.isNavKeyPress() && !this.list.isVisible()){
7405             this.fireEvent("specialkey", this, e);
7406         }
7407     },
7408
7409     // private
7410     onResize: function(w, h){
7411 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
7412 //        
7413 //        if(typeof w != 'number'){
7414 //            // we do not handle it!?!?
7415 //            return;
7416 //        }
7417 //        var tw = this.trigger.getWidth();
7418 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
7419 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
7420 //        var x = w - tw;
7421 //        this.inputEl().setWidth( this.adjustWidth('input', x));
7422 //            
7423 //        //this.trigger.setStyle('left', x+'px');
7424 //        
7425 //        if(this.list && this.listWidth === undefined){
7426 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
7427 //            this.list.setWidth(lw);
7428 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
7429 //        }
7430         
7431     
7432         
7433     },
7434
7435     /**
7436      * Allow or prevent the user from directly editing the field text.  If false is passed,
7437      * the user will only be able to select from the items defined in the dropdown list.  This method
7438      * is the runtime equivalent of setting the 'editable' config option at config time.
7439      * @param {Boolean} value True to allow the user to directly edit the field text
7440      */
7441     setEditable : function(value){
7442         if(value == this.editable){
7443             return;
7444         }
7445         this.editable = value;
7446         if(!value){
7447             this.inputEl().dom.setAttribute('readOnly', true);
7448             this.inputEl().on('mousedown', this.onTriggerClick,  this);
7449             this.inputEl().addClass('x-combo-noedit');
7450         }else{
7451             this.inputEl().dom.setAttribute('readOnly', false);
7452             this.inputEl().un('mousedown', this.onTriggerClick,  this);
7453             this.inputEl().removeClass('x-combo-noedit');
7454         }
7455     },
7456
7457     // private
7458     onBeforeLoad : function(){
7459         if(!this.hasFocus){
7460             return;
7461         }
7462         //this.innerList.update(this.loadingText ?
7463         //       '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
7464         this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
7465         
7466         this.restrictHeight();
7467         this.selectedIndex = -1;
7468     },
7469
7470     // private
7471     onLoad : function(){
7472         if(!this.hasFocus){
7473             return;
7474         }
7475         if(this.store.getCount() > 0){
7476             this.expand();
7477             this.restrictHeight();
7478             if(this.lastQuery == this.allQuery){
7479                 if(this.editable){
7480                     this.inputEl().dom.select();
7481                 }
7482                 if(!this.selectByValue(this.value, true)){
7483                     this.select(0, true);
7484                 }
7485             }else{
7486                 this.selectNext();
7487                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
7488                     this.taTask.delay(this.typeAheadDelay);
7489                 }
7490             }
7491         }else{
7492             this.onEmptyResults();
7493         }
7494         //this.el.focus();
7495     },
7496     // private
7497     onLoadException : function()
7498     {
7499         this.collapse();
7500         Roo.log(this.store.reader.jsonData);
7501         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7502             // fixme
7503             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7504         }
7505         
7506         
7507     },
7508     // private
7509     onTypeAhead : function(){
7510         if(this.store.getCount() > 0){
7511             var r = this.store.getAt(0);
7512             var newValue = r.data[this.displayField];
7513             var len = newValue.length;
7514             var selStart = this.getRawValue().length;
7515             
7516             if(selStart != len){
7517                 this.setRawValue(newValue);
7518                 this.selectText(selStart, newValue.length);
7519             }
7520         }
7521     },
7522
7523     // private
7524     onSelect : function(record, index){
7525         if(this.fireEvent('beforeselect', this, record, index) !== false){
7526             this.setFromData(index > -1 ? record.data : false);
7527             this.collapse();
7528             this.fireEvent('select', this, record, index);
7529         }
7530     },
7531
7532     /**
7533      * Returns the currently selected field value or empty string if no value is set.
7534      * @return {String} value The selected value
7535      */
7536     getValue : function(){
7537         if(this.valueField){
7538             return typeof this.value != 'undefined' ? this.value : '';
7539         }else{
7540             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
7541         }
7542     },
7543
7544     /**
7545      * Clears any text/value currently set in the field
7546      */
7547     clearValue : function(){
7548         if(this.hiddenField){
7549             this.hiddenField.dom.value = '';
7550         }
7551         this.value = '';
7552         this.setRawValue('');
7553         this.lastSelectionText = '';
7554         
7555     },
7556
7557     /**
7558      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
7559      * will be displayed in the field.  If the value does not match the data value of an existing item,
7560      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
7561      * Otherwise the field will be blank (although the value will still be set).
7562      * @param {String} value The value to match
7563      */
7564     setValue : function(v){
7565         var text = v;
7566         if(this.valueField){
7567             var r = this.findRecord(this.valueField, v);
7568             if(r){
7569                 text = r.data[this.displayField];
7570             }else if(this.valueNotFoundText !== undefined){
7571                 text = this.valueNotFoundText;
7572             }
7573         }
7574         this.lastSelectionText = text;
7575         if(this.hiddenField){
7576             this.hiddenField.dom.value = v;
7577         }
7578         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
7579         this.value = v;
7580     },
7581     /**
7582      * @property {Object} the last set data for the element
7583      */
7584     
7585     lastData : false,
7586     /**
7587      * Sets the value of the field based on a object which is related to the record format for the store.
7588      * @param {Object} value the value to set as. or false on reset?
7589      */
7590     setFromData : function(o){
7591         var dv = ''; // display value
7592         var vv = ''; // value value..
7593         this.lastData = o;
7594         if (this.displayField) {
7595             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
7596         } else {
7597             // this is an error condition!!!
7598             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
7599         }
7600         
7601         if(this.valueField){
7602             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
7603         }
7604         if(this.hiddenField){
7605             this.hiddenField.dom.value = vv;
7606             
7607             this.lastSelectionText = dv;
7608             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
7609             this.value = vv;
7610             return;
7611         }
7612         // no hidden field.. - we store the value in 'value', but still display
7613         // display field!!!!
7614         this.lastSelectionText = dv;
7615         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
7616         this.value = vv;
7617         
7618         
7619     },
7620     // private
7621     reset : function(){
7622         // overridden so that last data is reset..
7623         this.setValue(this.originalValue);
7624         this.clearInvalid();
7625         this.lastData = false;
7626         if (this.view) {
7627             this.view.clearSelections();
7628         }
7629     },
7630     // private
7631     findRecord : function(prop, value){
7632         var record;
7633         if(this.store.getCount() > 0){
7634             this.store.each(function(r){
7635                 if(r.data[prop] == value){
7636                     record = r;
7637                     return false;
7638                 }
7639                 return true;
7640             });
7641         }
7642         return record;
7643     },
7644     
7645     getName: function()
7646     {
7647         // returns hidden if it's set..
7648         if (!this.rendered) {return ''};
7649         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
7650         
7651     },
7652     // private
7653     onViewMove : function(e, t){
7654         this.inKeyMode = false;
7655     },
7656
7657     // private
7658     onViewOver : function(e, t){
7659         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
7660             return;
7661         }
7662         var item = this.view.findItemFromChild(t);
7663         if(item){
7664             var index = this.view.indexOf(item);
7665             this.select(index, false);
7666         }
7667     },
7668
7669     // private
7670     onViewClick : function(doFocus)
7671     {
7672         var index = this.view.getSelectedIndexes()[0];
7673         var r = this.store.getAt(index);
7674         if(r){
7675             this.onSelect(r, index);
7676         }
7677         if(doFocus !== false && !this.blockFocus){
7678             this.inputEl().focus();
7679         }
7680     },
7681
7682     // private
7683     restrictHeight : function(){
7684         //this.innerList.dom.style.height = '';
7685         //var inner = this.innerList.dom;
7686         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
7687         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
7688         //this.list.beginUpdate();
7689         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
7690         this.list.alignTo(this.inputEl(), this.listAlign);
7691         //this.list.endUpdate();
7692     },
7693
7694     // private
7695     onEmptyResults : function(){
7696         this.collapse();
7697     },
7698
7699     /**
7700      * Returns true if the dropdown list is expanded, else false.
7701      */
7702     isExpanded : function(){
7703         return this.list.isVisible();
7704     },
7705
7706     /**
7707      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
7708      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
7709      * @param {String} value The data value of the item to select
7710      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
7711      * selected item if it is not currently in view (defaults to true)
7712      * @return {Boolean} True if the value matched an item in the list, else false
7713      */
7714     selectByValue : function(v, scrollIntoView){
7715         if(v !== undefined && v !== null){
7716             var r = this.findRecord(this.valueField || this.displayField, v);
7717             if(r){
7718                 this.select(this.store.indexOf(r), scrollIntoView);
7719                 return true;
7720             }
7721         }
7722         return false;
7723     },
7724
7725     /**
7726      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
7727      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
7728      * @param {Number} index The zero-based index of the list item to select
7729      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
7730      * selected item if it is not currently in view (defaults to true)
7731      */
7732     select : function(index, scrollIntoView){
7733         this.selectedIndex = index;
7734         this.view.select(index);
7735         if(scrollIntoView !== false){
7736             var el = this.view.getNode(index);
7737             if(el){
7738                 //this.innerList.scrollChildIntoView(el, false);
7739                 
7740             }
7741         }
7742     },
7743
7744     // private
7745     selectNext : function(){
7746         var ct = this.store.getCount();
7747         if(ct > 0){
7748             if(this.selectedIndex == -1){
7749                 this.select(0);
7750             }else if(this.selectedIndex < ct-1){
7751                 this.select(this.selectedIndex+1);
7752             }
7753         }
7754     },
7755
7756     // private
7757     selectPrev : function(){
7758         var ct = this.store.getCount();
7759         if(ct > 0){
7760             if(this.selectedIndex == -1){
7761                 this.select(0);
7762             }else if(this.selectedIndex != 0){
7763                 this.select(this.selectedIndex-1);
7764             }
7765         }
7766     },
7767
7768     // private
7769     onKeyUp : function(e){
7770         if(this.editable !== false && !e.isSpecialKey()){
7771             this.lastKey = e.getKey();
7772             this.dqTask.delay(this.queryDelay);
7773         }
7774     },
7775
7776     // private
7777     validateBlur : function(){
7778         return !this.list || !this.list.isVisible();   
7779     },
7780
7781     // private
7782     initQuery : function(){
7783         this.doQuery(this.getRawValue());
7784     },
7785
7786     // private
7787     doForce : function(){
7788         if(this.el.dom.value.length > 0){
7789             this.el.dom.value =
7790                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
7791              
7792         }
7793     },
7794
7795     /**
7796      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
7797      * query allowing the query action to be canceled if needed.
7798      * @param {String} query The SQL query to execute
7799      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
7800      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
7801      * saved in the current store (defaults to false)
7802      */
7803     doQuery : function(q, forceAll){
7804         if(q === undefined || q === null){
7805             q = '';
7806         }
7807         var qe = {
7808             query: q,
7809             forceAll: forceAll,
7810             combo: this,
7811             cancel:false
7812         };
7813         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
7814             return false;
7815         }
7816         q = qe.query;
7817         forceAll = qe.forceAll;
7818         if(forceAll === true || (q.length >= this.minChars)){
7819             if(this.lastQuery != q || this.alwaysQuery){
7820                 this.lastQuery = q;
7821                 if(this.mode == 'local'){
7822                     this.selectedIndex = -1;
7823                     if(forceAll){
7824                         this.store.clearFilter();
7825                     }else{
7826                         this.store.filter(this.displayField, q);
7827                     }
7828                     this.onLoad();
7829                 }else{
7830                     this.store.baseParams[this.queryParam] = q;
7831                     this.store.load({
7832                         params: this.getParams(q)
7833                     });
7834                     this.expand();
7835                 }
7836             }else{
7837                 this.selectedIndex = -1;
7838                 this.onLoad();   
7839             }
7840         }
7841     },
7842
7843     // private
7844     getParams : function(q){
7845         var p = {};
7846         //p[this.queryParam] = q;
7847         if(this.pageSize){
7848             p.start = 0;
7849             p.limit = this.pageSize;
7850         }
7851         return p;
7852     },
7853
7854     /**
7855      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
7856      */
7857     collapse : function(){
7858         if(!this.isExpanded()){
7859             return;
7860         }
7861         this.list.hide();
7862         Roo.get(document).un('mousedown', this.collapseIf, this);
7863         Roo.get(document).un('mousewheel', this.collapseIf, this);
7864         if (!this.editable) {
7865             Roo.get(document).un('keydown', this.listKeyPress, this);
7866         }
7867         this.fireEvent('collapse', this);
7868     },
7869
7870     // private
7871     collapseIf : function(e){
7872         if(!e.within(this.el) && !e.within(this.el)){
7873             this.collapse();
7874         }
7875     },
7876
7877     /**
7878      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
7879      */
7880     expand : function(){
7881         Roo.log('expand');
7882         if(this.isExpanded() || !this.hasFocus){
7883             return;
7884         }
7885         this.list.alignTo(this.inputEl(), this.listAlign);
7886         this.list.show();
7887         Roo.get(document).on('mousedown', this.collapseIf, this);
7888         Roo.get(document).on('mousewheel', this.collapseIf, this);
7889         if (!this.editable) {
7890             Roo.get(document).on('keydown', this.listKeyPress, this);
7891         }
7892         
7893         this.fireEvent('expand', this);
7894     },
7895
7896     // private
7897     // Implements the default empty TriggerField.onTriggerClick function
7898     onTriggerClick : function()
7899     {
7900         Roo.log('trigger click');
7901         
7902         if(this.disabled){
7903             return;
7904         }
7905         if(this.isExpanded()){
7906             this.collapse();
7907             if (!this.blockFocus) {
7908                 this.inputEl().focus();
7909             }
7910             
7911         }else {
7912             this.hasFocus = true;
7913             if(this.triggerAction == 'all') {
7914                 this.doQuery(this.allQuery, true);
7915             } else {
7916                 this.doQuery(this.getRawValue());
7917             }
7918             if (!this.blockFocus) {
7919                 this.inputEl().focus();
7920             }
7921         }
7922     },
7923     listKeyPress : function(e)
7924     {
7925         //Roo.log('listkeypress');
7926         // scroll to first matching element based on key pres..
7927         if (e.isSpecialKey()) {
7928             return false;
7929         }
7930         var k = String.fromCharCode(e.getKey()).toUpperCase();
7931         //Roo.log(k);
7932         var match  = false;
7933         var csel = this.view.getSelectedNodes();
7934         var cselitem = false;
7935         if (csel.length) {
7936             var ix = this.view.indexOf(csel[0]);
7937             cselitem  = this.store.getAt(ix);
7938             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
7939                 cselitem = false;
7940             }
7941             
7942         }
7943         
7944         this.store.each(function(v) { 
7945             if (cselitem) {
7946                 // start at existing selection.
7947                 if (cselitem.id == v.id) {
7948                     cselitem = false;
7949                 }
7950                 return true;
7951             }
7952                 
7953             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
7954                 match = this.store.indexOf(v);
7955                 return false;
7956             }
7957             return true;
7958         }, this);
7959         
7960         if (match === false) {
7961             return true; // no more action?
7962         }
7963         // scroll to?
7964         this.view.select(match);
7965         var sn = Roo.get(this.view.getSelectedNodes()[0])
7966         //sn.scrollIntoView(sn.dom.parentNode, false);
7967     }
7968
7969     /** 
7970     * @cfg {Boolean} grow 
7971     * @hide 
7972     */
7973     /** 
7974     * @cfg {Number} growMin 
7975     * @hide 
7976     */
7977     /** 
7978     * @cfg {Number} growMax 
7979     * @hide 
7980     */
7981     /**
7982      * @hide
7983      * @method autoSize
7984      */
7985 });/*
7986  * Based on:
7987  * Ext JS Library 1.1.1
7988  * Copyright(c) 2006-2007, Ext JS, LLC.
7989  *
7990  * Originally Released Under LGPL - original licence link has changed is not relivant.
7991  *
7992  * Fork - LGPL
7993  * <script type="text/javascript">
7994  */
7995
7996 /**
7997  * @class Roo.View
7998  * @extends Roo.util.Observable
7999  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8000  * This class also supports single and multi selection modes. <br>
8001  * Create a data model bound view:
8002  <pre><code>
8003  var store = new Roo.data.Store(...);
8004
8005  var view = new Roo.View({
8006     el : "my-element",
8007     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8008  
8009     singleSelect: true,
8010     selectedClass: "ydataview-selected",
8011     store: store
8012  });
8013
8014  // listen for node click?
8015  view.on("click", function(vw, index, node, e){
8016  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8017  });
8018
8019  // load XML data
8020  dataModel.load("foobar.xml");
8021  </code></pre>
8022  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8023  * <br><br>
8024  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8025  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8026  * 
8027  * Note: old style constructor is still suported (container, template, config)
8028  * 
8029  * @constructor
8030  * Create a new View
8031  * @param {Object} config The config object
8032  * 
8033  */
8034 Roo.View = function(config, depreciated_tpl, depreciated_config){
8035     
8036     if (typeof(depreciated_tpl) == 'undefined') {
8037         // new way.. - universal constructor.
8038         Roo.apply(this, config);
8039         this.el  = Roo.get(this.el);
8040     } else {
8041         // old format..
8042         this.el  = Roo.get(config);
8043         this.tpl = depreciated_tpl;
8044         Roo.apply(this, depreciated_config);
8045     }
8046     this.wrapEl  = this.el.wrap().wrap();
8047     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8048     
8049     
8050     if(typeof(this.tpl) == "string"){
8051         this.tpl = new Roo.Template(this.tpl);
8052     } else {
8053         // support xtype ctors..
8054         this.tpl = new Roo.factory(this.tpl, Roo);
8055     }
8056     
8057     
8058     this.tpl.compile();
8059    
8060   
8061     
8062      
8063     /** @private */
8064     this.addEvents({
8065         /**
8066          * @event beforeclick
8067          * Fires before a click is processed. Returns false to cancel the default action.
8068          * @param {Roo.View} this
8069          * @param {Number} index The index of the target node
8070          * @param {HTMLElement} node The target node
8071          * @param {Roo.EventObject} e The raw event object
8072          */
8073             "beforeclick" : true,
8074         /**
8075          * @event click
8076          * Fires when a template node is clicked.
8077          * @param {Roo.View} this
8078          * @param {Number} index The index of the target node
8079          * @param {HTMLElement} node The target node
8080          * @param {Roo.EventObject} e The raw event object
8081          */
8082             "click" : true,
8083         /**
8084          * @event dblclick
8085          * Fires when a template node is double clicked.
8086          * @param {Roo.View} this
8087          * @param {Number} index The index of the target node
8088          * @param {HTMLElement} node The target node
8089          * @param {Roo.EventObject} e The raw event object
8090          */
8091             "dblclick" : true,
8092         /**
8093          * @event contextmenu
8094          * Fires when a template node is right clicked.
8095          * @param {Roo.View} this
8096          * @param {Number} index The index of the target node
8097          * @param {HTMLElement} node The target node
8098          * @param {Roo.EventObject} e The raw event object
8099          */
8100             "contextmenu" : true,
8101         /**
8102          * @event selectionchange
8103          * Fires when the selected nodes change.
8104          * @param {Roo.View} this
8105          * @param {Array} selections Array of the selected nodes
8106          */
8107             "selectionchange" : true,
8108     
8109         /**
8110          * @event beforeselect
8111          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8112          * @param {Roo.View} this
8113          * @param {HTMLElement} node The node to be selected
8114          * @param {Array} selections Array of currently selected nodes
8115          */
8116             "beforeselect" : true,
8117         /**
8118          * @event preparedata
8119          * Fires on every row to render, to allow you to change the data.
8120          * @param {Roo.View} this
8121          * @param {Object} data to be rendered (change this)
8122          */
8123           "preparedata" : true
8124           
8125           
8126         });
8127
8128
8129
8130     this.el.on({
8131         "click": this.onClick,
8132         "dblclick": this.onDblClick,
8133         "contextmenu": this.onContextMenu,
8134         scope:this
8135     });
8136
8137     this.selections = [];
8138     this.nodes = [];
8139     this.cmp = new Roo.CompositeElementLite([]);
8140     if(this.store){
8141         this.store = Roo.factory(this.store, Roo.data);
8142         this.setStore(this.store, true);
8143     }
8144     
8145     if ( this.footer && this.footer.xtype) {
8146            
8147          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8148         
8149         this.footer.dataSource = this.store
8150         this.footer.container = fctr;
8151         this.footer = Roo.factory(this.footer, Roo);
8152         fctr.insertFirst(this.el);
8153         
8154         // this is a bit insane - as the paging toolbar seems to detach the el..
8155 //        dom.parentNode.parentNode.parentNode
8156          // they get detached?
8157     }
8158     
8159     
8160     Roo.View.superclass.constructor.call(this);
8161     
8162     
8163 };
8164
8165 Roo.extend(Roo.View, Roo.util.Observable, {
8166     
8167      /**
8168      * @cfg {Roo.data.Store} store Data store to load data from.
8169      */
8170     store : false,
8171     
8172     /**
8173      * @cfg {String|Roo.Element} el The container element.
8174      */
8175     el : '',
8176     
8177     /**
8178      * @cfg {String|Roo.Template} tpl The template used by this View 
8179      */
8180     tpl : false,
8181     /**
8182      * @cfg {String} dataName the named area of the template to use as the data area
8183      *                          Works with domtemplates roo-name="name"
8184      */
8185     dataName: false,
8186     /**
8187      * @cfg {String} selectedClass The css class to add to selected nodes
8188      */
8189     selectedClass : "x-view-selected",
8190      /**
8191      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8192      */
8193     emptyText : "",
8194     
8195     /**
8196      * @cfg {String} text to display on mask (default Loading)
8197      */
8198     mask : false,
8199     /**
8200      * @cfg {Boolean} multiSelect Allow multiple selection
8201      */
8202     multiSelect : false,
8203     /**
8204      * @cfg {Boolean} singleSelect Allow single selection
8205      */
8206     singleSelect:  false,
8207     
8208     /**
8209      * @cfg {Boolean} toggleSelect - selecting 
8210      */
8211     toggleSelect : false,
8212     
8213     /**
8214      * Returns the element this view is bound to.
8215      * @return {Roo.Element}
8216      */
8217     getEl : function(){
8218         return this.wrapEl;
8219     },
8220     
8221     
8222
8223     /**
8224      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8225      */
8226     refresh : function(){
8227         var t = this.tpl;
8228         
8229         // if we are using something like 'domtemplate', then
8230         // the what gets used is:
8231         // t.applySubtemplate(NAME, data, wrapping data..)
8232         // the outer template then get' applied with
8233         //     the store 'extra data'
8234         // and the body get's added to the
8235         //      roo-name="data" node?
8236         //      <span class='roo-tpl-{name}'></span> ?????
8237         
8238         
8239         
8240         this.clearSelections();
8241         this.el.update("");
8242         var html = [];
8243         var records = this.store.getRange();
8244         if(records.length < 1) {
8245             
8246             // is this valid??  = should it render a template??
8247             
8248             this.el.update(this.emptyText);
8249             return;
8250         }
8251         var el = this.el;
8252         if (this.dataName) {
8253             this.el.update(t.apply(this.store.meta)); //????
8254             el = this.el.child('.roo-tpl-' + this.dataName);
8255         }
8256         
8257         for(var i = 0, len = records.length; i < len; i++){
8258             var data = this.prepareData(records[i].data, i, records[i]);
8259             this.fireEvent("preparedata", this, data, i, records[i]);
8260             html[html.length] = Roo.util.Format.trim(
8261                 this.dataName ?
8262                     t.applySubtemplate(this.dataName, data, this.store.meta) :
8263                     t.apply(data)
8264             );
8265         }
8266         
8267         
8268         
8269         el.update(html.join(""));
8270         this.nodes = el.dom.childNodes;
8271         this.updateIndexes(0);
8272     },
8273
8274     /**
8275      * Function to override to reformat the data that is sent to
8276      * the template for each node.
8277      * DEPRICATED - use the preparedata event handler.
8278      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8279      * a JSON object for an UpdateManager bound view).
8280      */
8281     prepareData : function(data, index, record)
8282     {
8283         this.fireEvent("preparedata", this, data, index, record);
8284         return data;
8285     },
8286
8287     onUpdate : function(ds, record){
8288         this.clearSelections();
8289         var index = this.store.indexOf(record);
8290         var n = this.nodes[index];
8291         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8292         n.parentNode.removeChild(n);
8293         this.updateIndexes(index, index);
8294     },
8295
8296     
8297     
8298 // --------- FIXME     
8299     onAdd : function(ds, records, index)
8300     {
8301         this.clearSelections();
8302         if(this.nodes.length == 0){
8303             this.refresh();
8304             return;
8305         }
8306         var n = this.nodes[index];
8307         for(var i = 0, len = records.length; i < len; i++){
8308             var d = this.prepareData(records[i].data, i, records[i]);
8309             if(n){
8310                 this.tpl.insertBefore(n, d);
8311             }else{
8312                 
8313                 this.tpl.append(this.el, d);
8314             }
8315         }
8316         this.updateIndexes(index);
8317     },
8318
8319     onRemove : function(ds, record, index){
8320         this.clearSelections();
8321         var el = this.dataName  ?
8322             this.el.child('.roo-tpl-' + this.dataName) :
8323             this.el; 
8324         el.dom.removeChild(this.nodes[index]);
8325         this.updateIndexes(index);
8326     },
8327
8328     /**
8329      * Refresh an individual node.
8330      * @param {Number} index
8331      */
8332     refreshNode : function(index){
8333         this.onUpdate(this.store, this.store.getAt(index));
8334     },
8335
8336     updateIndexes : function(startIndex, endIndex){
8337         var ns = this.nodes;
8338         startIndex = startIndex || 0;
8339         endIndex = endIndex || ns.length - 1;
8340         for(var i = startIndex; i <= endIndex; i++){
8341             ns[i].nodeIndex = i;
8342         }
8343     },
8344
8345     /**
8346      * Changes the data store this view uses and refresh the view.
8347      * @param {Store} store
8348      */
8349     setStore : function(store, initial){
8350         if(!initial && this.store){
8351             this.store.un("datachanged", this.refresh);
8352             this.store.un("add", this.onAdd);
8353             this.store.un("remove", this.onRemove);
8354             this.store.un("update", this.onUpdate);
8355             this.store.un("clear", this.refresh);
8356             this.store.un("beforeload", this.onBeforeLoad);
8357             this.store.un("load", this.onLoad);
8358             this.store.un("loadexception", this.onLoad);
8359         }
8360         if(store){
8361           
8362             store.on("datachanged", this.refresh, this);
8363             store.on("add", this.onAdd, this);
8364             store.on("remove", this.onRemove, this);
8365             store.on("update", this.onUpdate, this);
8366             store.on("clear", this.refresh, this);
8367             store.on("beforeload", this.onBeforeLoad, this);
8368             store.on("load", this.onLoad, this);
8369             store.on("loadexception", this.onLoad, this);
8370         }
8371         
8372         if(store){
8373             this.refresh();
8374         }
8375     },
8376     /**
8377      * onbeforeLoad - masks the loading area.
8378      *
8379      */
8380     onBeforeLoad : function()
8381     {
8382         this.el.update("");
8383         this.el.mask(this.mask ? this.mask : "Loading" ); 
8384     },
8385     onLoad : function ()
8386     {
8387         this.el.unmask();
8388     },
8389     
8390
8391     /**
8392      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8393      * @param {HTMLElement} node
8394      * @return {HTMLElement} The template node
8395      */
8396     findItemFromChild : function(node){
8397         var el = this.dataName  ?
8398             this.el.child('.roo-tpl-' + this.dataName,true) :
8399             this.el.dom; 
8400         
8401         if(!node || node.parentNode == el){
8402                     return node;
8403             }
8404             var p = node.parentNode;
8405             while(p && p != el){
8406             if(p.parentNode == el){
8407                 return p;
8408             }
8409             p = p.parentNode;
8410         }
8411             return null;
8412     },
8413
8414     /** @ignore */
8415     onClick : function(e){
8416         var item = this.findItemFromChild(e.getTarget());
8417         if(item){
8418             var index = this.indexOf(item);
8419             if(this.onItemClick(item, index, e) !== false){
8420                 this.fireEvent("click", this, index, item, e);
8421             }
8422         }else{
8423             this.clearSelections();
8424         }
8425     },
8426
8427     /** @ignore */
8428     onContextMenu : function(e){
8429         var item = this.findItemFromChild(e.getTarget());
8430         if(item){
8431             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8432         }
8433     },
8434
8435     /** @ignore */
8436     onDblClick : function(e){
8437         var item = this.findItemFromChild(e.getTarget());
8438         if(item){
8439             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8440         }
8441     },
8442
8443     onItemClick : function(item, index, e)
8444     {
8445         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8446             return false;
8447         }
8448         if (this.toggleSelect) {
8449             var m = this.isSelected(item) ? 'unselect' : 'select';
8450             Roo.log(m);
8451             var _t = this;
8452             _t[m](item, true, false);
8453             return true;
8454         }
8455         if(this.multiSelect || this.singleSelect){
8456             if(this.multiSelect && e.shiftKey && this.lastSelection){
8457                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8458             }else{
8459                 this.select(item, this.multiSelect && e.ctrlKey);
8460                 this.lastSelection = item;
8461             }
8462             e.preventDefault();
8463         }
8464         return true;
8465     },
8466
8467     /**
8468      * Get the number of selected nodes.
8469      * @return {Number}
8470      */
8471     getSelectionCount : function(){
8472         return this.selections.length;
8473     },
8474
8475     /**
8476      * Get the currently selected nodes.
8477      * @return {Array} An array of HTMLElements
8478      */
8479     getSelectedNodes : function(){
8480         return this.selections;
8481     },
8482
8483     /**
8484      * Get the indexes of the selected nodes.
8485      * @return {Array}
8486      */
8487     getSelectedIndexes : function(){
8488         var indexes = [], s = this.selections;
8489         for(var i = 0, len = s.length; i < len; i++){
8490             indexes.push(s[i].nodeIndex);
8491         }
8492         return indexes;
8493     },
8494
8495     /**
8496      * Clear all selections
8497      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8498      */
8499     clearSelections : function(suppressEvent){
8500         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8501             this.cmp.elements = this.selections;
8502             this.cmp.removeClass(this.selectedClass);
8503             this.selections = [];
8504             if(!suppressEvent){
8505                 this.fireEvent("selectionchange", this, this.selections);
8506             }
8507         }
8508     },
8509
8510     /**
8511      * Returns true if the passed node is selected
8512      * @param {HTMLElement/Number} node The node or node index
8513      * @return {Boolean}
8514      */
8515     isSelected : function(node){
8516         var s = this.selections;
8517         if(s.length < 1){
8518             return false;
8519         }
8520         node = this.getNode(node);
8521         return s.indexOf(node) !== -1;
8522     },
8523
8524     /**
8525      * Selects nodes.
8526      * @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
8527      * @param {Boolean} keepExisting (optional) true to keep existing selections
8528      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8529      */
8530     select : function(nodeInfo, keepExisting, suppressEvent){
8531         if(nodeInfo instanceof Array){
8532             if(!keepExisting){
8533                 this.clearSelections(true);
8534             }
8535             for(var i = 0, len = nodeInfo.length; i < len; i++){
8536                 this.select(nodeInfo[i], true, true);
8537             }
8538             return;
8539         } 
8540         var node = this.getNode(nodeInfo);
8541         if(!node || this.isSelected(node)){
8542             return; // already selected.
8543         }
8544         if(!keepExisting){
8545             this.clearSelections(true);
8546         }
8547         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8548             Roo.fly(node).addClass(this.selectedClass);
8549             this.selections.push(node);
8550             if(!suppressEvent){
8551                 this.fireEvent("selectionchange", this, this.selections);
8552             }
8553         }
8554         
8555         
8556     },
8557       /**
8558      * Unselects nodes.
8559      * @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
8560      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8561      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8562      */
8563     unselect : function(nodeInfo, keepExisting, suppressEvent)
8564     {
8565         if(nodeInfo instanceof Array){
8566             Roo.each(this.selections, function(s) {
8567                 this.unselect(s, nodeInfo);
8568             }, this);
8569             return;
8570         }
8571         var node = this.getNode(nodeInfo);
8572         if(!node || !this.isSelected(node)){
8573             Roo.log("not selected");
8574             return; // not selected.
8575         }
8576         // fireevent???
8577         var ns = [];
8578         Roo.each(this.selections, function(s) {
8579             if (s == node ) {
8580                 Roo.fly(node).removeClass(this.selectedClass);
8581
8582                 return;
8583             }
8584             ns.push(s);
8585         },this);
8586         
8587         this.selections= ns;
8588         this.fireEvent("selectionchange", this, this.selections);
8589     },
8590
8591     /**
8592      * Gets a template node.
8593      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
8594      * @return {HTMLElement} The node or null if it wasn't found
8595      */
8596     getNode : function(nodeInfo){
8597         if(typeof nodeInfo == "string"){
8598             return document.getElementById(nodeInfo);
8599         }else if(typeof nodeInfo == "number"){
8600             return this.nodes[nodeInfo];
8601         }
8602         return nodeInfo;
8603     },
8604
8605     /**
8606      * Gets a range template nodes.
8607      * @param {Number} startIndex
8608      * @param {Number} endIndex
8609      * @return {Array} An array of nodes
8610      */
8611     getNodes : function(start, end){
8612         var ns = this.nodes;
8613         start = start || 0;
8614         end = typeof end == "undefined" ? ns.length - 1 : end;
8615         var nodes = [];
8616         if(start <= end){
8617             for(var i = start; i <= end; i++){
8618                 nodes.push(ns[i]);
8619             }
8620         } else{
8621             for(var i = start; i >= end; i--){
8622                 nodes.push(ns[i]);
8623             }
8624         }
8625         return nodes;
8626     },
8627
8628     /**
8629      * Finds the index of the passed node
8630      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
8631      * @return {Number} The index of the node or -1
8632      */
8633     indexOf : function(node){
8634         node = this.getNode(node);
8635         if(typeof node.nodeIndex == "number"){
8636             return node.nodeIndex;
8637         }
8638         var ns = this.nodes;
8639         for(var i = 0, len = ns.length; i < len; i++){
8640             if(ns[i] == node){
8641                 return i;
8642             }
8643         }
8644         return -1;
8645     }
8646 });
8647 /*
8648  * - LGPL
8649  *
8650  * based on jquery fullcalendar
8651  * 
8652  */
8653
8654 Roo.bootstrap = Roo.bootstrap || {};
8655 /**
8656  * @class Roo.bootstrap.Calendar
8657  * @extends Roo.bootstrap.Component
8658  * Bootstrap Calendar class
8659     
8660  * @constructor
8661  * Create a new Container
8662  * @param {Object} config The config object
8663  */
8664
8665
8666
8667 Roo.bootstrap.Calendar = function(config){
8668     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
8669      this.addEvents({
8670         /**
8671              * @event select
8672              * Fires when a date is selected
8673              * @param {DatePicker} this
8674              * @param {Date} date The selected date
8675              */
8676         'select': true,
8677         /**
8678              * @event monthchange
8679              * Fires when the displayed month changes 
8680              * @param {DatePicker} this
8681              * @param {Date} date The selected month
8682              */
8683         'monthchange': true,
8684         /**
8685              * @event evententer
8686              * Fires when mouse over an event
8687              * @param {Calendar} this
8688              * @param {event} Event
8689              */
8690         'evententer': true,
8691         /**
8692              * @event eventleave
8693              * Fires when the mouse leaves an
8694              * @param {Calendar} this
8695              * @param {event}
8696              */
8697         'eventleave': true,
8698         /**
8699              * @event eventclick
8700              * Fires when the mouse click an
8701              * @param {Calendar} this
8702              * @param {event}
8703              */
8704         'eventclick': true
8705         
8706     });
8707
8708 };
8709
8710 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
8711     
8712      /**
8713      * @cfg {Number} startDay
8714      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
8715      */
8716     startDay : 0,
8717       
8718     getAutoCreate : function(){
8719         
8720         
8721         var fc_button = function(name, corner, style, content ) {
8722             return Roo.apply({},{
8723                 tag : 'span',
8724                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
8725                          (corner.length ?
8726                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
8727                             ''
8728                         ),
8729                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
8730                 unselectable: 'on'
8731             });
8732         };
8733         
8734         var header = {
8735             tag : 'table',
8736             cls : 'fc-header',
8737             style : 'width:100%',
8738             cn : [
8739                 {
8740                     tag: 'tr',
8741                     cn : [
8742                         {
8743                             tag : 'td',
8744                             cls : 'fc-header-left',
8745                             cn : [
8746                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
8747                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
8748                                 { tag: 'span', cls: 'fc-header-space' },
8749                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
8750                                 
8751                                 
8752                             ]
8753                         },
8754                         
8755                         {
8756                             tag : 'td',
8757                             cls : 'fc-header-center',
8758                             cn : [
8759                                 {
8760                                     tag: 'span',
8761                                     cls: 'fc-header-title',
8762                                     cn : {
8763                                         tag: 'H2',
8764                                         html : 'month / year'
8765                                     }
8766                                 }
8767                                 
8768                             ]
8769                         },
8770                         {
8771                             tag : 'td',
8772                             cls : 'fc-header-right',
8773                             cn : [
8774                           /*      fc_button('month', 'left', '', 'month' ),
8775                                 fc_button('week', '', '', 'week' ),
8776                                 fc_button('day', 'right', '', 'day' )
8777                             */    
8778                                 
8779                             ]
8780                         }
8781                         
8782                     ]
8783                 }
8784             ]
8785         };
8786         
8787        
8788         var cal_heads = function() {
8789             var ret = [];
8790             // fixme - handle this.
8791             
8792             for (var i =0; i < Date.dayNames.length; i++) {
8793                 var d = Date.dayNames[i];
8794                 ret.push({
8795                     tag: 'th',
8796                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
8797                     html : d.substring(0,3)
8798                 });
8799                 
8800             }
8801             ret[0].cls += ' fc-first';
8802             ret[6].cls += ' fc-last';
8803             return ret;
8804         };
8805         var cal_cell = function(n) {
8806             return  {
8807                 tag: 'td',
8808                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
8809                 cn : [
8810                     {
8811                         cn : [
8812                             {
8813                                 cls: 'fc-day-number',
8814                                 html: 'D'
8815                             },
8816                             {
8817                                 cls: 'fc-day-content',
8818                              
8819                                 cn : [
8820                                      {
8821                                         style: 'position: relative;' // height: 17px;
8822                                     }
8823                                 ]
8824                             }
8825                             
8826                             
8827                         ]
8828                     }
8829                 ]
8830                 
8831             }
8832         };
8833         var cal_rows = function() {
8834             
8835             var ret = []
8836             for (var r = 0; r < 6; r++) {
8837                 var row= {
8838                     tag : 'tr',
8839                     cls : 'fc-week',
8840                     cn : []
8841                 };
8842                 
8843                 for (var i =0; i < Date.dayNames.length; i++) {
8844                     var d = Date.dayNames[i];
8845                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
8846
8847                 }
8848                 row.cn[0].cls+=' fc-first';
8849                 row.cn[0].cn[0].style = 'min-height:90px';
8850                 row.cn[6].cls+=' fc-last';
8851                 ret.push(row);
8852                 
8853             }
8854             ret[0].cls += ' fc-first';
8855             ret[4].cls += ' fc-prev-last';
8856             ret[5].cls += ' fc-last';
8857             return ret;
8858             
8859         };
8860         
8861         var cal_table = {
8862             tag: 'table',
8863             cls: 'fc-border-separate',
8864             style : 'width:100%',
8865             cellspacing  : 0,
8866             cn : [
8867                 { 
8868                     tag: 'thead',
8869                     cn : [
8870                         { 
8871                             tag: 'tr',
8872                             cls : 'fc-first fc-last',
8873                             cn : cal_heads()
8874                         }
8875                     ]
8876                 },
8877                 { 
8878                     tag: 'tbody',
8879                     cn : cal_rows()
8880                 }
8881                   
8882             ]
8883         };
8884          
8885          var cfg = {
8886             cls : 'fc fc-ltr',
8887             cn : [
8888                 header,
8889                 {
8890                     cls : 'fc-content',
8891                     style : "position: relative;",
8892                     cn : [
8893                         {
8894                             cls : 'fc-view fc-view-month fc-grid',
8895                             style : 'position: relative',
8896                             unselectable : 'on',
8897                             cn : [
8898                                 {
8899                                     cls : 'fc-event-container',
8900                                     style : 'position:absolute;z-index:8;top:0;left:0;'
8901                                 },
8902                                 cal_table
8903                             ]
8904                         }
8905                     ]
8906     
8907                 }
8908            ] 
8909             
8910         };
8911         
8912          
8913         
8914         return cfg;
8915     },
8916     
8917     
8918     initEvents : function()
8919     {
8920         if(!this.store){
8921             throw "can not find store for calendar";
8922         }
8923         
8924         this.maskEl = Roo.DomHelper.append(this.el, {tag: "div", cls:"x-dlg-mask"}, true);
8925         this.maskEl.enableDisplayMode("block");
8926         this.maskEl.hide();
8927         
8928         this.store = Roo.factory(this.store, Roo.data);
8929         this.store.on('load', this.onLoad, this);
8930         
8931         this.resize();
8932         
8933         this.cells = this.el.select('.fc-day',true);
8934         //Roo.log(this.cells);
8935         this.textNodes = this.el.query('.fc-day-number');
8936         this.cells.addClassOnOver('fc-state-hover');
8937         
8938         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
8939         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
8940         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
8941         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
8942         
8943         this.on('monthchange', this.onMonthChange, this);
8944         
8945         this.update(new Date().clearTime());
8946     },
8947     
8948     resize : function() {
8949         var sz  = this.el.getSize();
8950         
8951         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
8952         this.el.select('.fc-day-content div',true).setHeight(34);
8953     },
8954     
8955     
8956     // private
8957     showPrevMonth : function(e){
8958         this.update(this.activeDate.add("mo", -1));
8959     },
8960     showToday : function(e){
8961         this.update(new Date().clearTime());
8962     },
8963     // private
8964     showNextMonth : function(e){
8965         this.update(this.activeDate.add("mo", 1));
8966     },
8967
8968     // private
8969     showPrevYear : function(){
8970         this.update(this.activeDate.add("y", -1));
8971     },
8972
8973     // private
8974     showNextYear : function(){
8975         this.update(this.activeDate.add("y", 1));
8976     },
8977
8978     
8979    // private
8980     update : function(date)
8981     {
8982         var vd = this.activeDate;
8983         this.activeDate = date;
8984 //        if(vd && this.el){
8985 //            var t = date.getTime();
8986 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
8987 //                Roo.log('using add remove');
8988 //                
8989 //                this.fireEvent('monthchange', this, date);
8990 //                
8991 //                this.cells.removeClass("fc-state-highlight");
8992 //                this.cells.each(function(c){
8993 //                   if(c.dateValue == t){
8994 //                       c.addClass("fc-state-highlight");
8995 //                       setTimeout(function(){
8996 //                            try{c.dom.firstChild.focus();}catch(e){}
8997 //                       }, 50);
8998 //                       return false;
8999 //                   }
9000 //                   return true;
9001 //                });
9002 //                return;
9003 //            }
9004 //        }
9005         
9006         var days = date.getDaysInMonth();
9007         
9008         var firstOfMonth = date.getFirstDateOfMonth();
9009         var startingPos = firstOfMonth.getDay()-this.startDay;
9010         
9011         if(startingPos < this.startDay){
9012             startingPos += 7;
9013         }
9014         
9015         var pm = date.add(Date.MONTH, -1);
9016         var prevStart = pm.getDaysInMonth()-startingPos;
9017 //        
9018         this.cells = this.el.select('.fc-day',true);
9019         this.textNodes = this.el.query('.fc-day-number');
9020         this.cells.addClassOnOver('fc-state-hover');
9021         
9022         var cells = this.cells.elements;
9023         var textEls = this.textNodes;
9024         
9025         Roo.each(cells, function(cell){
9026             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
9027         });
9028         
9029         days += startingPos;
9030
9031         // convert everything to numbers so it's fast
9032         var day = 86400000;
9033         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
9034         //Roo.log(d);
9035         //Roo.log(pm);
9036         //Roo.log(prevStart);
9037         
9038         var today = new Date().clearTime().getTime();
9039         var sel = date.clearTime().getTime();
9040         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
9041         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
9042         var ddMatch = this.disabledDatesRE;
9043         var ddText = this.disabledDatesText;
9044         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
9045         var ddaysText = this.disabledDaysText;
9046         var format = this.format;
9047         
9048         var setCellClass = function(cal, cell){
9049             
9050             //Roo.log('set Cell Class');
9051             cell.title = "";
9052             var t = d.getTime();
9053             
9054             //Roo.log(d);
9055             
9056             cell.dateValue = t;
9057             if(t == today){
9058                 cell.className += " fc-today";
9059                 cell.className += " fc-state-highlight";
9060                 cell.title = cal.todayText;
9061             }
9062             if(t == sel){
9063                 // disable highlight in other month..
9064                 //cell.className += " fc-state-highlight";
9065                 
9066             }
9067             // disabling
9068             if(t < min) {
9069                 cell.className = " fc-state-disabled";
9070                 cell.title = cal.minText;
9071                 return;
9072             }
9073             if(t > max) {
9074                 cell.className = " fc-state-disabled";
9075                 cell.title = cal.maxText;
9076                 return;
9077             }
9078             if(ddays){
9079                 if(ddays.indexOf(d.getDay()) != -1){
9080                     cell.title = ddaysText;
9081                     cell.className = " fc-state-disabled";
9082                 }
9083             }
9084             if(ddMatch && format){
9085                 var fvalue = d.dateFormat(format);
9086                 if(ddMatch.test(fvalue)){
9087                     cell.title = ddText.replace("%0", fvalue);
9088                     cell.className = " fc-state-disabled";
9089                 }
9090             }
9091             
9092             if (!cell.initialClassName) {
9093                 cell.initialClassName = cell.dom.className;
9094             }
9095             
9096             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
9097         };
9098
9099         var i = 0;
9100         
9101         for(; i < startingPos; i++) {
9102             textEls[i].innerHTML = (++prevStart);
9103             d.setDate(d.getDate()+1);
9104             
9105             cells[i].className = "fc-past fc-other-month";
9106             setCellClass(this, cells[i]);
9107         }
9108         
9109         var intDay = 0;
9110         
9111         for(; i < days; i++){
9112             intDay = i - startingPos + 1;
9113             textEls[i].innerHTML = (intDay);
9114             d.setDate(d.getDate()+1);
9115             
9116             cells[i].className = ''; // "x-date-active";
9117             setCellClass(this, cells[i]);
9118         }
9119         var extraDays = 0;
9120         
9121         for(; i < 42; i++) {
9122             textEls[i].innerHTML = (++extraDays);
9123             d.setDate(d.getDate()+1);
9124             
9125             cells[i].className = "fc-future fc-other-month";
9126             setCellClass(this, cells[i]);
9127         }
9128         
9129         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
9130         
9131         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
9132         
9133         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
9134         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
9135         
9136         if(totalRows != 6){
9137             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
9138             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
9139         }
9140         
9141         this.fireEvent('monthchange', this, date);
9142         
9143         
9144         /*
9145         if(!this.internalRender){
9146             var main = this.el.dom.firstChild;
9147             var w = main.offsetWidth;
9148             this.el.setWidth(w + this.el.getBorderWidth("lr"));
9149             Roo.fly(main).setWidth(w);
9150             this.internalRender = true;
9151             // opera does not respect the auto grow header center column
9152             // then, after it gets a width opera refuses to recalculate
9153             // without a second pass
9154             if(Roo.isOpera && !this.secondPass){
9155                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
9156                 this.secondPass = true;
9157                 this.update.defer(10, this, [date]);
9158             }
9159         }
9160         */
9161         
9162     },
9163     
9164     findCell : function(dt) {
9165         dt = dt.clearTime().getTime();
9166         var ret = false;
9167         this.cells.each(function(c){
9168             //Roo.log("check " +c.dateValue + '?=' + dt);
9169             if(c.dateValue == dt){
9170                 ret = c;
9171                 return false;
9172             }
9173             return true;
9174         });
9175         
9176         return ret;
9177     },
9178     
9179     findCells : function(ev) {
9180         var s = ev.start.clone().clearTime().getTime();
9181        // Roo.log(s);
9182         var e= ev.end.clone().clearTime().getTime();
9183        // Roo.log(e);
9184         var ret = [];
9185         this.cells.each(function(c){
9186              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
9187             
9188             if(c.dateValue > e){
9189                 return ;
9190             }
9191             if(c.dateValue < s){
9192                 return ;
9193             }
9194             ret.push(c);
9195         });
9196         
9197         return ret;    
9198     },
9199     
9200     findBestRow: function(cells)
9201     {
9202         var ret = 0;
9203         
9204         for (var i =0 ; i < cells.length;i++) {
9205             ret  = Math.max(cells[i].rows || 0,ret);
9206         }
9207         return ret;
9208         
9209     },
9210     
9211     
9212     addItem : function(ev)
9213     {
9214         // look for vertical location slot in
9215         var cells = this.findCells(ev);
9216         
9217         ev.row = this.findBestRow(cells);
9218         
9219         // work out the location.
9220         
9221         var crow = false;
9222         var rows = [];
9223         for(var i =0; i < cells.length; i++) {
9224             if (!crow) {
9225                 crow = {
9226                     start : cells[i],
9227                     end :  cells[i]
9228                 };
9229                 continue;
9230             }
9231             if (crow.start.getY() == cells[i].getY()) {
9232                 // on same row.
9233                 crow.end = cells[i];
9234                 continue;
9235             }
9236             // different row.
9237             rows.push(crow);
9238             crow = {
9239                 start: cells[i],
9240                 end : cells[i]
9241             };
9242             
9243         }
9244         
9245         rows.push(crow);
9246         ev.els = [];
9247         ev.rows = rows;
9248         ev.cells = cells;
9249         for (var i = 0; i < cells.length;i++) {
9250             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
9251             
9252         }
9253         
9254         this.calevents.push(ev);
9255     },
9256     
9257     clearEvents: function() {
9258         
9259         if(!this.calevents){
9260             return;
9261         }
9262         
9263         Roo.each(this.cells.elements, function(c){
9264             c.rows = 0;
9265         });
9266         
9267         Roo.each(this.calevents, function(e) {
9268             Roo.each(e.els, function(el) {
9269                 el.un('mouseenter' ,this.onEventEnter, this);
9270                 el.un('mouseleave' ,this.onEventLeave, this);
9271                 el.remove();
9272             },this);
9273         },this);
9274         
9275     },
9276     
9277     renderEvents: function()
9278     {   
9279         // first make sure there is enough space..
9280         
9281         this.cells.each(function(c) {
9282         
9283             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
9284         });
9285         
9286         for (var e = 0; e < this.calevents.length; e++) {
9287             var ev = this.calevents[e];
9288             var cells = ev.cells;
9289             var rows = ev.rows;
9290             
9291             for(var i =0; i < rows.length; i++) {
9292                 
9293                  
9294                 // how many rows should it span..
9295                 
9296                 var  cfg = {
9297                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
9298                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
9299                     
9300                     unselectable : "on",
9301                     cn : [
9302                         {
9303                             cls: 'fc-event-inner',
9304                             cn : [
9305                                 {
9306                                   tag:'span',
9307                                   cls: 'fc-event-time',
9308                                   html : cells.length > 1 ? '' : ev.time
9309                                 },
9310                                 {
9311                                   tag:'span',
9312                                   cls: 'fc-event-title',
9313                                   html : String.format('{0}', ev.title)
9314                                 }
9315                                 
9316                                 
9317                             ]
9318                         },
9319                         {
9320                             cls: 'ui-resizable-handle ui-resizable-e',
9321                             html : '&nbsp;&nbsp;&nbsp'
9322                         }
9323                         
9324                     ]
9325                 };
9326                 if (i == 0) {
9327                     cfg.cls += ' fc-event-start';
9328                 }
9329                 if ((i+1) == rows.length) {
9330                     cfg.cls += ' fc-event-end';
9331                 }
9332                 
9333                 var ctr = this.el.select('.fc-event-container',true).first();
9334                 var cg = ctr.createChild(cfg);
9335                 
9336                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
9337                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
9338                 cg.on('click', this.onEventClick, this, ev);
9339                 
9340                 ev.els.push(cg);
9341                 
9342                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
9343                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
9344                 //Roo.log(cg);
9345                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
9346                 cg.setWidth(ebox.right - sbox.x -2);
9347             }
9348             
9349             
9350         }
9351         
9352     },
9353     
9354     onEventEnter: function (e, el,event,d) {
9355         this.fireEvent('evententer', this, el, event);
9356     },
9357     
9358     onEventLeave: function (e, el,event,d) {
9359         this.fireEvent('eventleave', this, el, event);
9360     },
9361     
9362     onEventClick: function (e, el,event,d) {
9363         this.fireEvent('eventclick', this, el, event);
9364     },
9365     
9366     onMonthChange: function () {
9367         this.store.load();
9368     },
9369     
9370     onLoad: function () 
9371     {
9372         this.mask();
9373         
9374         this.clearEvents();
9375
9376         this.calevents = [];
9377         var cal = this;
9378         if(this.store.getCount() > 0){
9379             this.store.data.each(function(d){
9380                cal.addItem({
9381                     id : d.data.id,
9382                     start: new Date(d.data.start_dt),
9383                     end : new Date(d.data.end_dt),
9384                     time : d.data.start_time,
9385                     title : d.data.title,
9386                     description : d.data.description,
9387                     venue : d.data.venue
9388                 });
9389             });
9390         }
9391         
9392         this.renderEvents();
9393     },
9394     
9395     mask: function()
9396     {
9397         var size = this.el.getViewSize();
9398         var xy = this.el.getXY();
9399         
9400         this.maskEl.setSize(size.width, size.height);
9401         this.maskEl.setXY(xy);
9402         this.maskEl.show();
9403     }
9404 });
9405
9406  
9407  /*
9408  * - LGPL
9409  *
9410  * element
9411  * 
9412  */
9413
9414 /**
9415  * @class Roo.bootstrap.Popover
9416  * @extends Roo.bootstrap.Component
9417  * Bootstrap Popover class
9418  * @cfg {String} html contents of the popover   (or false to use children..)
9419  * @cfg {String} title of popover (or false to hide)
9420  * @cfg {String} placement how it is placed
9421  * @cfg {String} trigger click || hover (or false to trigger manually)
9422  * @cfg {String} over what (parent or false to trigger manually.)
9423  * 
9424  * @constructor
9425  * Create a new Popover
9426  * @param {Object} config The config object
9427  */
9428
9429 Roo.bootstrap.Popover = function(config){
9430     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
9431 };
9432
9433 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
9434     
9435     title: 'Fill in a title',
9436     html: false,
9437     
9438     placement : 'right',
9439     trigger : 'hover', // hover
9440     
9441     over: 'parent',
9442     
9443     can_build_overlaid : false,
9444     
9445     getChildContainer : function()
9446     {
9447         return this.el.select('.popover-content',true).first();
9448     },
9449     
9450     getAutoCreate : function(){
9451          Roo.log('make popover?');
9452         var cfg = {
9453            cls : 'popover roo-dynamic',
9454            style: 'display:block',
9455            cn : [
9456                 {
9457                     cls : 'arrow'
9458                 },
9459                 {
9460                     cls : 'popover-inner',
9461                     cn : [
9462                         {
9463                             tag: 'h3',
9464                             cls: 'popover-title',
9465                             html : this.title
9466                         },
9467                         {
9468                             cls : 'popover-content',
9469                             html : this.html
9470                         }
9471                     ]
9472                     
9473                 }
9474            ]
9475         };
9476         
9477         return cfg;
9478     },
9479     setTitle: function(str)
9480     {
9481         this.el.select('.popover-title',true).first().dom.innerHTML = str;
9482     },
9483     setContent: function(str)
9484     {
9485         this.el.select('.popover-content',true).first().dom.innerHTML = str;
9486     },
9487     // as it get's added to the bottom of the page.
9488     onRender : function(ct, position)
9489     {
9490         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
9491         if(!this.el){
9492             var cfg = Roo.apply({},  this.getAutoCreate());
9493             cfg.id = Roo.id();
9494             
9495             if (this.cls) {
9496                 cfg.cls += ' ' + this.cls;
9497             }
9498             if (this.style) {
9499                 cfg.style = this.style;
9500             }
9501             Roo.log("adding to ")
9502             this.el = Roo.get(document.body).createChild(cfg, position);
9503             Roo.log(this.el);
9504         }
9505         this.initEvents();
9506     },
9507     
9508     initEvents : function()
9509     {
9510         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
9511         this.el.enableDisplayMode('block');
9512         this.el.hide();
9513         if (this.over === false) {
9514             return; 
9515         }
9516         if (this.triggers === false) {
9517             return;
9518         }
9519         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
9520         var triggers = this.trigger ? this.trigger.split(' ') : [];
9521         Roo.each(triggers, function(trigger) {
9522         
9523             if (trigger == 'click') {
9524                 on_el.on('click', this.toggle, this);
9525             } else if (trigger != 'manual') {
9526                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
9527                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
9528       
9529                 on_el.on(eventIn  ,this.enter, this);
9530                 on_el.on(eventOut, this.leave, this);
9531             }
9532         }, this);
9533         
9534     },
9535     
9536     
9537     // private
9538     timeout : null,
9539     hoverState : null,
9540     
9541     toggle : function () {
9542         this.hoverState == 'in' ? this.leave() : this.enter();
9543     },
9544     
9545     enter : function () {
9546        
9547     
9548         clearTimeout(this.timeout);
9549     
9550         this.hoverState = 'in'
9551     
9552         if (!this.delay || !this.delay.show) {
9553             this.show();
9554             return 
9555         }
9556         var _t = this;
9557         this.timeout = setTimeout(function () {
9558             if (_t.hoverState == 'in') {
9559                 _t.show();
9560             }
9561         }, this.delay.show)
9562     },
9563     leave : function() {
9564         clearTimeout(this.timeout);
9565     
9566         this.hoverState = 'out'
9567     
9568         if (!this.delay || !this.delay.hide) {
9569             this.hide();
9570             return 
9571         }
9572         var _t = this;
9573         this.timeout = setTimeout(function () {
9574             if (_t.hoverState == 'out') {
9575                 _t.hide();
9576             }
9577         }, this.delay.hide)
9578     },
9579     
9580     show : function (on_el)
9581     {
9582         if (!on_el) {
9583             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
9584         }
9585         // set content.
9586         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
9587         if (this.html !== false) {
9588             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
9589         }
9590         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
9591         if (!this.title.length) {
9592             this.el.select('.popover-title',true).hide();
9593         }
9594         
9595         var placement = typeof this.placement == 'function' ?
9596             this.placement.call(this, this.el, on_el) :
9597             this.placement;
9598             
9599         var autoToken = /\s?auto?\s?/i;
9600         var autoPlace = autoToken.test(placement);
9601         if (autoPlace) {
9602             placement = placement.replace(autoToken, '') || 'top';
9603         }
9604         
9605         //this.el.detach()
9606         //this.el.setXY([0,0]);
9607         this.el.show();
9608         this.el.dom.style.display='block';
9609         this.el.addClass(placement);
9610         
9611         //this.el.appendTo(on_el);
9612         
9613         var p = this.getPosition();
9614         var box = this.el.getBox();
9615         
9616         if (autoPlace) {
9617             // fixme..
9618         }
9619         var align = Roo.bootstrap.Popover.alignment[placement]
9620         this.el.alignTo(on_el, align[0],align[1]);
9621         //var arrow = this.el.select('.arrow',true).first();
9622         //arrow.set(align[2], 
9623         
9624         this.el.addClass('in');
9625         this.hoverState = null;
9626         
9627         if (this.el.hasClass('fade')) {
9628             // fade it?
9629         }
9630         
9631     },
9632     hide : function()
9633     {
9634         this.el.setXY([0,0]);
9635         this.el.removeClass('in');
9636         this.el.hide();
9637         
9638     }
9639     
9640 });
9641
9642 Roo.bootstrap.Popover.alignment = {
9643     'left' : ['r-l', [-10,0], 'right'],
9644     'right' : ['l-r', [10,0], 'left'],
9645     'bottom' : ['t-b', [0,10], 'top'],
9646     'top' : [ 'b-t', [0,-10], 'bottom']
9647 };
9648
9649  /*
9650  * - LGPL
9651  *
9652  * Progress
9653  * 
9654  */
9655
9656 /**
9657  * @class Roo.bootstrap.Progress
9658  * @extends Roo.bootstrap.Component
9659  * Bootstrap Progress class
9660  * @cfg {Boolean} striped striped of the progress bar
9661  * @cfg {Boolean} active animated of the progress bar
9662  * 
9663  * 
9664  * @constructor
9665  * Create a new Progress
9666  * @param {Object} config The config object
9667  */
9668
9669 Roo.bootstrap.Progress = function(config){
9670     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
9671 };
9672
9673 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
9674     
9675     striped : false,
9676     active: false,
9677     
9678     getAutoCreate : function(){
9679         var cfg = {
9680             tag: 'div',
9681             cls: 'progress'
9682         };
9683         
9684         
9685         if(this.striped){
9686             cfg.cls += ' progress-striped';
9687         }
9688       
9689         if(this.active){
9690             cfg.cls += ' active';
9691         }
9692         
9693         
9694         return cfg;
9695     }
9696    
9697 });
9698
9699  
9700
9701  /*
9702  * - LGPL
9703  *
9704  * ProgressBar
9705  * 
9706  */
9707
9708 /**
9709  * @class Roo.bootstrap.ProgressBar
9710  * @extends Roo.bootstrap.Component
9711  * Bootstrap ProgressBar class
9712  * @cfg {Number} aria_valuenow aria-value now
9713  * @cfg {Number} aria_valuemin aria-value min
9714  * @cfg {Number} aria_valuemax aria-value max
9715  * @cfg {String} label label for the progress bar
9716  * @cfg {String} panel (success | info | warning | danger )
9717  * @cfg {String} role role of the progress bar
9718  * @cfg {String} sr_only text
9719  * 
9720  * 
9721  * @constructor
9722  * Create a new ProgressBar
9723  * @param {Object} config The config object
9724  */
9725
9726 Roo.bootstrap.ProgressBar = function(config){
9727     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
9728 };
9729
9730 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
9731     
9732     aria_valuenow : 0,
9733     aria_valuemin : 0,
9734     aria_valuemax : 100,
9735     label : false,
9736     panel : false,
9737     role : false,
9738     sr_only: false,
9739     
9740     getAutoCreate : function()
9741     {
9742         
9743         var cfg = {
9744             tag: 'div',
9745             cls: 'progress-bar',
9746             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
9747         };
9748         
9749         if(this.sr_only){
9750             cfg.cn = {
9751                 tag: 'span',
9752                 cls: 'sr-only',
9753                 html: this.sr_only
9754             }
9755         }
9756         
9757         if(this.role){
9758             cfg.role = this.role;
9759         }
9760         
9761         if(this.aria_valuenow){
9762             cfg['aria-valuenow'] = this.aria_valuenow;
9763         }
9764         
9765         if(this.aria_valuemin){
9766             cfg['aria-valuemin'] = this.aria_valuemin;
9767         }
9768         
9769         if(this.aria_valuemax){
9770             cfg['aria-valuemax'] = this.aria_valuemax;
9771         }
9772         
9773         if(this.label && !this.sr_only){
9774             cfg.html = this.label;
9775         }
9776         
9777         if(this.panel){
9778             cfg.cls += ' progress-bar-' + this.panel;
9779         }
9780         
9781         return cfg;
9782     },
9783     
9784     update : function(aria_valuenow)
9785     {
9786         this.aria_valuenow = aria_valuenow;
9787         
9788         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
9789     }
9790    
9791 });
9792
9793  
9794
9795  /*
9796  * - LGPL
9797  *
9798  * TabPanel
9799  * 
9800  */
9801
9802 /**
9803  * @class Roo.bootstrap.TabPanel
9804  * @extends Roo.bootstrap.Component
9805  * Bootstrap TabPanel class
9806  * @cfg {Boolean} active panel active
9807  * @cfg {String} html panel content
9808  * @cfg {String} tabId tab relate id
9809  * 
9810  * 
9811  * @constructor
9812  * Create a new TabPanel
9813  * @param {Object} config The config object
9814  */
9815
9816 Roo.bootstrap.TabPanel = function(config){
9817     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
9818 };
9819
9820 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
9821     
9822     active: false,
9823     html: false,
9824     tabId: false,
9825     
9826     getAutoCreate : function(){
9827         var cfg = {
9828             tag: 'div',
9829             cls: 'tab-pane',
9830             html: this.html || ''
9831         };
9832         
9833         if(this.active){
9834             cfg.cls += ' active';
9835         }
9836         
9837         if(this.tabId){
9838             cfg.tabId = this.tabId;
9839         }
9840         
9841         return cfg;
9842     }
9843    
9844 });
9845
9846  
9847
9848  /*
9849  * - LGPL
9850  *
9851  * DateField
9852  * 
9853  */
9854
9855 /**
9856  * @class Roo.bootstrap.DateField
9857  * @extends Roo.bootstrap.Input
9858  * Bootstrap DateField class
9859  * @cfg {Number} weekStart default 0
9860  * @cfg {Number} weekStart default 0
9861  * @cfg {Number} viewMode default empty, (months|years)
9862  * @cfg {Number} minViewMode default empty, (months|years)
9863  * @cfg {Number} startDate default -Infinity
9864  * @cfg {Number} endDate default Infinity
9865  * @cfg {Boolean} todayHighlight default false
9866  * @cfg {Boolean} todayBtn default false
9867  * @cfg {Boolean} calendarWeeks default false
9868  * @cfg {Object} daysOfWeekDisabled default empty
9869  * 
9870  * @cfg {Boolean} keyboardNavigation default true
9871  * @cfg {String} language default en
9872  * 
9873  * @constructor
9874  * Create a new DateField
9875  * @param {Object} config The config object
9876  */
9877
9878 Roo.bootstrap.DateField = function(config){
9879     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
9880 };
9881
9882 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
9883     
9884     /**
9885      * @cfg {String} format
9886      * The default date format string which can be overriden for localization support.  The format must be
9887      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9888      */
9889     format : "m/d/y",
9890     /**
9891      * @cfg {String} altFormats
9892      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
9893      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
9894      */
9895     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
9896     
9897     weekStart : 0,
9898     
9899     viewMode : '',
9900     
9901     minViewMode : '',
9902     
9903     todayHighlight : false,
9904     
9905     todayBtn: false,
9906     
9907     language: 'en',
9908     
9909     keyboardNavigation: true,
9910     
9911     calendarWeeks: false,
9912     
9913     startDate: -Infinity,
9914     
9915     endDate: Infinity,
9916     
9917     daysOfWeekDisabled: [],
9918     
9919     _events: [],
9920     
9921     UTCDate: function()
9922     {
9923         return new Date(Date.UTC.apply(Date, arguments));
9924     },
9925     
9926     UTCToday: function()
9927     {
9928         var today = new Date();
9929         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
9930     },
9931     
9932     getDate: function() {
9933             var d = this.getUTCDate();
9934             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
9935     },
9936     
9937     getUTCDate: function() {
9938             return this.date;
9939     },
9940     
9941     setDate: function(d) {
9942             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
9943     },
9944     
9945     setUTCDate: function(d) {
9946             this.date = d;
9947             this.setValue(this.formatDate(this.date));
9948     },
9949         
9950     onRender: function(ct, position)
9951     {
9952         
9953         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
9954         
9955         this.language = this.language || 'en';
9956         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
9957         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
9958         
9959         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
9960         this.format = this.format || 'm/d/y';
9961         this.isInline = false;
9962         this.isInput = true;
9963         this.component = this.el.select('.add-on', true).first() || false;
9964         this.component = (this.component && this.component.length === 0) ? false : this.component;
9965         this.hasInput = this.component && this.inputEL().length;
9966         
9967         if (typeof(this.minViewMode === 'string')) {
9968             switch (this.minViewMode) {
9969                 case 'months':
9970                     this.minViewMode = 1;
9971                     break;
9972                 case 'years':
9973                     this.minViewMode = 2;
9974                     break;
9975                 default:
9976                     this.minViewMode = 0;
9977                     break;
9978             }
9979         }
9980         
9981         if (typeof(this.viewMode === 'string')) {
9982             switch (this.viewMode) {
9983                 case 'months':
9984                     this.viewMode = 1;
9985                     break;
9986                 case 'years':
9987                     this.viewMode = 2;
9988                     break;
9989                 default:
9990                     this.viewMode = 0;
9991                     break;
9992             }
9993         }
9994                 
9995         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
9996         
9997         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
9998         
9999         this.picker().on('mousedown', this.onMousedown, this);
10000         this.picker().on('click', this.onClick, this);
10001         
10002         this.picker().addClass('datepicker-dropdown');
10003         
10004         this.startViewMode = this.viewMode;
10005         
10006         
10007         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
10008             if(!this.calendarWeeks){
10009                 v.remove();
10010                 return;
10011             };
10012             
10013             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
10014             v.attr('colspan', function(i, val){
10015                 return parseInt(val) + 1;
10016             });
10017         })
10018                         
10019         
10020         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
10021         
10022         this.setStartDate(this.startDate);
10023         this.setEndDate(this.endDate);
10024         
10025         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
10026         
10027         this.fillDow();
10028         this.fillMonths();
10029         this.update();
10030         this.showMode();
10031         
10032         if(this.isInline) {
10033             this.show();
10034         }
10035     },
10036     
10037     picker : function()
10038     {
10039         return this.el.select('.datepicker', true).first();
10040     },
10041     
10042     fillDow: function()
10043     {
10044         var dowCnt = this.weekStart;
10045         
10046         var dow = {
10047             tag: 'tr',
10048             cn: [
10049                 
10050             ]
10051         };
10052         
10053         if(this.calendarWeeks){
10054             dow.cn.push({
10055                 tag: 'th',
10056                 cls: 'cw',
10057                 html: '&nbsp;'
10058             })
10059         }
10060         
10061         while (dowCnt < this.weekStart + 7) {
10062             dow.cn.push({
10063                 tag: 'th',
10064                 cls: 'dow',
10065                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
10066             });
10067         }
10068         
10069         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
10070     },
10071     
10072     fillMonths: function()
10073     {    
10074         var i = 0
10075         var months = this.picker().select('>.datepicker-months td', true).first();
10076         
10077         months.dom.innerHTML = '';
10078         
10079         while (i < 12) {
10080             var month = {
10081                 tag: 'span',
10082                 cls: 'month',
10083                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
10084             }
10085             
10086             months.createChild(month);
10087         }
10088         
10089     },
10090     
10091     update: function(){
10092         
10093         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
10094         
10095         if (this.date < this.startDate) {
10096             this.viewDate = new Date(this.startDate);
10097         } else if (this.date > this.endDate) {
10098             this.viewDate = new Date(this.endDate);
10099         } else {
10100             this.viewDate = new Date(this.date);
10101         }
10102         
10103         this.fill();
10104     },
10105     
10106     fill: function() {
10107         var d = new Date(this.viewDate),
10108                 year = d.getUTCFullYear(),
10109                 month = d.getUTCMonth(),
10110                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
10111                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
10112                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
10113                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
10114                 currentDate = this.date && this.date.valueOf(),
10115                 today = this.UTCToday();
10116         
10117         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
10118         
10119 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
10120         
10121 //        this.picker.select('>tfoot th.today').
10122 //                                              .text(dates[this.language].today)
10123 //                                              .toggle(this.todayBtn !== false);
10124     
10125         this.updateNavArrows();
10126         this.fillMonths();
10127                                                 
10128         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
10129         
10130         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
10131          
10132         prevMonth.setUTCDate(day);
10133         
10134         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
10135         
10136         var nextMonth = new Date(prevMonth);
10137         
10138         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
10139         
10140         nextMonth = nextMonth.valueOf();
10141         
10142         var fillMonths = false;
10143         
10144         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
10145         
10146         while(prevMonth.valueOf() < nextMonth) {
10147             var clsName = '';
10148             
10149             if (prevMonth.getUTCDay() === this.weekStart) {
10150                 if(fillMonths){
10151                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
10152                 }
10153                     
10154                 fillMonths = {
10155                     tag: 'tr',
10156                     cn: []
10157                 };
10158                 
10159                 if(this.calendarWeeks){
10160                     // ISO 8601: First week contains first thursday.
10161                     // ISO also states week starts on Monday, but we can be more abstract here.
10162                     var
10163                     // Start of current week: based on weekstart/current date
10164                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
10165                     // Thursday of this week
10166                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
10167                     // First Thursday of year, year from thursday
10168                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
10169                     // Calendar week: ms between thursdays, div ms per day, div 7 days
10170                     calWeek =  (th - yth) / 864e5 / 7 + 1;
10171                     
10172                     fillMonths.cn.push({
10173                         tag: 'td',
10174                         cls: 'cw',
10175                         html: calWeek
10176                     });
10177                 }
10178             }
10179             
10180             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
10181                 clsName += ' old';
10182             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
10183                 clsName += ' new';
10184             }
10185             if (this.todayHighlight &&
10186                 prevMonth.getUTCFullYear() == today.getFullYear() &&
10187                 prevMonth.getUTCMonth() == today.getMonth() &&
10188                 prevMonth.getUTCDate() == today.getDate()) {
10189                 clsName += ' today';
10190             }
10191             
10192             if (currentDate && prevMonth.valueOf() === currentDate) {
10193                 clsName += ' active';
10194             }
10195             
10196             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
10197                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
10198                     clsName += ' disabled';
10199             }
10200             
10201             fillMonths.cn.push({
10202                 tag: 'td',
10203                 cls: 'day ' + clsName,
10204                 html: prevMonth.getDate()
10205             })
10206             
10207             prevMonth.setDate(prevMonth.getDate()+1);
10208         }
10209           
10210         var currentYear = this.date && this.date.getUTCFullYear();
10211         var currentMonth = this.date && this.date.getUTCMonth();
10212         
10213         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
10214         
10215         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
10216             v.removeClass('active');
10217             
10218             if(currentYear === year && k === currentMonth){
10219                 v.addClass('active');
10220             }
10221             
10222             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
10223                 v.addClass('disabled');
10224             }
10225             
10226         });
10227         
10228         
10229         year = parseInt(year/10, 10) * 10;
10230         
10231         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
10232         
10233         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
10234         
10235         year -= 1;
10236         for (var i = -1; i < 11; i++) {
10237             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
10238                 tag: 'span',
10239                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
10240                 html: year
10241             })
10242             
10243             year += 1;
10244         }
10245     },
10246     
10247     showMode: function(dir) {
10248         if (dir) {
10249             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
10250         }
10251         Roo.each(this.picker().select('>div',true).elements, function(v){
10252             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10253             v.hide();
10254         });
10255         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
10256     },
10257     
10258     place: function()
10259     {
10260         if(this.isInline) return;
10261         
10262         this.picker().removeClass(['bottom', 'top']);
10263         
10264         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
10265             /*
10266              * place to the top of element!
10267              *
10268              */
10269             
10270             this.picker().addClass('top');
10271             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
10272             
10273             return;
10274         }
10275         
10276         this.picker().addClass('bottom');
10277         
10278         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
10279     },
10280     
10281     parseDate : function(value){
10282         if(!value || value instanceof Date){
10283             return value;
10284         }
10285         var v = Date.parseDate(value, this.format);
10286         if (!v && this.useIso) {
10287             v = Date.parseDate(value, 'Y-m-d');
10288         }
10289         if(!v && this.altFormats){
10290             if(!this.altFormatsArray){
10291                 this.altFormatsArray = this.altFormats.split("|");
10292             }
10293             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
10294                 v = Date.parseDate(value, this.altFormatsArray[i]);
10295             }
10296         }
10297         return v;
10298     },
10299     
10300     formatDate : function(date, fmt){
10301         return (!date || !(date instanceof Date)) ?
10302         date : date.dateFormat(fmt || this.format);
10303     },
10304     
10305     onFocus : function()
10306     {
10307         Roo.bootstrap.DateField.superclass.onFocus.call(this);
10308         this.show();
10309     },
10310     
10311     onBlur : function()
10312     {
10313         Roo.bootstrap.DateField.superclass.onBlur.call(this);
10314         this.hide();
10315     },
10316     
10317     show : function()
10318     {
10319         this.picker().show();
10320         this.update();
10321         this.place();
10322     },
10323     
10324     hide : function()
10325     {
10326         if(this.isInline) return;
10327         this.picker().hide();
10328         this.viewMode = this.startViewMode;
10329         this.showMode();
10330         
10331     },
10332     
10333     onMousedown: function(e){
10334         e.stopPropagation();
10335         e.preventDefault();
10336     },
10337     
10338     keyup: function(e){
10339         Roo.bootstrap.DateField.superclass.keyup.call(this);
10340         this.update();
10341         
10342     },
10343     
10344     fireKey: function(e){
10345         if (!this.picker().isVisible()){
10346             if (e.keyCode == 27) // allow escape to hide and re-show picker
10347                 this.show();
10348             return;
10349         }
10350         var dateChanged = false,
10351         dir, day, month,
10352         newDate, newViewDate;
10353         switch(e.keyCode){
10354             case 27: // escape
10355                 this.hide();
10356                 e.preventDefault();
10357                 break;
10358             case 37: // left
10359             case 39: // right
10360                 if (!this.keyboardNavigation) break;
10361                 dir = e.keyCode == 37 ? -1 : 1;
10362                 
10363                 if (e.ctrlKey){
10364                     newDate = this.moveYear(this.date, dir);
10365                     newViewDate = this.moveYear(this.viewDate, dir);
10366                 } else if (e.shiftKey){
10367                     newDate = this.moveMonth(this.date, dir);
10368                     newViewDate = this.moveMonth(this.viewDate, dir);
10369                 } else {
10370                     newDate = new Date(this.date);
10371                     newDate.setUTCDate(this.date.getUTCDate() + dir);
10372                     newViewDate = new Date(this.viewDate);
10373                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
10374                 }
10375                 if (this.dateWithinRange(newDate)){
10376                     this.date = newDate;
10377                     this.viewDate = newViewDate;
10378                     this.setValue(this.formatDate(this.date));
10379                     this.update();
10380                     e.preventDefault();
10381                     dateChanged = true;
10382                 }
10383                 break;
10384             case 38: // up
10385             case 40: // down
10386                 if (!this.keyboardNavigation) break;
10387                 dir = e.keyCode == 38 ? -1 : 1;
10388                 if (e.ctrlKey){
10389                     newDate = this.moveYear(this.date, dir);
10390                     newViewDate = this.moveYear(this.viewDate, dir);
10391                 } else if (e.shiftKey){
10392                     newDate = this.moveMonth(this.date, dir);
10393                     newViewDate = this.moveMonth(this.viewDate, dir);
10394                 } else {
10395                     newDate = new Date(this.date);
10396                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
10397                     newViewDate = new Date(this.viewDate);
10398                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
10399                 }
10400                 if (this.dateWithinRange(newDate)){
10401                     this.date = newDate;
10402                     this.viewDate = newViewDate;
10403                     this.setValue(this.formatDate(this.date));
10404                     this.update();
10405                     e.preventDefault();
10406                     dateChanged = true;
10407                 }
10408                 break;
10409             case 13: // enter
10410                 this.setValue(this.formatDate(this.date));
10411                 this.hide();
10412                 e.preventDefault();
10413                 break;
10414             case 9: // tab
10415                 this.setValue(this.formatDate(this.date));
10416                 this.hide();
10417                 break;
10418         }
10419     },
10420     
10421     
10422     onClick: function(e) {
10423         e.stopPropagation();
10424         e.preventDefault();
10425         
10426         var target = e.getTarget();
10427         
10428         if(target.nodeName.toLowerCase() === 'i'){
10429             target = Roo.get(target).dom.parentNode;
10430         }
10431         
10432         var nodeName = target.nodeName;
10433         var className = target.className;
10434         var html = target.innerHTML;
10435         
10436         switch(nodeName.toLowerCase()) {
10437             case 'th':
10438                 switch(className) {
10439                     case 'switch':
10440                         this.showMode(1);
10441                         break;
10442                     case 'prev':
10443                     case 'next':
10444                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
10445                         switch(this.viewMode){
10446                                 case 0:
10447                                         this.viewDate = this.moveMonth(this.viewDate, dir);
10448                                         break;
10449                                 case 1:
10450                                 case 2:
10451                                         this.viewDate = this.moveYear(this.viewDate, dir);
10452                                         break;
10453                         }
10454                         this.fill();
10455                         break;
10456                     case 'today':
10457                         var date = new Date();
10458                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
10459                         this.fill()
10460                         this.setValue(this.formatDate(this.date));
10461                         this.hide();
10462                         break;
10463                 }
10464                 break;
10465             case 'span':
10466                 if (className.indexOf('disabled') === -1) {
10467                     this.viewDate.setUTCDate(1);
10468                     if (className.indexOf('month') !== -1) {
10469                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
10470                     } else {
10471                         var year = parseInt(html, 10) || 0;
10472                         this.viewDate.setUTCFullYear(year);
10473                         
10474                     }
10475                     this.showMode(-1);
10476                     this.fill();
10477                 }
10478                 break;
10479                 
10480             case 'td':
10481                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
10482                     var day = parseInt(html, 10) || 1;
10483                     var year = this.viewDate.getUTCFullYear(),
10484                         month = this.viewDate.getUTCMonth();
10485
10486                     if (className.indexOf('old') !== -1) {
10487                         if(month === 0 ){
10488                             month = 11;
10489                             year -= 1;
10490                         }else{
10491                             month -= 1;
10492                         }
10493                     } else if (className.indexOf('new') !== -1) {
10494                         if (month == 11) {
10495                             month = 0;
10496                             year += 1;
10497                         } else {
10498                             month += 1;
10499                         }
10500                     }
10501                     this.date = this.UTCDate(year, month, day,0,0,0,0);
10502                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
10503                     this.fill();
10504                     this.setValue(this.formatDate(this.date));
10505                     this.hide();
10506                 }
10507                 break;
10508         }
10509     },
10510     
10511     setStartDate: function(startDate){
10512         this.startDate = startDate || -Infinity;
10513         if (this.startDate !== -Infinity) {
10514             this.startDate = this.parseDate(this.startDate);
10515         }
10516         this.update();
10517         this.updateNavArrows();
10518     },
10519
10520     setEndDate: function(endDate){
10521         this.endDate = endDate || Infinity;
10522         if (this.endDate !== Infinity) {
10523             this.endDate = this.parseDate(this.endDate);
10524         }
10525         this.update();
10526         this.updateNavArrows();
10527     },
10528     
10529     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
10530         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
10531         if (typeof(this.daysOfWeekDisabled) !== 'object') {
10532             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
10533         }
10534         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
10535             return parseInt(d, 10);
10536         });
10537         this.update();
10538         this.updateNavArrows();
10539     },
10540     
10541     updateNavArrows: function() {
10542         var d = new Date(this.viewDate),
10543         year = d.getUTCFullYear(),
10544         month = d.getUTCMonth();
10545         
10546         Roo.each(this.picker().select('.prev', true).elements, function(v){
10547             v.show();
10548             switch (this.viewMode) {
10549                 case 0:
10550
10551                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
10552                         v.hide();
10553                     }
10554                     break;
10555                 case 1:
10556                 case 2:
10557                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
10558                         v.hide();
10559                     }
10560                     break;
10561             }
10562         });
10563         
10564         Roo.each(this.picker().select('.next', true).elements, function(v){
10565             v.show();
10566             switch (this.viewMode) {
10567                 case 0:
10568
10569                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
10570                         v.hide();
10571                     }
10572                     break;
10573                 case 1:
10574                 case 2:
10575                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
10576                         v.hide();
10577                     }
10578                     break;
10579             }
10580         })
10581     },
10582     
10583     moveMonth: function(date, dir){
10584         if (!dir) return date;
10585         var new_date = new Date(date.valueOf()),
10586         day = new_date.getUTCDate(),
10587         month = new_date.getUTCMonth(),
10588         mag = Math.abs(dir),
10589         new_month, test;
10590         dir = dir > 0 ? 1 : -1;
10591         if (mag == 1){
10592             test = dir == -1
10593             // If going back one month, make sure month is not current month
10594             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
10595             ? function(){
10596                 return new_date.getUTCMonth() == month;
10597             }
10598             // If going forward one month, make sure month is as expected
10599             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
10600             : function(){
10601                 return new_date.getUTCMonth() != new_month;
10602             };
10603             new_month = month + dir;
10604             new_date.setUTCMonth(new_month);
10605             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
10606             if (new_month < 0 || new_month > 11)
10607                 new_month = (new_month + 12) % 12;
10608         } else {
10609             // For magnitudes >1, move one month at a time...
10610             for (var i=0; i<mag; i++)
10611                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
10612                 new_date = this.moveMonth(new_date, dir);
10613             // ...then reset the day, keeping it in the new month
10614             new_month = new_date.getUTCMonth();
10615             new_date.setUTCDate(day);
10616             test = function(){
10617                 return new_month != new_date.getUTCMonth();
10618             };
10619         }
10620         // Common date-resetting loop -- if date is beyond end of month, make it
10621         // end of month
10622         while (test()){
10623             new_date.setUTCDate(--day);
10624             new_date.setUTCMonth(new_month);
10625         }
10626         return new_date;
10627     },
10628
10629     moveYear: function(date, dir){
10630         return this.moveMonth(date, dir*12);
10631     },
10632
10633     dateWithinRange: function(date){
10634         return date >= this.startDate && date <= this.endDate;
10635     },
10636
10637     
10638     remove: function() {
10639         this.picker().remove();
10640     }
10641    
10642 });
10643
10644 Roo.apply(Roo.bootstrap.DateField,  {
10645     
10646     head : {
10647         tag: 'thead',
10648         cn: [
10649         {
10650             tag: 'tr',
10651             cn: [
10652             {
10653                 tag: 'th',
10654                 cls: 'prev',
10655                 html: '<i class="icon-arrow-left"/>'
10656             },
10657             {
10658                 tag: 'th',
10659                 cls: 'switch',
10660                 colspan: '5'
10661             },
10662             {
10663                 tag: 'th',
10664                 cls: 'next',
10665                 html: '<i class="icon-arrow-right"/>'
10666             }
10667
10668             ]
10669         }
10670         ]
10671     },
10672     
10673     content : {
10674         tag: 'tbody',
10675         cn: [
10676         {
10677             tag: 'tr',
10678             cn: [
10679             {
10680                 tag: 'td',
10681                 colspan: '7'
10682             }
10683             ]
10684         }
10685         ]
10686     },
10687     
10688     footer : {
10689         tag: 'tfoot',
10690         cn: [
10691         {
10692             tag: 'tr',
10693             cn: [
10694             {
10695                 tag: 'th',
10696                 colspan: '7',
10697                 cls: 'today'
10698             }
10699                     
10700             ]
10701         }
10702         ]
10703     },
10704     
10705     dates:{
10706         en: {
10707             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
10708             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
10709             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
10710             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
10711             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
10712             today: "Today"
10713         }
10714     },
10715     
10716     modes: [
10717     {
10718         clsName: 'days',
10719         navFnc: 'Month',
10720         navStep: 1
10721     },
10722     {
10723         clsName: 'months',
10724         navFnc: 'FullYear',
10725         navStep: 1
10726     },
10727     {
10728         clsName: 'years',
10729         navFnc: 'FullYear',
10730         navStep: 10
10731     }]
10732 });
10733
10734 Roo.apply(Roo.bootstrap.DateField,  {
10735   
10736     template : {
10737         tag: 'div',
10738         cls: 'datepicker dropdown-menu',
10739         cn: [
10740         {
10741             tag: 'div',
10742             cls: 'datepicker-days',
10743             cn: [
10744             {
10745                 tag: 'table',
10746                 cls: 'table-condensed',
10747                 cn:[
10748                 Roo.bootstrap.DateField.head,
10749                 {
10750                     tag: 'tbody'
10751                 },
10752                 Roo.bootstrap.DateField.footer
10753                 ]
10754             }
10755             ]
10756         },
10757         {
10758             tag: 'div',
10759             cls: 'datepicker-months',
10760             cn: [
10761             {
10762                 tag: 'table',
10763                 cls: 'table-condensed',
10764                 cn:[
10765                 Roo.bootstrap.DateField.head,
10766                 Roo.bootstrap.DateField.content,
10767                 Roo.bootstrap.DateField.footer
10768                 ]
10769             }
10770             ]
10771         },
10772         {
10773             tag: 'div',
10774             cls: 'datepicker-years',
10775             cn: [
10776             {
10777                 tag: 'table',
10778                 cls: 'table-condensed',
10779                 cn:[
10780                 Roo.bootstrap.DateField.head,
10781                 Roo.bootstrap.DateField.content,
10782                 Roo.bootstrap.DateField.footer
10783                 ]
10784             }
10785             ]
10786         }
10787         ]
10788     }
10789 });
10790
10791  
10792
10793  /*
10794  * - LGPL
10795  *
10796  * CheckBox
10797  * 
10798  */
10799
10800 /**
10801  * @class Roo.bootstrap.CheckBox
10802  * @extends Roo.bootstrap.Input
10803  * Bootstrap CheckBox class
10804  * 
10805  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
10806  * @cfg {String} boxLabel The text that appears beside the checkbox
10807  * @cfg {Boolean} checked initnal the element
10808  * 
10809  * @constructor
10810  * Create a new CheckBox
10811  * @param {Object} config The config object
10812  */
10813
10814 Roo.bootstrap.CheckBox = function(config){
10815     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
10816    
10817         this.addEvents({
10818             /**
10819             * @event check
10820             * Fires when the element is checked or unchecked.
10821             * @param {Roo.bootstrap.CheckBox} this This input
10822             * @param {Boolean} checked The new checked value
10823             */
10824            check : true
10825         });
10826 };
10827
10828 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
10829     
10830     inputType: 'checkbox',
10831     value: 1,
10832     valueOff: 0,
10833     boxLabel: false,
10834     checked: false,
10835     
10836     getAutoCreate : function()
10837     {
10838         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10839         
10840         var id = Roo.id();
10841         
10842         var cfg = {};
10843         
10844         cfg.cls = 'form-group' //input-group
10845         
10846         var input =  {
10847             tag: 'input',
10848             id : id,
10849             type : this.inputType,
10850             value : (!this.checked) ? this.valueOff : this.value,
10851             cls : 'form-box',
10852             placeholder : this.placeholder || ''
10853             
10854         };
10855         
10856         if (this.disabled) {
10857             input.disabled=true;
10858         }
10859         
10860         if(this.checked){
10861             input.checked = this.checked;
10862         }
10863         
10864         if (this.name) {
10865             input.name = this.name;
10866         }
10867         
10868         if (this.size) {
10869             input.cls += ' input-' + this.size;
10870         }
10871         
10872         var settings=this;
10873         ['xs','sm','md','lg'].map(function(size){
10874             if (settings[size]) {
10875                 cfg.cls += ' col-' + size + '-' + settings[size];
10876             }
10877         });
10878         
10879         var inputblock = input;
10880         
10881         if (this.before || this.after) {
10882             
10883             inputblock = {
10884                 cls : 'input-group',
10885                 cn :  [] 
10886             };
10887             if (this.before) {
10888                 inputblock.cn.push({
10889                     tag :'span',
10890                     cls : 'input-group-addon',
10891                     html : this.before
10892                 });
10893             }
10894             inputblock.cn.push(input);
10895             if (this.after) {
10896                 inputblock.cn.push({
10897                     tag :'span',
10898                     cls : 'input-group-addon',
10899                     html : this.after
10900                 });
10901             }
10902             
10903         };
10904         
10905         if (align ==='left' && this.fieldLabel.length) {
10906                 Roo.log("left and has label");
10907                 cfg.cn = [
10908                     
10909                     {
10910                         tag: 'label',
10911                         'for' :  id,
10912                         cls : 'control-label col-md-' + this.labelWidth,
10913                         html : this.fieldLabel
10914                         
10915                     },
10916                     {
10917                         cls : "col-md-" + (12 - this.labelWidth), 
10918                         cn: [
10919                             inputblock
10920                         ]
10921                     }
10922                     
10923                 ];
10924         } else if ( this.fieldLabel.length) {
10925                 Roo.log(" label");
10926                  cfg.cn = [
10927                    
10928                     {
10929                         tag: 'label',
10930                         'for': id,
10931                         cls: 'control-label box-input-label',
10932                         //cls : 'input-group-addon',
10933                         html : this.fieldLabel
10934                         
10935                     },
10936                     
10937                     inputblock
10938                     
10939                 ];
10940
10941         } else {
10942             
10943                    Roo.log(" no label && no align");
10944                 cfg.cn = [
10945                     
10946                         inputblock
10947                     
10948                 ];
10949                 
10950                 
10951         };
10952         
10953         if(this.boxLabel){
10954             cfg.cn.push({
10955                 tag: 'span',
10956                 'for': id,
10957                 cls: 'box-label',
10958                 html: this.boxLabel
10959             })
10960         }
10961         
10962         return cfg;
10963         
10964     },
10965     
10966     /**
10967      * return the real input element.
10968      */
10969     inputEl: function ()
10970     {
10971         return this.el.select('input.form-box',true).first();
10972     },
10973     
10974     initEvents : function()
10975     {
10976         Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
10977         
10978         this.inputEl().on('click', this.onClick,  this);
10979         
10980     },
10981     
10982     onClick : function()
10983     {   
10984         this.setChecked(!this.checked);
10985     },
10986     
10987     setChecked : function(state,suppressEvent)
10988     {
10989         this.checked = state;
10990         
10991         if(suppressEvent !== true){
10992             this.fireEvent('check', this, state);
10993         }
10994         
10995         this.inputEl().dom.value = state ? this.value : this.valueOff;
10996         
10997     }
10998 });
10999
11000  
11001 /*
11002  * - LGPL
11003  *
11004  * Radio
11005  * 
11006  */
11007
11008 /**
11009  * @class Roo.bootstrap.Radio
11010  * @extends Roo.bootstrap.CheckBox
11011  * Bootstrap Radio class
11012
11013  * @constructor
11014  * Create a new Radio
11015  * @param {Object} config The config object
11016  */
11017
11018 Roo.bootstrap.Radio = function(config){
11019     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
11020    
11021 };
11022
11023 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
11024     
11025     inputType: 'radio',
11026     
11027     getAutoCreate : function()
11028     {
11029         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11030         
11031         var id = Roo.id();
11032         
11033         var cfg = {};
11034         
11035         cfg.cls = 'form-group' //input-group
11036         
11037         var input =  {
11038             tag: 'input',
11039             id : id,
11040             type : this.inputType,
11041             value : (!this.checked) ? this.valueOff : this.value,
11042             cls : 'form-box',
11043             placeholder : this.placeholder || ''
11044             
11045         };
11046         
11047         if (this.disabled) {
11048             input.disabled=true;
11049         }
11050         
11051         if(this.checked){
11052             input.checked = this.checked;
11053         }
11054         
11055         if (this.name) {
11056             input.name = this.name;
11057         }
11058         
11059         if (this.size) {
11060             input.cls += ' input-' + this.size;
11061         }
11062         
11063         var settings=this;
11064         ['xs','sm','md','lg'].map(function(size){
11065             if (settings[size]) {
11066                 cfg.cls += ' col-' + size + '-' + settings[size];
11067             }
11068         });
11069         
11070         var inputblock = input;
11071         
11072         if (this.before || this.after) {
11073             
11074             inputblock = {
11075                 cls : 'input-group',
11076                 cn :  [] 
11077             };
11078             if (this.before) {
11079                 inputblock.cn.push({
11080                     tag :'span',
11081                     cls : 'input-group-addon',
11082                     html : this.before
11083                 });
11084             }
11085             inputblock.cn.push(input);
11086             if (this.after) {
11087                 inputblock.cn.push({
11088                     tag :'span',
11089                     cls : 'input-group-addon',
11090                     html : this.after
11091                 });
11092             }
11093             
11094         };
11095         
11096         if (align ==='left' && this.fieldLabel.length) {
11097                 Roo.log("left and has label");
11098                 cfg.cn = [
11099                     
11100                     {
11101                         tag: 'label',
11102                         'for' :  id,
11103                         cls : 'control-label col-md-' + this.labelWidth,
11104                         html : this.fieldLabel
11105                         
11106                     },
11107                     {
11108                         cls : "col-md-" + (12 - this.labelWidth), 
11109                         cn: [
11110                             inputblock
11111                         ]
11112                     }
11113                     
11114                 ];
11115         } else if ( this.fieldLabel.length) {
11116                 Roo.log(" label");
11117                  cfg.cn = [
11118                    
11119                     {
11120                         tag: 'label',
11121                         'for': id,
11122                         cls: 'control-label box-input-label',
11123                         //cls : 'input-group-addon',
11124                         html : this.fieldLabel
11125                         
11126                     },
11127                     
11128                     inputblock
11129                     
11130                 ];
11131
11132         } else {
11133             
11134                    Roo.log(" no label && no align");
11135                 cfg.cn = [
11136                     
11137                         inputblock
11138                     
11139                 ];
11140                 
11141                 
11142         };
11143         
11144         if(this.boxLabel){
11145             cfg.cn.push({
11146                 tag: 'span',
11147                 'for': id,
11148                 cls: 'box-label',
11149                 html: this.boxLabel
11150             })
11151         }
11152         
11153         return cfg;
11154         
11155     },
11156     
11157     onClick : function()
11158     {   
11159         this.setChecked(true);
11160     },
11161     
11162     setChecked : function(state,suppressEvent)
11163     {
11164         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
11165             v.checked = false;
11166         });
11167         
11168         this.checked = state;
11169         
11170         if(suppressEvent !== true){
11171             this.fireEvent('check', this, state);
11172         }
11173         
11174         this.inputEl().dom.value = state ? this.value : this.valueOff;
11175         
11176     },
11177     
11178     getGroupValue : function()
11179     {
11180         if(typeof(this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true)) == 'undefined'){
11181             return '';
11182         }
11183         
11184         return this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true).value;
11185     },
11186     
11187     /**
11188      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11189      * @return {Mixed} value The field value
11190      */
11191     getValue : function(){
11192         return this.getGroupValue();
11193     }
11194     
11195 });
11196
11197  
11198 /*
11199  * - LGPL
11200  *
11201  * HtmlEditor
11202  * 
11203  */
11204
11205 /**
11206  * @class Roo.bootstrap.HtmlEditor
11207  * @extends Roo.bootstrap.Component
11208  * Bootstrap HtmlEditor class
11209
11210  * @constructor
11211  * Create a new HtmlEditor
11212  * @param {Object} config The config object
11213  */
11214
11215 Roo.bootstrap.HtmlEditor = function(config){
11216     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
11217     
11218 };
11219
11220
11221 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.Component,  {
11222     
11223     getAutoCreate : function()
11224     {
11225 //        var cfg = {};
11226 //        
11227 //        var toolbar = new Roo.bootstrap.ButtonGroup({
11228 //            toolbar: true,
11229 //            cn: [
11230 //                {
11231 //                    new Roo.bootstrap.Button({
11232 //                        
11233 //                    })
11234 //                }
11235 //            ]
11236 //        });
11237 //        
11238 //        return cfg;
11239         
11240     }
11241 });
11242
11243 /**
11244  * @class Roo.bootstrap.Table.AbstractSelectionModel
11245  * @extends Roo.util.Observable
11246  * Abstract base class for grid SelectionModels.  It provides the interface that should be
11247  * implemented by descendant classes.  This class should not be directly instantiated.
11248  * @constructor
11249  */
11250 Roo.bootstrap.Table.AbstractSelectionModel = function(){
11251     this.locked = false;
11252     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
11253 };
11254
11255
11256 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
11257     /** @ignore Called by the grid automatically. Do not call directly. */
11258     init : function(grid){
11259         this.grid = grid;
11260         this.initEvents();
11261     },
11262
11263     /**
11264      * Locks the selections.
11265      */
11266     lock : function(){
11267         this.locked = true;
11268     },
11269
11270     /**
11271      * Unlocks the selections.
11272      */
11273     unlock : function(){
11274         this.locked = false;
11275     },
11276
11277     /**
11278      * Returns true if the selections are locked.
11279      * @return {Boolean}
11280      */
11281     isLocked : function(){
11282         return this.locked;
11283     }
11284 });
11285 /**
11286  * @class Roo.bootstrap.Table.ColumnModel
11287  * @extends Roo.util.Observable
11288  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
11289  * the columns in the table.
11290  
11291  * @constructor
11292  * @param {Object} config An Array of column config objects. See this class's
11293  * config objects for details.
11294 */
11295 Roo.bootstrap.Table.ColumnModel = function(config){
11296         /**
11297      * The config passed into the constructor
11298      */
11299     this.config = config;
11300     this.lookup = {};
11301
11302     // if no id, create one
11303     // if the column does not have a dataIndex mapping,
11304     // map it to the order it is in the config
11305     for(var i = 0, len = config.length; i < len; i++){
11306         var c = config[i];
11307         if(typeof c.dataIndex == "undefined"){
11308             c.dataIndex = i;
11309         }
11310         if(typeof c.renderer == "string"){
11311             c.renderer = Roo.util.Format[c.renderer];
11312         }
11313         if(typeof c.id == "undefined"){
11314             c.id = Roo.id();
11315         }
11316 //        if(c.editor && c.editor.xtype){
11317 //            c.editor  = Roo.factory(c.editor, Roo.grid);
11318 //        }
11319 //        if(c.editor && c.editor.isFormField){
11320 //            c.editor = new Roo.grid.GridEditor(c.editor);
11321 //        }
11322
11323         this.lookup[c.id] = c;
11324     }
11325
11326     /**
11327      * The width of columns which have no width specified (defaults to 100)
11328      * @type Number
11329      */
11330     this.defaultWidth = 100;
11331
11332     /**
11333      * Default sortable of columns which have no sortable specified (defaults to false)
11334      * @type Boolean
11335      */
11336     this.defaultSortable = false;
11337
11338     this.addEvents({
11339         /**
11340              * @event widthchange
11341              * Fires when the width of a column changes.
11342              * @param {ColumnModel} this
11343              * @param {Number} columnIndex The column index
11344              * @param {Number} newWidth The new width
11345              */
11346             "widthchange": true,
11347         /**
11348              * @event headerchange
11349              * Fires when the text of a header changes.
11350              * @param {ColumnModel} this
11351              * @param {Number} columnIndex The column index
11352              * @param {Number} newText The new header text
11353              */
11354             "headerchange": true,
11355         /**
11356              * @event hiddenchange
11357              * Fires when a column is hidden or "unhidden".
11358              * @param {ColumnModel} this
11359              * @param {Number} columnIndex The column index
11360              * @param {Boolean} hidden true if hidden, false otherwise
11361              */
11362             "hiddenchange": true,
11363             /**
11364          * @event columnmoved
11365          * Fires when a column is moved.
11366          * @param {ColumnModel} this
11367          * @param {Number} oldIndex
11368          * @param {Number} newIndex
11369          */
11370         "columnmoved" : true,
11371         /**
11372          * @event columlockchange
11373          * Fires when a column's locked state is changed
11374          * @param {ColumnModel} this
11375          * @param {Number} colIndex
11376          * @param {Boolean} locked true if locked
11377          */
11378         "columnlockchange" : true
11379     });
11380     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
11381 };
11382 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
11383     /**
11384      * @cfg {String} header The header text to display in the Grid view.
11385      */
11386     /**
11387      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
11388      * {@link Roo.data.Record} definition from which to draw the column's value. If not
11389      * specified, the column's index is used as an index into the Record's data Array.
11390      */
11391     /**
11392      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
11393      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
11394      */
11395     /**
11396      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
11397      * Defaults to the value of the {@link #defaultSortable} property.
11398      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
11399      */
11400     /**
11401      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
11402      */
11403     /**
11404      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
11405      */
11406     /**
11407      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
11408      */
11409     /**
11410      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
11411      */
11412     /**
11413      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
11414      * given the cell's data value. See {@link #setRenderer}. If not specified, the
11415      * default renderer uses the raw data value.
11416      */
11417     /**
11418      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
11419      */
11420
11421     /**
11422      * Returns the id of the column at the specified index.
11423      * @param {Number} index The column index
11424      * @return {String} the id
11425      */
11426     getColumnId : function(index){
11427         return this.config[index].id;
11428     },
11429
11430     /**
11431      * Returns the column for a specified id.
11432      * @param {String} id The column id
11433      * @return {Object} the column
11434      */
11435     getColumnById : function(id){
11436         return this.lookup[id];
11437     },
11438
11439     
11440     /**
11441      * Returns the column for a specified dataIndex.
11442      * @param {String} dataIndex The column dataIndex
11443      * @return {Object|Boolean} the column or false if not found
11444      */
11445     getColumnByDataIndex: function(dataIndex){
11446         var index = this.findColumnIndex(dataIndex);
11447         return index > -1 ? this.config[index] : false;
11448     },
11449     
11450     /**
11451      * Returns the index for a specified column id.
11452      * @param {String} id The column id
11453      * @return {Number} the index, or -1 if not found
11454      */
11455     getIndexById : function(id){
11456         for(var i = 0, len = this.config.length; i < len; i++){
11457             if(this.config[i].id == id){
11458                 return i;
11459             }
11460         }
11461         return -1;
11462     },
11463     
11464     /**
11465      * Returns the index for a specified column dataIndex.
11466      * @param {String} dataIndex The column dataIndex
11467      * @return {Number} the index, or -1 if not found
11468      */
11469     
11470     findColumnIndex : function(dataIndex){
11471         for(var i = 0, len = this.config.length; i < len; i++){
11472             if(this.config[i].dataIndex == dataIndex){
11473                 return i;
11474             }
11475         }
11476         return -1;
11477     },
11478     
11479     
11480     moveColumn : function(oldIndex, newIndex){
11481         var c = this.config[oldIndex];
11482         this.config.splice(oldIndex, 1);
11483         this.config.splice(newIndex, 0, c);
11484         this.dataMap = null;
11485         this.fireEvent("columnmoved", this, oldIndex, newIndex);
11486     },
11487
11488     isLocked : function(colIndex){
11489         return this.config[colIndex].locked === true;
11490     },
11491
11492     setLocked : function(colIndex, value, suppressEvent){
11493         if(this.isLocked(colIndex) == value){
11494             return;
11495         }
11496         this.config[colIndex].locked = value;
11497         if(!suppressEvent){
11498             this.fireEvent("columnlockchange", this, colIndex, value);
11499         }
11500     },
11501
11502     getTotalLockedWidth : function(){
11503         var totalWidth = 0;
11504         for(var i = 0; i < this.config.length; i++){
11505             if(this.isLocked(i) && !this.isHidden(i)){
11506                 this.totalWidth += this.getColumnWidth(i);
11507             }
11508         }
11509         return totalWidth;
11510     },
11511
11512     getLockedCount : function(){
11513         for(var i = 0, len = this.config.length; i < len; i++){
11514             if(!this.isLocked(i)){
11515                 return i;
11516             }
11517         }
11518     },
11519
11520     /**
11521      * Returns the number of columns.
11522      * @return {Number}
11523      */
11524     getColumnCount : function(visibleOnly){
11525         if(visibleOnly === true){
11526             var c = 0;
11527             for(var i = 0, len = this.config.length; i < len; i++){
11528                 if(!this.isHidden(i)){
11529                     c++;
11530                 }
11531             }
11532             return c;
11533         }
11534         return this.config.length;
11535     },
11536
11537     /**
11538      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
11539      * @param {Function} fn
11540      * @param {Object} scope (optional)
11541      * @return {Array} result
11542      */
11543     getColumnsBy : function(fn, scope){
11544         var r = [];
11545         for(var i = 0, len = this.config.length; i < len; i++){
11546             var c = this.config[i];
11547             if(fn.call(scope||this, c, i) === true){
11548                 r[r.length] = c;
11549             }
11550         }
11551         return r;
11552     },
11553
11554     /**
11555      * Returns true if the specified column is sortable.
11556      * @param {Number} col The column index
11557      * @return {Boolean}
11558      */
11559     isSortable : function(col){
11560         if(typeof this.config[col].sortable == "undefined"){
11561             return this.defaultSortable;
11562         }
11563         return this.config[col].sortable;
11564     },
11565
11566     /**
11567      * Returns the rendering (formatting) function defined for the column.
11568      * @param {Number} col The column index.
11569      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
11570      */
11571     getRenderer : function(col){
11572         if(!this.config[col].renderer){
11573             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
11574         }
11575         return this.config[col].renderer;
11576     },
11577
11578     /**
11579      * Sets the rendering (formatting) function for a column.
11580      * @param {Number} col The column index
11581      * @param {Function} fn The function to use to process the cell's raw data
11582      * to return HTML markup for the grid view. The render function is called with
11583      * the following parameters:<ul>
11584      * <li>Data value.</li>
11585      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
11586      * <li>css A CSS style string to apply to the table cell.</li>
11587      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
11588      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
11589      * <li>Row index</li>
11590      * <li>Column index</li>
11591      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
11592      */
11593     setRenderer : function(col, fn){
11594         this.config[col].renderer = fn;
11595     },
11596
11597     /**
11598      * Returns the width for the specified column.
11599      * @param {Number} col The column index
11600      * @return {Number}
11601      */
11602     getColumnWidth : function(col){
11603         return this.config[col].width * 1 || this.defaultWidth;
11604     },
11605
11606     /**
11607      * Sets the width for a column.
11608      * @param {Number} col The column index
11609      * @param {Number} width The new width
11610      */
11611     setColumnWidth : function(col, width, suppressEvent){
11612         this.config[col].width = width;
11613         this.totalWidth = null;
11614         if(!suppressEvent){
11615              this.fireEvent("widthchange", this, col, width);
11616         }
11617     },
11618
11619     /**
11620      * Returns the total width of all columns.
11621      * @param {Boolean} includeHidden True to include hidden column widths
11622      * @return {Number}
11623      */
11624     getTotalWidth : function(includeHidden){
11625         if(!this.totalWidth){
11626             this.totalWidth = 0;
11627             for(var i = 0, len = this.config.length; i < len; i++){
11628                 if(includeHidden || !this.isHidden(i)){
11629                     this.totalWidth += this.getColumnWidth(i);
11630                 }
11631             }
11632         }
11633         return this.totalWidth;
11634     },
11635
11636     /**
11637      * Returns the header for the specified column.
11638      * @param {Number} col The column index
11639      * @return {String}
11640      */
11641     getColumnHeader : function(col){
11642         return this.config[col].header;
11643     },
11644
11645     /**
11646      * Sets the header for a column.
11647      * @param {Number} col The column index
11648      * @param {String} header The new header
11649      */
11650     setColumnHeader : function(col, header){
11651         this.config[col].header = header;
11652         this.fireEvent("headerchange", this, col, header);
11653     },
11654
11655     /**
11656      * Returns the tooltip for the specified column.
11657      * @param {Number} col The column index
11658      * @return {String}
11659      */
11660     getColumnTooltip : function(col){
11661             return this.config[col].tooltip;
11662     },
11663     /**
11664      * Sets the tooltip for a column.
11665      * @param {Number} col The column index
11666      * @param {String} tooltip The new tooltip
11667      */
11668     setColumnTooltip : function(col, tooltip){
11669             this.config[col].tooltip = tooltip;
11670     },
11671
11672     /**
11673      * Returns the dataIndex for the specified column.
11674      * @param {Number} col The column index
11675      * @return {Number}
11676      */
11677     getDataIndex : function(col){
11678         return this.config[col].dataIndex;
11679     },
11680
11681     /**
11682      * Sets the dataIndex for a column.
11683      * @param {Number} col The column index
11684      * @param {Number} dataIndex The new dataIndex
11685      */
11686     setDataIndex : function(col, dataIndex){
11687         this.config[col].dataIndex = dataIndex;
11688     },
11689
11690     
11691     
11692     /**
11693      * Returns true if the cell is editable.
11694      * @param {Number} colIndex The column index
11695      * @param {Number} rowIndex The row index
11696      * @return {Boolean}
11697      */
11698     isCellEditable : function(colIndex, rowIndex){
11699         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
11700     },
11701
11702     /**
11703      * Returns the editor defined for the cell/column.
11704      * return false or null to disable editing.
11705      * @param {Number} colIndex The column index
11706      * @param {Number} rowIndex The row index
11707      * @return {Object}
11708      */
11709     getCellEditor : function(colIndex, rowIndex){
11710         return this.config[colIndex].editor;
11711     },
11712
11713     /**
11714      * Sets if a column is editable.
11715      * @param {Number} col The column index
11716      * @param {Boolean} editable True if the column is editable
11717      */
11718     setEditable : function(col, editable){
11719         this.config[col].editable = editable;
11720     },
11721
11722
11723     /**
11724      * Returns true if the column is hidden.
11725      * @param {Number} colIndex The column index
11726      * @return {Boolean}
11727      */
11728     isHidden : function(colIndex){
11729         return this.config[colIndex].hidden;
11730     },
11731
11732
11733     /**
11734      * Returns true if the column width cannot be changed
11735      */
11736     isFixed : function(colIndex){
11737         return this.config[colIndex].fixed;
11738     },
11739
11740     /**
11741      * Returns true if the column can be resized
11742      * @return {Boolean}
11743      */
11744     isResizable : function(colIndex){
11745         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
11746     },
11747     /**
11748      * Sets if a column is hidden.
11749      * @param {Number} colIndex The column index
11750      * @param {Boolean} hidden True if the column is hidden
11751      */
11752     setHidden : function(colIndex, hidden){
11753         this.config[colIndex].hidden = hidden;
11754         this.totalWidth = null;
11755         this.fireEvent("hiddenchange", this, colIndex, hidden);
11756     },
11757
11758     /**
11759      * Sets the editor for a column.
11760      * @param {Number} col The column index
11761      * @param {Object} editor The editor object
11762      */
11763     setEditor : function(col, editor){
11764         this.config[col].editor = editor;
11765     }
11766 });
11767
11768 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
11769         if(typeof value == "string" && value.length < 1){
11770             return "&#160;";
11771         }
11772         return value;
11773 };
11774
11775 // Alias for backwards compatibility
11776 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
11777
11778 /**
11779  * @extends Roo.bootstrap.Table.AbstractSelectionModel
11780  * @class Roo.bootstrap.Table.RowSelectionModel
11781  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
11782  * It supports multiple selections and keyboard selection/navigation. 
11783  * @constructor
11784  * @param {Object} config
11785  */
11786
11787 Roo.bootstrap.Table.RowSelectionModel = function(config){
11788     Roo.apply(this, config);
11789     this.selections = new Roo.util.MixedCollection(false, function(o){
11790         return o.id;
11791     });
11792
11793     this.last = false;
11794     this.lastActive = false;
11795
11796     this.addEvents({
11797         /**
11798              * @event selectionchange
11799              * Fires when the selection changes
11800              * @param {SelectionModel} this
11801              */
11802             "selectionchange" : true,
11803         /**
11804              * @event afterselectionchange
11805              * Fires after the selection changes (eg. by key press or clicking)
11806              * @param {SelectionModel} this
11807              */
11808             "afterselectionchange" : true,
11809         /**
11810              * @event beforerowselect
11811              * Fires when a row is selected being selected, return false to cancel.
11812              * @param {SelectionModel} this
11813              * @param {Number} rowIndex The selected index
11814              * @param {Boolean} keepExisting False if other selections will be cleared
11815              */
11816             "beforerowselect" : true,
11817         /**
11818              * @event rowselect
11819              * Fires when a row is selected.
11820              * @param {SelectionModel} this
11821              * @param {Number} rowIndex The selected index
11822              * @param {Roo.data.Record} r The record
11823              */
11824             "rowselect" : true,
11825         /**
11826              * @event rowdeselect
11827              * Fires when a row is deselected.
11828              * @param {SelectionModel} this
11829              * @param {Number} rowIndex The selected index
11830              */
11831         "rowdeselect" : true
11832     });
11833     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
11834     this.locked = false;
11835 };
11836
11837 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
11838     /**
11839      * @cfg {Boolean} singleSelect
11840      * True to allow selection of only one row at a time (defaults to false)
11841      */
11842     singleSelect : false,
11843
11844     // private
11845     initEvents : function(){
11846
11847         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
11848             this.grid.on("mousedown", this.handleMouseDown, this);
11849         }else{ // allow click to work like normal
11850             this.grid.on("rowclick", this.handleDragableRowClick, this);
11851         }
11852
11853         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
11854             "up" : function(e){
11855                 if(!e.shiftKey){
11856                     this.selectPrevious(e.shiftKey);
11857                 }else if(this.last !== false && this.lastActive !== false){
11858                     var last = this.last;
11859                     this.selectRange(this.last,  this.lastActive-1);
11860                     this.grid.getView().focusRow(this.lastActive);
11861                     if(last !== false){
11862                         this.last = last;
11863                     }
11864                 }else{
11865                     this.selectFirstRow();
11866                 }
11867                 this.fireEvent("afterselectionchange", this);
11868             },
11869             "down" : function(e){
11870                 if(!e.shiftKey){
11871                     this.selectNext(e.shiftKey);
11872                 }else if(this.last !== false && this.lastActive !== false){
11873                     var last = this.last;
11874                     this.selectRange(this.last,  this.lastActive+1);
11875                     this.grid.getView().focusRow(this.lastActive);
11876                     if(last !== false){
11877                         this.last = last;
11878                     }
11879                 }else{
11880                     this.selectFirstRow();
11881                 }
11882                 this.fireEvent("afterselectionchange", this);
11883             },
11884             scope: this
11885         });
11886
11887         var view = this.grid.view;
11888         view.on("refresh", this.onRefresh, this);
11889         view.on("rowupdated", this.onRowUpdated, this);
11890         view.on("rowremoved", this.onRemove, this);
11891     },
11892
11893     // private
11894     onRefresh : function(){
11895         var ds = this.grid.dataSource, i, v = this.grid.view;
11896         var s = this.selections;
11897         s.each(function(r){
11898             if((i = ds.indexOfId(r.id)) != -1){
11899                 v.onRowSelect(i);
11900             }else{
11901                 s.remove(r);
11902             }
11903         });
11904     },
11905
11906     // private
11907     onRemove : function(v, index, r){
11908         this.selections.remove(r);
11909     },
11910
11911     // private
11912     onRowUpdated : function(v, index, r){
11913         if(this.isSelected(r)){
11914             v.onRowSelect(index);
11915         }
11916     },
11917
11918     /**
11919      * Select records.
11920      * @param {Array} records The records to select
11921      * @param {Boolean} keepExisting (optional) True to keep existing selections
11922      */
11923     selectRecords : function(records, keepExisting){
11924         if(!keepExisting){
11925             this.clearSelections();
11926         }
11927         var ds = this.grid.dataSource;
11928         for(var i = 0, len = records.length; i < len; i++){
11929             this.selectRow(ds.indexOf(records[i]), true);
11930         }
11931     },
11932
11933     /**
11934      * Gets the number of selected rows.
11935      * @return {Number}
11936      */
11937     getCount : function(){
11938         return this.selections.length;
11939     },
11940
11941     /**
11942      * Selects the first row in the grid.
11943      */
11944     selectFirstRow : function(){
11945         this.selectRow(0);
11946     },
11947
11948     /**
11949      * Select the last row.
11950      * @param {Boolean} keepExisting (optional) True to keep existing selections
11951      */
11952     selectLastRow : function(keepExisting){
11953         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
11954     },
11955
11956     /**
11957      * Selects the row immediately following the last selected row.
11958      * @param {Boolean} keepExisting (optional) True to keep existing selections
11959      */
11960     selectNext : function(keepExisting){
11961         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
11962             this.selectRow(this.last+1, keepExisting);
11963             this.grid.getView().focusRow(this.last);
11964         }
11965     },
11966
11967     /**
11968      * Selects the row that precedes the last selected row.
11969      * @param {Boolean} keepExisting (optional) True to keep existing selections
11970      */
11971     selectPrevious : function(keepExisting){
11972         if(this.last){
11973             this.selectRow(this.last-1, keepExisting);
11974             this.grid.getView().focusRow(this.last);
11975         }
11976     },
11977
11978     /**
11979      * Returns the selected records
11980      * @return {Array} Array of selected records
11981      */
11982     getSelections : function(){
11983         return [].concat(this.selections.items);
11984     },
11985
11986     /**
11987      * Returns the first selected record.
11988      * @return {Record}
11989      */
11990     getSelected : function(){
11991         return this.selections.itemAt(0);
11992     },
11993
11994
11995     /**
11996      * Clears all selections.
11997      */
11998     clearSelections : function(fast){
11999         if(this.locked) return;
12000         if(fast !== true){
12001             var ds = this.grid.dataSource;
12002             var s = this.selections;
12003             s.each(function(r){
12004                 this.deselectRow(ds.indexOfId(r.id));
12005             }, this);
12006             s.clear();
12007         }else{
12008             this.selections.clear();
12009         }
12010         this.last = false;
12011     },
12012
12013
12014     /**
12015      * Selects all rows.
12016      */
12017     selectAll : function(){
12018         if(this.locked) return;
12019         this.selections.clear();
12020         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
12021             this.selectRow(i, true);
12022         }
12023     },
12024
12025     /**
12026      * Returns True if there is a selection.
12027      * @return {Boolean}
12028      */
12029     hasSelection : function(){
12030         return this.selections.length > 0;
12031     },
12032
12033     /**
12034      * Returns True if the specified row is selected.
12035      * @param {Number/Record} record The record or index of the record to check
12036      * @return {Boolean}
12037      */
12038     isSelected : function(index){
12039         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
12040         return (r && this.selections.key(r.id) ? true : false);
12041     },
12042
12043     /**
12044      * Returns True if the specified record id is selected.
12045      * @param {String} id The id of record to check
12046      * @return {Boolean}
12047      */
12048     isIdSelected : function(id){
12049         return (this.selections.key(id) ? true : false);
12050     },
12051
12052     // private
12053     handleMouseDown : function(e, t){
12054         var view = this.grid.getView(), rowIndex;
12055         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
12056             return;
12057         };
12058         if(e.shiftKey && this.last !== false){
12059             var last = this.last;
12060             this.selectRange(last, rowIndex, e.ctrlKey);
12061             this.last = last; // reset the last
12062             view.focusRow(rowIndex);
12063         }else{
12064             var isSelected = this.isSelected(rowIndex);
12065             if(e.button !== 0 && isSelected){
12066                 view.focusRow(rowIndex);
12067             }else if(e.ctrlKey && isSelected){
12068                 this.deselectRow(rowIndex);
12069             }else if(!isSelected){
12070                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
12071                 view.focusRow(rowIndex);
12072             }
12073         }
12074         this.fireEvent("afterselectionchange", this);
12075     },
12076     // private
12077     handleDragableRowClick :  function(grid, rowIndex, e) 
12078     {
12079         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
12080             this.selectRow(rowIndex, false);
12081             grid.view.focusRow(rowIndex);
12082              this.fireEvent("afterselectionchange", this);
12083         }
12084     },
12085     
12086     /**
12087      * Selects multiple rows.
12088      * @param {Array} rows Array of the indexes of the row to select
12089      * @param {Boolean} keepExisting (optional) True to keep existing selections
12090      */
12091     selectRows : function(rows, keepExisting){
12092         if(!keepExisting){
12093             this.clearSelections();
12094         }
12095         for(var i = 0, len = rows.length; i < len; i++){
12096             this.selectRow(rows[i], true);
12097         }
12098     },
12099
12100     /**
12101      * Selects a range of rows. All rows in between startRow and endRow are also selected.
12102      * @param {Number} startRow The index of the first row in the range
12103      * @param {Number} endRow The index of the last row in the range
12104      * @param {Boolean} keepExisting (optional) True to retain existing selections
12105      */
12106     selectRange : function(startRow, endRow, keepExisting){
12107         if(this.locked) return;
12108         if(!keepExisting){
12109             this.clearSelections();
12110         }
12111         if(startRow <= endRow){
12112             for(var i = startRow; i <= endRow; i++){
12113                 this.selectRow(i, true);
12114             }
12115         }else{
12116             for(var i = startRow; i >= endRow; i--){
12117                 this.selectRow(i, true);
12118             }
12119         }
12120     },
12121
12122     /**
12123      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
12124      * @param {Number} startRow The index of the first row in the range
12125      * @param {Number} endRow The index of the last row in the range
12126      */
12127     deselectRange : function(startRow, endRow, preventViewNotify){
12128         if(this.locked) return;
12129         for(var i = startRow; i <= endRow; i++){
12130             this.deselectRow(i, preventViewNotify);
12131         }
12132     },
12133
12134     /**
12135      * Selects a row.
12136      * @param {Number} row The index of the row to select
12137      * @param {Boolean} keepExisting (optional) True to keep existing selections
12138      */
12139     selectRow : function(index, keepExisting, preventViewNotify){
12140         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
12141         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
12142             if(!keepExisting || this.singleSelect){
12143                 this.clearSelections();
12144             }
12145             var r = this.grid.dataSource.getAt(index);
12146             this.selections.add(r);
12147             this.last = this.lastActive = index;
12148             if(!preventViewNotify){
12149                 this.grid.getView().onRowSelect(index);
12150             }
12151             this.fireEvent("rowselect", this, index, r);
12152             this.fireEvent("selectionchange", this);
12153         }
12154     },
12155
12156     /**
12157      * Deselects a row.
12158      * @param {Number} row The index of the row to deselect
12159      */
12160     deselectRow : function(index, preventViewNotify){
12161         if(this.locked) return;
12162         if(this.last == index){
12163             this.last = false;
12164         }
12165         if(this.lastActive == index){
12166             this.lastActive = false;
12167         }
12168         var r = this.grid.dataSource.getAt(index);
12169         this.selections.remove(r);
12170         if(!preventViewNotify){
12171             this.grid.getView().onRowDeselect(index);
12172         }
12173         this.fireEvent("rowdeselect", this, index);
12174         this.fireEvent("selectionchange", this);
12175     },
12176
12177     // private
12178     restoreLast : function(){
12179         if(this._last){
12180             this.last = this._last;
12181         }
12182     },
12183
12184     // private
12185     acceptsNav : function(row, col, cm){
12186         return !cm.isHidden(col) && cm.isCellEditable(col, row);
12187     },
12188
12189     // private
12190     onEditorKey : function(field, e){
12191         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
12192         if(k == e.TAB){
12193             e.stopEvent();
12194             ed.completeEdit();
12195             if(e.shiftKey){
12196                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
12197             }else{
12198                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
12199             }
12200         }else if(k == e.ENTER && !e.ctrlKey){
12201             e.stopEvent();
12202             ed.completeEdit();
12203             if(e.shiftKey){
12204                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
12205             }else{
12206                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
12207             }
12208         }else if(k == e.ESC){
12209             ed.cancelEdit();
12210         }
12211         if(newCell){
12212             g.startEditing(newCell[0], newCell[1]);
12213         }
12214     }
12215 });