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