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