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         if(this.pageSize){
7375             this.footer = this.list.createChild({cls:cls+'-ft'});
7376             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
7377                     {pageSize: this.pageSize});
7378             
7379         }
7380         
7381         if (this.pageTb && this.allowBlank && !this.disableClear) {
7382             var _this = this;
7383             this.pageTb.add(new Roo.Toolbar.Fill(), {
7384                 cls: 'x-btn-icon x-btn-clear',
7385                 text: '&#160;',
7386                 handler: function()
7387                 {
7388                     _this.collapse();
7389                     _this.clearValue();
7390                     _this.onSelect(false, -1);
7391                 }
7392             });
7393         }
7394         if (this.footer) {
7395             this.assetHeight += this.footer.getHeight();
7396         }
7397         */
7398             
7399         if(!this.tpl){
7400             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
7401         }
7402
7403         this.view = new Roo.View(this.el.select('ul',true).first(), this.tpl, {
7404             singleSelect:true, store: this.store, selectedClass: this.selectedClass
7405         });
7406         //this.view.wrapEl.setDisplayed(false);
7407         this.view.on('click', this.onViewClick, this);
7408         
7409         
7410         
7411         this.store.on('beforeload', this.onBeforeLoad, this);
7412         this.store.on('load', this.onLoad, this);
7413         this.store.on('loadexception', this.onLoadException, this);
7414         /*
7415         if(this.resizable){
7416             this.resizer = new Roo.Resizable(this.list,  {
7417                pinned:true, handles:'se'
7418             });
7419             this.resizer.on('resize', function(r, w, h){
7420                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
7421                 this.listWidth = w;
7422                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
7423                 this.restrictHeight();
7424             }, this);
7425             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
7426         }
7427         */
7428         if(!this.editable){
7429             this.editable = true;
7430             this.setEditable(false);
7431         }
7432         
7433         /*
7434         
7435         if (typeof(this.events.add.listeners) != 'undefined') {
7436             
7437             this.addicon = this.wrap.createChild(
7438                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
7439        
7440             this.addicon.on('click', function(e) {
7441                 this.fireEvent('add', this);
7442             }, this);
7443         }
7444         if (typeof(this.events.edit.listeners) != 'undefined') {
7445             
7446             this.editicon = this.wrap.createChild(
7447                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
7448             if (this.addicon) {
7449                 this.editicon.setStyle('margin-left', '40px');
7450             }
7451             this.editicon.on('click', function(e) {
7452                 
7453                 // we fire even  if inothing is selected..
7454                 this.fireEvent('edit', this, this.lastData );
7455                 
7456             }, this);
7457         }
7458         */
7459         
7460  
7461         this.keyNav = new Roo.KeyNav(this.inputEl(), {
7462             "up" : function(e){
7463                 this.inKeyMode = true;
7464                 this.selectPrev();
7465             },
7466
7467             "down" : function(e){
7468                 if(!this.isExpanded()){
7469                     this.onTriggerClick();
7470                 }else{
7471                     this.inKeyMode = true;
7472                     this.selectNext();
7473                 }
7474             },
7475
7476             "enter" : function(e){
7477                 this.onViewClick();
7478                 //return true;
7479             },
7480
7481             "esc" : function(e){
7482                 this.collapse();
7483             },
7484
7485             "tab" : function(e){
7486                 this.collapse();
7487                 
7488                 if(this.fireEvent("specialkey", this, e)){
7489                     this.onViewClick(false);
7490                 }
7491                 
7492                 return true;
7493             },
7494
7495             scope : this,
7496
7497             doRelay : function(foo, bar, hname){
7498                 if(hname == 'down' || this.scope.isExpanded()){
7499                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
7500                 }
7501                 return true;
7502             },
7503
7504             forceKeyDown: true
7505         });
7506         
7507         
7508         this.queryDelay = Math.max(this.queryDelay || 10,
7509                 this.mode == 'local' ? 10 : 250);
7510         
7511         
7512         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
7513         
7514         if(this.typeAhead){
7515             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
7516         }
7517         if(this.editable !== false){
7518             this.inputEl().on("keyup", this.onKeyUp, this);
7519         }
7520         if(this.forceSelection){
7521             this.on('blur', this.doForce, this);
7522         }
7523     },
7524
7525     onDestroy : function(){
7526         if(this.view){
7527             this.view.setStore(null);
7528             this.view.el.removeAllListeners();
7529             this.view.el.remove();
7530             this.view.purgeListeners();
7531         }
7532         if(this.list){
7533             this.list.dom.innerHTML  = '';
7534         }
7535         if(this.store){
7536             this.store.un('beforeload', this.onBeforeLoad, this);
7537             this.store.un('load', this.onLoad, this);
7538             this.store.un('loadexception', this.onLoadException, this);
7539         }
7540         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
7541     },
7542
7543     // private
7544     fireKey : function(e){
7545         if(e.isNavKeyPress() && !this.list.isVisible()){
7546             this.fireEvent("specialkey", this, e);
7547         }
7548     },
7549
7550     // private
7551     onResize: function(w, h){
7552 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
7553 //        
7554 //        if(typeof w != 'number'){
7555 //            // we do not handle it!?!?
7556 //            return;
7557 //        }
7558 //        var tw = this.trigger.getWidth();
7559 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
7560 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
7561 //        var x = w - tw;
7562 //        this.inputEl().setWidth( this.adjustWidth('input', x));
7563 //            
7564 //        //this.trigger.setStyle('left', x+'px');
7565 //        
7566 //        if(this.list && this.listWidth === undefined){
7567 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
7568 //            this.list.setWidth(lw);
7569 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
7570 //        }
7571         
7572     
7573         
7574     },
7575
7576     /**
7577      * Allow or prevent the user from directly editing the field text.  If false is passed,
7578      * the user will only be able to select from the items defined in the dropdown list.  This method
7579      * is the runtime equivalent of setting the 'editable' config option at config time.
7580      * @param {Boolean} value True to allow the user to directly edit the field text
7581      */
7582     setEditable : function(value){
7583         if(value == this.editable){
7584             return;
7585         }
7586         this.editable = value;
7587         if(!value){
7588             this.inputEl().dom.setAttribute('readOnly', true);
7589             this.inputEl().on('mousedown', this.onTriggerClick,  this);
7590             this.inputEl().addClass('x-combo-noedit');
7591         }else{
7592             this.inputEl().dom.setAttribute('readOnly', false);
7593             this.inputEl().un('mousedown', this.onTriggerClick,  this);
7594             this.inputEl().removeClass('x-combo-noedit');
7595         }
7596     },
7597
7598     // private
7599     onBeforeLoad : function(){
7600         if(!this.hasFocus){
7601             return;
7602         }
7603         //this.innerList.update(this.loadingText ?
7604         //       '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
7605         this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
7606         
7607         this.restrictHeight();
7608         this.selectedIndex = -1;
7609     },
7610
7611     // private
7612     onLoad : function(){
7613         if(!this.hasFocus){
7614             return;
7615         }
7616         if(this.store.getCount() > 0){
7617             this.expand();
7618             this.restrictHeight();
7619             if(this.lastQuery == this.allQuery){
7620                 if(this.editable){
7621                     this.inputEl().dom.select();
7622                 }
7623                 if(!this.selectByValue(this.value, true)){
7624                     this.select(0, true);
7625                 }
7626             }else{
7627                 this.selectNext();
7628                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
7629                     this.taTask.delay(this.typeAheadDelay);
7630                 }
7631             }
7632         }else{
7633             this.onEmptyResults();
7634         }
7635         //this.el.focus();
7636     },
7637     // private
7638     onLoadException : function()
7639     {
7640         this.collapse();
7641         Roo.log(this.store.reader.jsonData);
7642         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7643             // fixme
7644             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7645         }
7646         
7647         
7648     },
7649     // private
7650     onTypeAhead : function(){
7651         if(this.store.getCount() > 0){
7652             var r = this.store.getAt(0);
7653             var newValue = r.data[this.displayField];
7654             var len = newValue.length;
7655             var selStart = this.getRawValue().length;
7656             
7657             if(selStart != len){
7658                 this.setRawValue(newValue);
7659                 this.selectText(selStart, newValue.length);
7660             }
7661         }
7662     },
7663
7664     // private
7665     onSelect : function(record, index){
7666         if(this.fireEvent('beforeselect', this, record, index) !== false){
7667             this.setFromData(index > -1 ? record.data : false);
7668             this.collapse();
7669             this.fireEvent('select', this, record, index);
7670         }
7671     },
7672
7673     /**
7674      * Returns the currently selected field value or empty string if no value is set.
7675      * @return {String} value The selected value
7676      */
7677     getValue : function(){
7678         if(this.valueField){
7679             return typeof this.value != 'undefined' ? this.value : '';
7680         }else{
7681             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
7682         }
7683     },
7684
7685     /**
7686      * Clears any text/value currently set in the field
7687      */
7688     clearValue : function(){
7689         if(this.hiddenField){
7690             this.hiddenField.dom.value = '';
7691         }
7692         this.value = '';
7693         this.setRawValue('');
7694         this.lastSelectionText = '';
7695         
7696     },
7697
7698     /**
7699      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
7700      * will be displayed in the field.  If the value does not match the data value of an existing item,
7701      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
7702      * Otherwise the field will be blank (although the value will still be set).
7703      * @param {String} value The value to match
7704      */
7705     setValue : function(v){
7706         var text = v;
7707         if(this.valueField){
7708             var r = this.findRecord(this.valueField, v);
7709             if(r){
7710                 text = r.data[this.displayField];
7711             }else if(this.valueNotFoundText !== undefined){
7712                 text = this.valueNotFoundText;
7713             }
7714         }
7715         this.lastSelectionText = text;
7716         if(this.hiddenField){
7717             this.hiddenField.dom.value = v;
7718         }
7719         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
7720         this.value = v;
7721     },
7722     /**
7723      * @property {Object} the last set data for the element
7724      */
7725     
7726     lastData : false,
7727     /**
7728      * Sets the value of the field based on a object which is related to the record format for the store.
7729      * @param {Object} value the value to set as. or false on reset?
7730      */
7731     setFromData : function(o){
7732         var dv = ''; // display value
7733         var vv = ''; // value value..
7734         this.lastData = o;
7735         if (this.displayField) {
7736             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
7737         } else {
7738             // this is an error condition!!!
7739             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
7740         }
7741         
7742         if(this.valueField){
7743             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
7744         }
7745         if(this.hiddenField){
7746             this.hiddenField.dom.value = vv;
7747             
7748             this.lastSelectionText = dv;
7749             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
7750             this.value = vv;
7751             return;
7752         }
7753         // no hidden field.. - we store the value in 'value', but still display
7754         // display field!!!!
7755         this.lastSelectionText = dv;
7756         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
7757         this.value = vv;
7758         
7759         
7760     },
7761     // private
7762     reset : function(){
7763         // overridden so that last data is reset..
7764         this.setValue(this.originalValue);
7765         this.clearInvalid();
7766         this.lastData = false;
7767         if (this.view) {
7768             this.view.clearSelections();
7769         }
7770     },
7771     // private
7772     findRecord : function(prop, value){
7773         var record;
7774         if(this.store.getCount() > 0){
7775             this.store.each(function(r){
7776                 if(r.data[prop] == value){
7777                     record = r;
7778                     return false;
7779                 }
7780                 return true;
7781             });
7782         }
7783         return record;
7784     },
7785     
7786     getName: function()
7787     {
7788         // returns hidden if it's set..
7789         if (!this.rendered) {return ''};
7790         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
7791         
7792     },
7793     // private
7794     onViewMove : function(e, t){
7795         this.inKeyMode = false;
7796     },
7797
7798     // private
7799     onViewOver : function(e, t){
7800         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
7801             return;
7802         }
7803         var item = this.view.findItemFromChild(t);
7804         if(item){
7805             var index = this.view.indexOf(item);
7806             this.select(index, false);
7807         }
7808     },
7809
7810     // private
7811     onViewClick : function(doFocus)
7812     {
7813         var index = this.view.getSelectedIndexes()[0];
7814         var r = this.store.getAt(index);
7815         if(r){
7816             this.onSelect(r, index);
7817         }
7818         if(doFocus !== false && !this.blockFocus){
7819             this.inputEl().focus();
7820         }
7821     },
7822
7823     // private
7824     restrictHeight : function(){
7825         //this.innerList.dom.style.height = '';
7826         //var inner = this.innerList.dom;
7827         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
7828         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
7829         //this.list.beginUpdate();
7830         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
7831         this.list.alignTo(this.inputEl(), this.listAlign);
7832         //this.list.endUpdate();
7833     },
7834
7835     // private
7836     onEmptyResults : function(){
7837         this.collapse();
7838     },
7839
7840     /**
7841      * Returns true if the dropdown list is expanded, else false.
7842      */
7843     isExpanded : function(){
7844         return this.list.isVisible();
7845     },
7846
7847     /**
7848      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
7849      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
7850      * @param {String} value The data value of the item to select
7851      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
7852      * selected item if it is not currently in view (defaults to true)
7853      * @return {Boolean} True if the value matched an item in the list, else false
7854      */
7855     selectByValue : function(v, scrollIntoView){
7856         if(v !== undefined && v !== null){
7857             var r = this.findRecord(this.valueField || this.displayField, v);
7858             if(r){
7859                 this.select(this.store.indexOf(r), scrollIntoView);
7860                 return true;
7861             }
7862         }
7863         return false;
7864     },
7865
7866     /**
7867      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
7868      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
7869      * @param {Number} index The zero-based index of the list item to select
7870      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
7871      * selected item if it is not currently in view (defaults to true)
7872      */
7873     select : function(index, scrollIntoView){
7874         this.selectedIndex = index;
7875         this.view.select(index);
7876         if(scrollIntoView !== false){
7877             var el = this.view.getNode(index);
7878             if(el){
7879                 //this.innerList.scrollChildIntoView(el, false);
7880                 
7881             }
7882         }
7883     },
7884
7885     // private
7886     selectNext : function(){
7887         var ct = this.store.getCount();
7888         if(ct > 0){
7889             if(this.selectedIndex == -1){
7890                 this.select(0);
7891             }else if(this.selectedIndex < ct-1){
7892                 this.select(this.selectedIndex+1);
7893             }
7894         }
7895     },
7896
7897     // private
7898     selectPrev : function(){
7899         var ct = this.store.getCount();
7900         if(ct > 0){
7901             if(this.selectedIndex == -1){
7902                 this.select(0);
7903             }else if(this.selectedIndex != 0){
7904                 this.select(this.selectedIndex-1);
7905             }
7906         }
7907     },
7908
7909     // private
7910     onKeyUp : function(e){
7911         if(this.editable !== false && !e.isSpecialKey()){
7912             this.lastKey = e.getKey();
7913             this.dqTask.delay(this.queryDelay);
7914         }
7915     },
7916
7917     // private
7918     validateBlur : function(){
7919         return !this.list || !this.list.isVisible();   
7920     },
7921
7922     // private
7923     initQuery : function(){
7924         this.doQuery(this.getRawValue());
7925     },
7926
7927     // private
7928     doForce : function(){
7929         if(this.el.dom.value.length > 0){
7930             this.el.dom.value =
7931                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
7932              
7933         }
7934     },
7935
7936     /**
7937      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
7938      * query allowing the query action to be canceled if needed.
7939      * @param {String} query The SQL query to execute
7940      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
7941      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
7942      * saved in the current store (defaults to false)
7943      */
7944     doQuery : function(q, forceAll){
7945         if(q === undefined || q === null){
7946             q = '';
7947         }
7948         var qe = {
7949             query: q,
7950             forceAll: forceAll,
7951             combo: this,
7952             cancel:false
7953         };
7954         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
7955             return false;
7956         }
7957         q = qe.query;
7958         forceAll = qe.forceAll;
7959         if(forceAll === true || (q.length >= this.minChars)){
7960             if(this.lastQuery != q || this.alwaysQuery){
7961                 this.lastQuery = q;
7962                 if(this.mode == 'local'){
7963                     this.selectedIndex = -1;
7964                     if(forceAll){
7965                         this.store.clearFilter();
7966                     }else{
7967                         this.store.filter(this.displayField, q);
7968                     }
7969                     this.onLoad();
7970                 }else{
7971                     this.store.baseParams[this.queryParam] = q;
7972                     this.store.load({
7973                         params: this.getParams(q)
7974                     });
7975                     this.expand();
7976                 }
7977             }else{
7978                 this.selectedIndex = -1;
7979                 this.onLoad();   
7980             }
7981         }
7982     },
7983
7984     // private
7985     getParams : function(q){
7986         var p = {};
7987         //p[this.queryParam] = q;
7988         if(this.pageSize){
7989             p.start = 0;
7990             p.limit = this.pageSize;
7991         }
7992         return p;
7993     },
7994
7995     /**
7996      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
7997      */
7998     collapse : function(){
7999         if(!this.isExpanded()){
8000             return;
8001         }
8002         this.list.hide();
8003         Roo.get(document).un('mousedown', this.collapseIf, this);
8004         Roo.get(document).un('mousewheel', this.collapseIf, this);
8005         if (!this.editable) {
8006             Roo.get(document).un('keydown', this.listKeyPress, this);
8007         }
8008         this.fireEvent('collapse', this);
8009     },
8010
8011     // private
8012     collapseIf : function(e){
8013         if(!e.within(this.el) && !e.within(this.el)){
8014             this.collapse();
8015         }
8016     },
8017
8018     /**
8019      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8020      */
8021     expand : function(){
8022         Roo.log('expand');
8023         if(this.isExpanded() || !this.hasFocus){
8024             return;
8025         }
8026         this.list.alignTo(this.inputEl(), this.listAlign);
8027         this.list.show();
8028         Roo.get(document).on('mousedown', this.collapseIf, this);
8029         Roo.get(document).on('mousewheel', this.collapseIf, this);
8030         if (!this.editable) {
8031             Roo.get(document).on('keydown', this.listKeyPress, this);
8032         }
8033         
8034         this.fireEvent('expand', this);
8035     },
8036
8037     // private
8038     // Implements the default empty TriggerField.onTriggerClick function
8039     onTriggerClick : function()
8040     {
8041         Roo.log('trigger click');
8042         
8043         if(this.disabled){
8044             return;
8045         }
8046         if(this.isExpanded()){
8047             this.collapse();
8048             if (!this.blockFocus) {
8049                 this.inputEl().focus();
8050             }
8051             
8052         }else {
8053             this.hasFocus = true;
8054             if(this.triggerAction == 'all') {
8055                 this.doQuery(this.allQuery, true);
8056             } else {
8057                 this.doQuery(this.getRawValue());
8058             }
8059             if (!this.blockFocus) {
8060                 this.inputEl().focus();
8061             }
8062         }
8063     },
8064     listKeyPress : function(e)
8065     {
8066         //Roo.log('listkeypress');
8067         // scroll to first matching element based on key pres..
8068         if (e.isSpecialKey()) {
8069             return false;
8070         }
8071         var k = String.fromCharCode(e.getKey()).toUpperCase();
8072         //Roo.log(k);
8073         var match  = false;
8074         var csel = this.view.getSelectedNodes();
8075         var cselitem = false;
8076         if (csel.length) {
8077             var ix = this.view.indexOf(csel[0]);
8078             cselitem  = this.store.getAt(ix);
8079             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8080                 cselitem = false;
8081             }
8082             
8083         }
8084         
8085         this.store.each(function(v) { 
8086             if (cselitem) {
8087                 // start at existing selection.
8088                 if (cselitem.id == v.id) {
8089                     cselitem = false;
8090                 }
8091                 return true;
8092             }
8093                 
8094             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8095                 match = this.store.indexOf(v);
8096                 return false;
8097             }
8098             return true;
8099         }, this);
8100         
8101         if (match === false) {
8102             return true; // no more action?
8103         }
8104         // scroll to?
8105         this.view.select(match);
8106         var sn = Roo.get(this.view.getSelectedNodes()[0])
8107         //sn.scrollIntoView(sn.dom.parentNode, false);
8108     }
8109
8110     /** 
8111     * @cfg {Boolean} grow 
8112     * @hide 
8113     */
8114     /** 
8115     * @cfg {Number} growMin 
8116     * @hide 
8117     */
8118     /** 
8119     * @cfg {Number} growMax 
8120     * @hide 
8121     */
8122     /**
8123      * @hide
8124      * @method autoSize
8125      */
8126 });/*
8127  * Based on:
8128  * Ext JS Library 1.1.1
8129  * Copyright(c) 2006-2007, Ext JS, LLC.
8130  *
8131  * Originally Released Under LGPL - original licence link has changed is not relivant.
8132  *
8133  * Fork - LGPL
8134  * <script type="text/javascript">
8135  */
8136
8137 /**
8138  * @class Roo.View
8139  * @extends Roo.util.Observable
8140  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8141  * This class also supports single and multi selection modes. <br>
8142  * Create a data model bound view:
8143  <pre><code>
8144  var store = new Roo.data.Store(...);
8145
8146  var view = new Roo.View({
8147     el : "my-element",
8148     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8149  
8150     singleSelect: true,
8151     selectedClass: "ydataview-selected",
8152     store: store
8153  });
8154
8155  // listen for node click?
8156  view.on("click", function(vw, index, node, e){
8157  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8158  });
8159
8160  // load XML data
8161  dataModel.load("foobar.xml");
8162  </code></pre>
8163  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8164  * <br><br>
8165  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8166  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8167  * 
8168  * Note: old style constructor is still suported (container, template, config)
8169  * 
8170  * @constructor
8171  * Create a new View
8172  * @param {Object} config The config object
8173  * 
8174  */
8175 Roo.View = function(config, depreciated_tpl, depreciated_config){
8176     
8177     if (typeof(depreciated_tpl) == 'undefined') {
8178         // new way.. - universal constructor.
8179         Roo.apply(this, config);
8180         this.el  = Roo.get(this.el);
8181     } else {
8182         // old format..
8183         this.el  = Roo.get(config);
8184         this.tpl = depreciated_tpl;
8185         Roo.apply(this, depreciated_config);
8186     }
8187     this.wrapEl  = this.el.wrap().wrap();
8188     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8189     
8190     
8191     if(typeof(this.tpl) == "string"){
8192         this.tpl = new Roo.Template(this.tpl);
8193     } else {
8194         // support xtype ctors..
8195         this.tpl = new Roo.factory(this.tpl, Roo);
8196     }
8197     
8198     
8199     this.tpl.compile();
8200    
8201   
8202     
8203      
8204     /** @private */
8205     this.addEvents({
8206         /**
8207          * @event beforeclick
8208          * Fires before a click is processed. Returns false to cancel the default action.
8209          * @param {Roo.View} this
8210          * @param {Number} index The index of the target node
8211          * @param {HTMLElement} node The target node
8212          * @param {Roo.EventObject} e The raw event object
8213          */
8214             "beforeclick" : true,
8215         /**
8216          * @event click
8217          * Fires when a template node is clicked.
8218          * @param {Roo.View} this
8219          * @param {Number} index The index of the target node
8220          * @param {HTMLElement} node The target node
8221          * @param {Roo.EventObject} e The raw event object
8222          */
8223             "click" : true,
8224         /**
8225          * @event dblclick
8226          * Fires when a template node is double clicked.
8227          * @param {Roo.View} this
8228          * @param {Number} index The index of the target node
8229          * @param {HTMLElement} node The target node
8230          * @param {Roo.EventObject} e The raw event object
8231          */
8232             "dblclick" : true,
8233         /**
8234          * @event contextmenu
8235          * Fires when a template node is right clicked.
8236          * @param {Roo.View} this
8237          * @param {Number} index The index of the target node
8238          * @param {HTMLElement} node The target node
8239          * @param {Roo.EventObject} e The raw event object
8240          */
8241             "contextmenu" : true,
8242         /**
8243          * @event selectionchange
8244          * Fires when the selected nodes change.
8245          * @param {Roo.View} this
8246          * @param {Array} selections Array of the selected nodes
8247          */
8248             "selectionchange" : true,
8249     
8250         /**
8251          * @event beforeselect
8252          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8253          * @param {Roo.View} this
8254          * @param {HTMLElement} node The node to be selected
8255          * @param {Array} selections Array of currently selected nodes
8256          */
8257             "beforeselect" : true,
8258         /**
8259          * @event preparedata
8260          * Fires on every row to render, to allow you to change the data.
8261          * @param {Roo.View} this
8262          * @param {Object} data to be rendered (change this)
8263          */
8264           "preparedata" : true
8265           
8266           
8267         });
8268
8269
8270
8271     this.el.on({
8272         "click": this.onClick,
8273         "dblclick": this.onDblClick,
8274         "contextmenu": this.onContextMenu,
8275         scope:this
8276     });
8277
8278     this.selections = [];
8279     this.nodes = [];
8280     this.cmp = new Roo.CompositeElementLite([]);
8281     if(this.store){
8282         this.store = Roo.factory(this.store, Roo.data);
8283         this.setStore(this.store, true);
8284     }
8285     
8286     if ( this.footer && this.footer.xtype) {
8287            
8288          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8289         
8290         this.footer.dataSource = this.store
8291         this.footer.container = fctr;
8292         this.footer = Roo.factory(this.footer, Roo);
8293         fctr.insertFirst(this.el);
8294         
8295         // this is a bit insane - as the paging toolbar seems to detach the el..
8296 //        dom.parentNode.parentNode.parentNode
8297          // they get detached?
8298     }
8299     
8300     
8301     Roo.View.superclass.constructor.call(this);
8302     
8303     
8304 };
8305
8306 Roo.extend(Roo.View, Roo.util.Observable, {
8307     
8308      /**
8309      * @cfg {Roo.data.Store} store Data store to load data from.
8310      */
8311     store : false,
8312     
8313     /**
8314      * @cfg {String|Roo.Element} el The container element.
8315      */
8316     el : '',
8317     
8318     /**
8319      * @cfg {String|Roo.Template} tpl The template used by this View 
8320      */
8321     tpl : false,
8322     /**
8323      * @cfg {String} dataName the named area of the template to use as the data area
8324      *                          Works with domtemplates roo-name="name"
8325      */
8326     dataName: false,
8327     /**
8328      * @cfg {String} selectedClass The css class to add to selected nodes
8329      */
8330     selectedClass : "x-view-selected",
8331      /**
8332      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8333      */
8334     emptyText : "",
8335     
8336     /**
8337      * @cfg {String} text to display on mask (default Loading)
8338      */
8339     mask : false,
8340     /**
8341      * @cfg {Boolean} multiSelect Allow multiple selection
8342      */
8343     multiSelect : false,
8344     /**
8345      * @cfg {Boolean} singleSelect Allow single selection
8346      */
8347     singleSelect:  false,
8348     
8349     /**
8350      * @cfg {Boolean} toggleSelect - selecting 
8351      */
8352     toggleSelect : false,
8353     
8354     /**
8355      * Returns the element this view is bound to.
8356      * @return {Roo.Element}
8357      */
8358     getEl : function(){
8359         return this.wrapEl;
8360     },
8361     
8362     
8363
8364     /**
8365      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8366      */
8367     refresh : function(){
8368         var t = this.tpl;
8369         
8370         // if we are using something like 'domtemplate', then
8371         // the what gets used is:
8372         // t.applySubtemplate(NAME, data, wrapping data..)
8373         // the outer template then get' applied with
8374         //     the store 'extra data'
8375         // and the body get's added to the
8376         //      roo-name="data" node?
8377         //      <span class='roo-tpl-{name}'></span> ?????
8378         
8379         
8380         
8381         this.clearSelections();
8382         this.el.update("");
8383         var html = [];
8384         var records = this.store.getRange();
8385         if(records.length < 1) {
8386             
8387             // is this valid??  = should it render a template??
8388             
8389             this.el.update(this.emptyText);
8390             return;
8391         }
8392         var el = this.el;
8393         if (this.dataName) {
8394             this.el.update(t.apply(this.store.meta)); //????
8395             el = this.el.child('.roo-tpl-' + this.dataName);
8396         }
8397         
8398         for(var i = 0, len = records.length; i < len; i++){
8399             var data = this.prepareData(records[i].data, i, records[i]);
8400             this.fireEvent("preparedata", this, data, i, records[i]);
8401             html[html.length] = Roo.util.Format.trim(
8402                 this.dataName ?
8403                     t.applySubtemplate(this.dataName, data, this.store.meta) :
8404                     t.apply(data)
8405             );
8406         }
8407         
8408         
8409         
8410         el.update(html.join(""));
8411         this.nodes = el.dom.childNodes;
8412         this.updateIndexes(0);
8413     },
8414
8415     /**
8416      * Function to override to reformat the data that is sent to
8417      * the template for each node.
8418      * DEPRICATED - use the preparedata event handler.
8419      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8420      * a JSON object for an UpdateManager bound view).
8421      */
8422     prepareData : function(data, index, record)
8423     {
8424         this.fireEvent("preparedata", this, data, index, record);
8425         return data;
8426     },
8427
8428     onUpdate : function(ds, record){
8429         this.clearSelections();
8430         var index = this.store.indexOf(record);
8431         var n = this.nodes[index];
8432         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8433         n.parentNode.removeChild(n);
8434         this.updateIndexes(index, index);
8435     },
8436
8437     
8438     
8439 // --------- FIXME     
8440     onAdd : function(ds, records, index)
8441     {
8442         this.clearSelections();
8443         if(this.nodes.length == 0){
8444             this.refresh();
8445             return;
8446         }
8447         var n = this.nodes[index];
8448         for(var i = 0, len = records.length; i < len; i++){
8449             var d = this.prepareData(records[i].data, i, records[i]);
8450             if(n){
8451                 this.tpl.insertBefore(n, d);
8452             }else{
8453                 
8454                 this.tpl.append(this.el, d);
8455             }
8456         }
8457         this.updateIndexes(index);
8458     },
8459
8460     onRemove : function(ds, record, index){
8461         this.clearSelections();
8462         var el = this.dataName  ?
8463             this.el.child('.roo-tpl-' + this.dataName) :
8464             this.el; 
8465         el.dom.removeChild(this.nodes[index]);
8466         this.updateIndexes(index);
8467     },
8468
8469     /**
8470      * Refresh an individual node.
8471      * @param {Number} index
8472      */
8473     refreshNode : function(index){
8474         this.onUpdate(this.store, this.store.getAt(index));
8475     },
8476
8477     updateIndexes : function(startIndex, endIndex){
8478         var ns = this.nodes;
8479         startIndex = startIndex || 0;
8480         endIndex = endIndex || ns.length - 1;
8481         for(var i = startIndex; i <= endIndex; i++){
8482             ns[i].nodeIndex = i;
8483         }
8484     },
8485
8486     /**
8487      * Changes the data store this view uses and refresh the view.
8488      * @param {Store} store
8489      */
8490     setStore : function(store, initial){
8491         if(!initial && this.store){
8492             this.store.un("datachanged", this.refresh);
8493             this.store.un("add", this.onAdd);
8494             this.store.un("remove", this.onRemove);
8495             this.store.un("update", this.onUpdate);
8496             this.store.un("clear", this.refresh);
8497             this.store.un("beforeload", this.onBeforeLoad);
8498             this.store.un("load", this.onLoad);
8499             this.store.un("loadexception", this.onLoad);
8500         }
8501         if(store){
8502           
8503             store.on("datachanged", this.refresh, this);
8504             store.on("add", this.onAdd, this);
8505             store.on("remove", this.onRemove, this);
8506             store.on("update", this.onUpdate, this);
8507             store.on("clear", this.refresh, this);
8508             store.on("beforeload", this.onBeforeLoad, this);
8509             store.on("load", this.onLoad, this);
8510             store.on("loadexception", this.onLoad, this);
8511         }
8512         
8513         if(store){
8514             this.refresh();
8515         }
8516     },
8517     /**
8518      * onbeforeLoad - masks the loading area.
8519      *
8520      */
8521     onBeforeLoad : function()
8522     {
8523         this.el.update("");
8524         this.el.mask(this.mask ? this.mask : "Loading" ); 
8525     },
8526     onLoad : function ()
8527     {
8528         this.el.unmask();
8529     },
8530     
8531
8532     /**
8533      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8534      * @param {HTMLElement} node
8535      * @return {HTMLElement} The template node
8536      */
8537     findItemFromChild : function(node){
8538         var el = this.dataName  ?
8539             this.el.child('.roo-tpl-' + this.dataName,true) :
8540             this.el.dom; 
8541         
8542         if(!node || node.parentNode == el){
8543                     return node;
8544             }
8545             var p = node.parentNode;
8546             while(p && p != el){
8547             if(p.parentNode == el){
8548                 return p;
8549             }
8550             p = p.parentNode;
8551         }
8552             return null;
8553     },
8554
8555     /** @ignore */
8556     onClick : function(e){
8557         var item = this.findItemFromChild(e.getTarget());
8558         if(item){
8559             var index = this.indexOf(item);
8560             if(this.onItemClick(item, index, e) !== false){
8561                 this.fireEvent("click", this, index, item, e);
8562             }
8563         }else{
8564             this.clearSelections();
8565         }
8566     },
8567
8568     /** @ignore */
8569     onContextMenu : function(e){
8570         var item = this.findItemFromChild(e.getTarget());
8571         if(item){
8572             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8573         }
8574     },
8575
8576     /** @ignore */
8577     onDblClick : function(e){
8578         var item = this.findItemFromChild(e.getTarget());
8579         if(item){
8580             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8581         }
8582     },
8583
8584     onItemClick : function(item, index, e)
8585     {
8586         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8587             return false;
8588         }
8589         if (this.toggleSelect) {
8590             var m = this.isSelected(item) ? 'unselect' : 'select';
8591             Roo.log(m);
8592             var _t = this;
8593             _t[m](item, true, false);
8594             return true;
8595         }
8596         if(this.multiSelect || this.singleSelect){
8597             if(this.multiSelect && e.shiftKey && this.lastSelection){
8598                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8599             }else{
8600                 this.select(item, this.multiSelect && e.ctrlKey);
8601                 this.lastSelection = item;
8602             }
8603             e.preventDefault();
8604         }
8605         return true;
8606     },
8607
8608     /**
8609      * Get the number of selected nodes.
8610      * @return {Number}
8611      */
8612     getSelectionCount : function(){
8613         return this.selections.length;
8614     },
8615
8616     /**
8617      * Get the currently selected nodes.
8618      * @return {Array} An array of HTMLElements
8619      */
8620     getSelectedNodes : function(){
8621         return this.selections;
8622     },
8623
8624     /**
8625      * Get the indexes of the selected nodes.
8626      * @return {Array}
8627      */
8628     getSelectedIndexes : function(){
8629         var indexes = [], s = this.selections;
8630         for(var i = 0, len = s.length; i < len; i++){
8631             indexes.push(s[i].nodeIndex);
8632         }
8633         return indexes;
8634     },
8635
8636     /**
8637      * Clear all selections
8638      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8639      */
8640     clearSelections : function(suppressEvent){
8641         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8642             this.cmp.elements = this.selections;
8643             this.cmp.removeClass(this.selectedClass);
8644             this.selections = [];
8645             if(!suppressEvent){
8646                 this.fireEvent("selectionchange", this, this.selections);
8647             }
8648         }
8649     },
8650
8651     /**
8652      * Returns true if the passed node is selected
8653      * @param {HTMLElement/Number} node The node or node index
8654      * @return {Boolean}
8655      */
8656     isSelected : function(node){
8657         var s = this.selections;
8658         if(s.length < 1){
8659             return false;
8660         }
8661         node = this.getNode(node);
8662         return s.indexOf(node) !== -1;
8663     },
8664
8665     /**
8666      * Selects nodes.
8667      * @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
8668      * @param {Boolean} keepExisting (optional) true to keep existing selections
8669      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8670      */
8671     select : function(nodeInfo, keepExisting, suppressEvent){
8672         if(nodeInfo instanceof Array){
8673             if(!keepExisting){
8674                 this.clearSelections(true);
8675             }
8676             for(var i = 0, len = nodeInfo.length; i < len; i++){
8677                 this.select(nodeInfo[i], true, true);
8678             }
8679             return;
8680         } 
8681         var node = this.getNode(nodeInfo);
8682         if(!node || this.isSelected(node)){
8683             return; // already selected.
8684         }
8685         if(!keepExisting){
8686             this.clearSelections(true);
8687         }
8688         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8689             Roo.fly(node).addClass(this.selectedClass);
8690             this.selections.push(node);
8691             if(!suppressEvent){
8692                 this.fireEvent("selectionchange", this, this.selections);
8693             }
8694         }
8695         
8696         
8697     },
8698       /**
8699      * Unselects nodes.
8700      * @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
8701      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8702      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8703      */
8704     unselect : function(nodeInfo, keepExisting, suppressEvent)
8705     {
8706         if(nodeInfo instanceof Array){
8707             Roo.each(this.selections, function(s) {
8708                 this.unselect(s, nodeInfo);
8709             }, this);
8710             return;
8711         }
8712         var node = this.getNode(nodeInfo);
8713         if(!node || !this.isSelected(node)){
8714             Roo.log("not selected");
8715             return; // not selected.
8716         }
8717         // fireevent???
8718         var ns = [];
8719         Roo.each(this.selections, function(s) {
8720             if (s == node ) {
8721                 Roo.fly(node).removeClass(this.selectedClass);
8722
8723                 return;
8724             }
8725             ns.push(s);
8726         },this);
8727         
8728         this.selections= ns;
8729         this.fireEvent("selectionchange", this, this.selections);
8730     },
8731
8732     /**
8733      * Gets a template node.
8734      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
8735      * @return {HTMLElement} The node or null if it wasn't found
8736      */
8737     getNode : function(nodeInfo){
8738         if(typeof nodeInfo == "string"){
8739             return document.getElementById(nodeInfo);
8740         }else if(typeof nodeInfo == "number"){
8741             return this.nodes[nodeInfo];
8742         }
8743         return nodeInfo;
8744     },
8745
8746     /**
8747      * Gets a range template nodes.
8748      * @param {Number} startIndex
8749      * @param {Number} endIndex
8750      * @return {Array} An array of nodes
8751      */
8752     getNodes : function(start, end){
8753         var ns = this.nodes;
8754         start = start || 0;
8755         end = typeof end == "undefined" ? ns.length - 1 : end;
8756         var nodes = [];
8757         if(start <= end){
8758             for(var i = start; i <= end; i++){
8759                 nodes.push(ns[i]);
8760             }
8761         } else{
8762             for(var i = start; i >= end; i--){
8763                 nodes.push(ns[i]);
8764             }
8765         }
8766         return nodes;
8767     },
8768
8769     /**
8770      * Finds the index of the passed node
8771      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
8772      * @return {Number} The index of the node or -1
8773      */
8774     indexOf : function(node){
8775         node = this.getNode(node);
8776         if(typeof node.nodeIndex == "number"){
8777             return node.nodeIndex;
8778         }
8779         var ns = this.nodes;
8780         for(var i = 0, len = ns.length; i < len; i++){
8781             if(ns[i] == node){
8782                 return i;
8783             }
8784         }
8785         return -1;
8786     }
8787 });
8788 /*
8789  * - LGPL
8790  *
8791  * based on jquery fullcalendar
8792  * 
8793  */
8794
8795 Roo.bootstrap = Roo.bootstrap || {};
8796 /**
8797  * @class Roo.bootstrap.Calendar
8798  * @extends Roo.bootstrap.Component
8799  * Bootstrap Calendar class
8800  * @cfg {Boolean} loadMask (true|false) default false
8801     
8802  * @constructor
8803  * Create a new Container
8804  * @param {Object} config The config object
8805  */
8806
8807
8808
8809 Roo.bootstrap.Calendar = function(config){
8810     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
8811      this.addEvents({
8812         /**
8813              * @event select
8814              * Fires when a date is selected
8815              * @param {DatePicker} this
8816              * @param {Date} date The selected date
8817              */
8818         'select': true,
8819         /**
8820              * @event monthchange
8821              * Fires when the displayed month changes 
8822              * @param {DatePicker} this
8823              * @param {Date} date The selected month
8824              */
8825         'monthchange': true,
8826         /**
8827              * @event evententer
8828              * Fires when mouse over an event
8829              * @param {Calendar} this
8830              * @param {event} Event
8831              */
8832         'evententer': true,
8833         /**
8834              * @event eventleave
8835              * Fires when the mouse leaves an
8836              * @param {Calendar} this
8837              * @param {event}
8838              */
8839         'eventleave': true,
8840         /**
8841              * @event eventclick
8842              * Fires when the mouse click an
8843              * @param {Calendar} this
8844              * @param {event}
8845              */
8846         'eventclick': true
8847         
8848     });
8849
8850 };
8851
8852 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
8853     
8854      /**
8855      * @cfg {Number} startDay
8856      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
8857      */
8858     startDay : 0,
8859     
8860     loadMask : false,
8861       
8862     getAutoCreate : function(){
8863         
8864         
8865         var fc_button = function(name, corner, style, content ) {
8866             return Roo.apply({},{
8867                 tag : 'span',
8868                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
8869                          (corner.length ?
8870                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
8871                             ''
8872                         ),
8873                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
8874                 unselectable: 'on'
8875             });
8876         };
8877         
8878         var header = {
8879             tag : 'table',
8880             cls : 'fc-header',
8881             style : 'width:100%',
8882             cn : [
8883                 {
8884                     tag: 'tr',
8885                     cn : [
8886                         {
8887                             tag : 'td',
8888                             cls : 'fc-header-left',
8889                             cn : [
8890                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
8891                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
8892                                 { tag: 'span', cls: 'fc-header-space' },
8893                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
8894                                 
8895                                 
8896                             ]
8897                         },
8898                         
8899                         {
8900                             tag : 'td',
8901                             cls : 'fc-header-center',
8902                             cn : [
8903                                 {
8904                                     tag: 'span',
8905                                     cls: 'fc-header-title',
8906                                     cn : {
8907                                         tag: 'H2',
8908                                         html : 'month / year'
8909                                     }
8910                                 }
8911                                 
8912                             ]
8913                         },
8914                         {
8915                             tag : 'td',
8916                             cls : 'fc-header-right',
8917                             cn : [
8918                           /*      fc_button('month', 'left', '', 'month' ),
8919                                 fc_button('week', '', '', 'week' ),
8920                                 fc_button('day', 'right', '', 'day' )
8921                             */    
8922                                 
8923                             ]
8924                         }
8925                         
8926                     ]
8927                 }
8928             ]
8929         };
8930         
8931        
8932         var cal_heads = function() {
8933             var ret = [];
8934             // fixme - handle this.
8935             
8936             for (var i =0; i < Date.dayNames.length; i++) {
8937                 var d = Date.dayNames[i];
8938                 ret.push({
8939                     tag: 'th',
8940                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
8941                     html : d.substring(0,3)
8942                 });
8943                 
8944             }
8945             ret[0].cls += ' fc-first';
8946             ret[6].cls += ' fc-last';
8947             return ret;
8948         };
8949         var cal_cell = function(n) {
8950             return  {
8951                 tag: 'td',
8952                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
8953                 cn : [
8954                     {
8955                         cn : [
8956                             {
8957                                 cls: 'fc-day-number',
8958                                 html: 'D'
8959                             },
8960                             {
8961                                 cls: 'fc-day-content',
8962                              
8963                                 cn : [
8964                                      {
8965                                         style: 'position: relative;' // height: 17px;
8966                                     }
8967                                 ]
8968                             }
8969                             
8970                             
8971                         ]
8972                     }
8973                 ]
8974                 
8975             }
8976         };
8977         var cal_rows = function() {
8978             
8979             var ret = []
8980             for (var r = 0; r < 6; r++) {
8981                 var row= {
8982                     tag : 'tr',
8983                     cls : 'fc-week',
8984                     cn : []
8985                 };
8986                 
8987                 for (var i =0; i < Date.dayNames.length; i++) {
8988                     var d = Date.dayNames[i];
8989                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
8990
8991                 }
8992                 row.cn[0].cls+=' fc-first';
8993                 row.cn[0].cn[0].style = 'min-height:90px';
8994                 row.cn[6].cls+=' fc-last';
8995                 ret.push(row);
8996                 
8997             }
8998             ret[0].cls += ' fc-first';
8999             ret[4].cls += ' fc-prev-last';
9000             ret[5].cls += ' fc-last';
9001             return ret;
9002             
9003         };
9004         
9005         var cal_table = {
9006             tag: 'table',
9007             cls: 'fc-border-separate',
9008             style : 'width:100%',
9009             cellspacing  : 0,
9010             cn : [
9011                 { 
9012                     tag: 'thead',
9013                     cn : [
9014                         { 
9015                             tag: 'tr',
9016                             cls : 'fc-first fc-last',
9017                             cn : cal_heads()
9018                         }
9019                     ]
9020                 },
9021                 { 
9022                     tag: 'tbody',
9023                     cn : cal_rows()
9024                 }
9025                   
9026             ]
9027         };
9028          
9029          var cfg = {
9030             cls : 'fc fc-ltr',
9031             cn : [
9032                 header,
9033                 {
9034                     cls : 'fc-content',
9035                     style : "position: relative;",
9036                     cn : [
9037                         {
9038                             cls : 'fc-view fc-view-month fc-grid',
9039                             style : 'position: relative',
9040                             unselectable : 'on',
9041                             cn : [
9042                                 {
9043                                     cls : 'fc-event-container',
9044                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9045                                 },
9046                                 cal_table
9047                             ]
9048                         }
9049                     ]
9050     
9051                 }
9052            ] 
9053             
9054         };
9055         
9056          
9057         
9058         return cfg;
9059     },
9060     
9061     
9062     initEvents : function()
9063     {
9064         if(!this.store){
9065             throw "can not find store for calendar";
9066         }
9067         
9068         var mark = {
9069             tag: "div",
9070             cls:"x-dlg-mask",
9071             style: "text-align:center",
9072             cn: [
9073                 {
9074                     tag: "div",
9075                     style: "background-color:white;width:50%;margin:250 auto",
9076                     cn: [
9077                         {
9078                             tag: "img",
9079                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9080                         },
9081                         {
9082                             tag: "span",
9083                             html: "Loading"
9084                         }
9085                         
9086                     ]
9087                 }
9088             ]
9089         }
9090         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9091         
9092         var size = this.el.select('.fc-content', true).first().getSize();
9093         this.maskEl.setSize(size.width, size.height);
9094         this.maskEl.enableDisplayMode("block");
9095         if(!this.loadMask){
9096             this.maskEl.hide();
9097         }
9098         
9099         this.store = Roo.factory(this.store, Roo.data);
9100         this.store.on('load', this.onLoad, this);
9101         this.store.on('beforeload', this.onBeforeLoad, this);
9102         
9103         this.resize();
9104         
9105         this.cells = this.el.select('.fc-day',true);
9106         //Roo.log(this.cells);
9107         this.textNodes = this.el.query('.fc-day-number');
9108         this.cells.addClassOnOver('fc-state-hover');
9109         
9110         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
9111         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
9112         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
9113         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
9114         
9115         this.on('monthchange', this.onMonthChange, this);
9116         
9117 //        this.update(new Date().clearTime());
9118     },
9119     
9120     resize : function() {
9121         var sz  = this.el.getSize();
9122         
9123         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
9124         this.el.select('.fc-day-content div',true).setHeight(34);
9125     },
9126     
9127     
9128     // private
9129     showPrevMonth : function(e){
9130         this.update(this.activeDate.add("mo", -1));
9131     },
9132     showToday : function(e){
9133         this.update(new Date().clearTime());
9134     },
9135     // private
9136     showNextMonth : function(e){
9137         this.update(this.activeDate.add("mo", 1));
9138     },
9139
9140     // private
9141     showPrevYear : function(){
9142         this.update(this.activeDate.add("y", -1));
9143     },
9144
9145     // private
9146     showNextYear : function(){
9147         this.update(this.activeDate.add("y", 1));
9148     },
9149
9150     
9151    // private
9152     update : function(date)
9153     {
9154         var vd = this.activeDate;
9155         this.activeDate = date;
9156 //        if(vd && this.el){
9157 //            var t = date.getTime();
9158 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
9159 //                Roo.log('using add remove');
9160 //                
9161 //                this.fireEvent('monthchange', this, date);
9162 //                
9163 //                this.cells.removeClass("fc-state-highlight");
9164 //                this.cells.each(function(c){
9165 //                   if(c.dateValue == t){
9166 //                       c.addClass("fc-state-highlight");
9167 //                       setTimeout(function(){
9168 //                            try{c.dom.firstChild.focus();}catch(e){}
9169 //                       }, 50);
9170 //                       return false;
9171 //                   }
9172 //                   return true;
9173 //                });
9174 //                return;
9175 //            }
9176 //        }
9177         
9178         var days = date.getDaysInMonth();
9179         
9180         var firstOfMonth = date.getFirstDateOfMonth();
9181         var startingPos = firstOfMonth.getDay()-this.startDay;
9182         
9183         if(startingPos < this.startDay){
9184             startingPos += 7;
9185         }
9186         
9187         var pm = date.add(Date.MONTH, -1);
9188         var prevStart = pm.getDaysInMonth()-startingPos;
9189 //        
9190         this.cells = this.el.select('.fc-day',true);
9191         this.textNodes = this.el.query('.fc-day-number');
9192         this.cells.addClassOnOver('fc-state-hover');
9193         
9194         var cells = this.cells.elements;
9195         var textEls = this.textNodes;
9196         
9197         Roo.each(cells, function(cell){
9198             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
9199         });
9200         
9201         days += startingPos;
9202
9203         // convert everything to numbers so it's fast
9204         var day = 86400000;
9205         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
9206         //Roo.log(d);
9207         //Roo.log(pm);
9208         //Roo.log(prevStart);
9209         
9210         var today = new Date().clearTime().getTime();
9211         var sel = date.clearTime().getTime();
9212         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
9213         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
9214         var ddMatch = this.disabledDatesRE;
9215         var ddText = this.disabledDatesText;
9216         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
9217         var ddaysText = this.disabledDaysText;
9218         var format = this.format;
9219         
9220         var setCellClass = function(cal, cell){
9221             
9222             //Roo.log('set Cell Class');
9223             cell.title = "";
9224             var t = d.getTime();
9225             
9226             //Roo.log(d);
9227             
9228             cell.dateValue = t;
9229             if(t == today){
9230                 cell.className += " fc-today";
9231                 cell.className += " fc-state-highlight";
9232                 cell.title = cal.todayText;
9233             }
9234             if(t == sel){
9235                 // disable highlight in other month..
9236                 //cell.className += " fc-state-highlight";
9237                 
9238             }
9239             // disabling
9240             if(t < min) {
9241                 cell.className = " fc-state-disabled";
9242                 cell.title = cal.minText;
9243                 return;
9244             }
9245             if(t > max) {
9246                 cell.className = " fc-state-disabled";
9247                 cell.title = cal.maxText;
9248                 return;
9249             }
9250             if(ddays){
9251                 if(ddays.indexOf(d.getDay()) != -1){
9252                     cell.title = ddaysText;
9253                     cell.className = " fc-state-disabled";
9254                 }
9255             }
9256             if(ddMatch && format){
9257                 var fvalue = d.dateFormat(format);
9258                 if(ddMatch.test(fvalue)){
9259                     cell.title = ddText.replace("%0", fvalue);
9260                     cell.className = " fc-state-disabled";
9261                 }
9262             }
9263             
9264             if (!cell.initialClassName) {
9265                 cell.initialClassName = cell.dom.className;
9266             }
9267             
9268             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
9269         };
9270
9271         var i = 0;
9272         
9273         for(; i < startingPos; i++) {
9274             textEls[i].innerHTML = (++prevStart);
9275             d.setDate(d.getDate()+1);
9276             
9277             cells[i].className = "fc-past fc-other-month";
9278             setCellClass(this, cells[i]);
9279         }
9280         
9281         var intDay = 0;
9282         
9283         for(; i < days; i++){
9284             intDay = i - startingPos + 1;
9285             textEls[i].innerHTML = (intDay);
9286             d.setDate(d.getDate()+1);
9287             
9288             cells[i].className = ''; // "x-date-active";
9289             setCellClass(this, cells[i]);
9290         }
9291         var extraDays = 0;
9292         
9293         for(; i < 42; i++) {
9294             textEls[i].innerHTML = (++extraDays);
9295             d.setDate(d.getDate()+1);
9296             
9297             cells[i].className = "fc-future fc-other-month";
9298             setCellClass(this, cells[i]);
9299         }
9300         
9301         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
9302         
9303         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
9304         
9305         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
9306         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
9307         
9308         if(totalRows != 6){
9309             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
9310             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
9311         }
9312         
9313         this.fireEvent('monthchange', this, date);
9314         
9315         
9316         /*
9317         if(!this.internalRender){
9318             var main = this.el.dom.firstChild;
9319             var w = main.offsetWidth;
9320             this.el.setWidth(w + this.el.getBorderWidth("lr"));
9321             Roo.fly(main).setWidth(w);
9322             this.internalRender = true;
9323             // opera does not respect the auto grow header center column
9324             // then, after it gets a width opera refuses to recalculate
9325             // without a second pass
9326             if(Roo.isOpera && !this.secondPass){
9327                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
9328                 this.secondPass = true;
9329                 this.update.defer(10, this, [date]);
9330             }
9331         }
9332         */
9333         
9334     },
9335     
9336     findCell : function(dt) {
9337         dt = dt.clearTime().getTime();
9338         var ret = false;
9339         this.cells.each(function(c){
9340             //Roo.log("check " +c.dateValue + '?=' + dt);
9341             if(c.dateValue == dt){
9342                 ret = c;
9343                 return false;
9344             }
9345             return true;
9346         });
9347         
9348         return ret;
9349     },
9350     
9351     findCells : function(ev) {
9352         var s = ev.start.clone().clearTime().getTime();
9353        // Roo.log(s);
9354         var e= ev.end.clone().clearTime().getTime();
9355        // Roo.log(e);
9356         var ret = [];
9357         this.cells.each(function(c){
9358              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
9359             
9360             if(c.dateValue > e){
9361                 return ;
9362             }
9363             if(c.dateValue < s){
9364                 return ;
9365             }
9366             ret.push(c);
9367         });
9368         
9369         return ret;    
9370     },
9371     
9372     findBestRow: function(cells)
9373     {
9374         var ret = 0;
9375         
9376         for (var i =0 ; i < cells.length;i++) {
9377             ret  = Math.max(cells[i].rows || 0,ret);
9378         }
9379         return ret;
9380         
9381     },
9382     
9383     
9384     addItem : function(ev)
9385     {
9386         // look for vertical location slot in
9387         var cells = this.findCells(ev);
9388         
9389         ev.row = this.findBestRow(cells);
9390         
9391         // work out the location.
9392         
9393         var crow = false;
9394         var rows = [];
9395         for(var i =0; i < cells.length; i++) {
9396             if (!crow) {
9397                 crow = {
9398                     start : cells[i],
9399                     end :  cells[i]
9400                 };
9401                 continue;
9402             }
9403             if (crow.start.getY() == cells[i].getY()) {
9404                 // on same row.
9405                 crow.end = cells[i];
9406                 continue;
9407             }
9408             // different row.
9409             rows.push(crow);
9410             crow = {
9411                 start: cells[i],
9412                 end : cells[i]
9413             };
9414             
9415         }
9416         
9417         rows.push(crow);
9418         ev.els = [];
9419         ev.rows = rows;
9420         ev.cells = cells;
9421         for (var i = 0; i < cells.length;i++) {
9422             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
9423             
9424         }
9425         
9426         this.calevents.push(ev);
9427     },
9428     
9429     clearEvents: function() {
9430         
9431         if(!this.calevents){
9432             return;
9433         }
9434         
9435         Roo.each(this.cells.elements, function(c){
9436             c.rows = 0;
9437         });
9438         
9439         Roo.each(this.calevents, function(e) {
9440             Roo.each(e.els, function(el) {
9441                 el.un('mouseenter' ,this.onEventEnter, this);
9442                 el.un('mouseleave' ,this.onEventLeave, this);
9443                 el.remove();
9444             },this);
9445         },this);
9446         
9447     },
9448     
9449     renderEvents: function()
9450     {   
9451         // first make sure there is enough space..
9452         
9453         this.cells.each(function(c) {
9454         
9455             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
9456         });
9457         
9458         for (var e = 0; e < this.calevents.length; e++) {
9459             var ev = this.calevents[e];
9460             var cells = ev.cells;
9461             var rows = ev.rows;
9462             
9463             for(var i =0; i < rows.length; i++) {
9464                 
9465                  
9466                 // how many rows should it span..
9467                 
9468                 var  cfg = {
9469                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
9470                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
9471                     
9472                     unselectable : "on",
9473                     cn : [
9474                         {
9475                             cls: 'fc-event-inner',
9476                             cn : [
9477                                 {
9478                                   tag:'span',
9479                                   cls: 'fc-event-time',
9480                                   html : cells.length > 1 ? '' : ev.time
9481                                 },
9482                                 {
9483                                   tag:'span',
9484                                   cls: 'fc-event-title',
9485                                   html : String.format('{0}', ev.title)
9486                                 }
9487                                 
9488                                 
9489                             ]
9490                         },
9491                         {
9492                             cls: 'ui-resizable-handle ui-resizable-e',
9493                             html : '&nbsp;&nbsp;&nbsp'
9494                         }
9495                         
9496                     ]
9497                 };
9498                 if (i == 0) {
9499                     cfg.cls += ' fc-event-start';
9500                 }
9501                 if ((i+1) == rows.length) {
9502                     cfg.cls += ' fc-event-end';
9503                 }
9504                 
9505                 var ctr = this.el.select('.fc-event-container',true).first();
9506                 var cg = ctr.createChild(cfg);
9507                 
9508                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
9509                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
9510                 cg.on('click', this.onEventClick, this, ev);
9511                 
9512                 ev.els.push(cg);
9513                 
9514                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
9515                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
9516                 //Roo.log(cg);
9517                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
9518                 cg.setWidth(ebox.right - sbox.x -2);
9519             }
9520             
9521             
9522         }
9523         
9524     },
9525     
9526     onEventEnter: function (e, el,event,d) {
9527         this.fireEvent('evententer', this, el, event);
9528     },
9529     
9530     onEventLeave: function (e, el,event,d) {
9531         this.fireEvent('eventleave', this, el, event);
9532     },
9533     
9534     onEventClick: function (e, el,event,d) {
9535         this.fireEvent('eventclick', this, el, event);
9536     },
9537     
9538     onMonthChange: function () {
9539         this.store.load();
9540     },
9541     
9542     onLoad: function () 
9543     {   
9544         this.calevents = [];
9545         var cal = this;
9546         if(this.store.getCount() > 0){
9547             this.store.data.each(function(d){
9548                cal.addItem({
9549                     id : d.data.id,
9550                     start: new Date(d.data.start_dt),
9551                     end : new Date(d.data.end_dt),
9552                     time : d.data.start_time,
9553                     title : d.data.title,
9554                     description : d.data.description,
9555                     venue : d.data.venue
9556                 });
9557             });
9558         }
9559         
9560         this.renderEvents();
9561         
9562         if(this.loadMask){
9563             this.maskEl.hide();
9564         }
9565     },
9566     
9567     onBeforeLoad: function()
9568     {
9569         this.clearEvents();
9570         
9571         if(this.loadMask){
9572             this.maskEl.show();
9573         }
9574     }
9575 });
9576
9577  
9578  /*
9579  * - LGPL
9580  *
9581  * element
9582  * 
9583  */
9584
9585 /**
9586  * @class Roo.bootstrap.Popover
9587  * @extends Roo.bootstrap.Component
9588  * Bootstrap Popover class
9589  * @cfg {String} html contents of the popover   (or false to use children..)
9590  * @cfg {String} title of popover (or false to hide)
9591  * @cfg {String} placement how it is placed
9592  * @cfg {String} trigger click || hover (or false to trigger manually)
9593  * @cfg {String} over what (parent or false to trigger manually.)
9594  * 
9595  * @constructor
9596  * Create a new Popover
9597  * @param {Object} config The config object
9598  */
9599
9600 Roo.bootstrap.Popover = function(config){
9601     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
9602 };
9603
9604 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
9605     
9606     title: 'Fill in a title',
9607     html: false,
9608     
9609     placement : 'right',
9610     trigger : 'hover', // hover
9611     
9612     over: 'parent',
9613     
9614     can_build_overlaid : false,
9615     
9616     getChildContainer : function()
9617     {
9618         return this.el.select('.popover-content',true).first();
9619     },
9620     
9621     getAutoCreate : function(){
9622          Roo.log('make popover?');
9623         var cfg = {
9624            cls : 'popover roo-dynamic',
9625            style: 'display:block',
9626            cn : [
9627                 {
9628                     cls : 'arrow'
9629                 },
9630                 {
9631                     cls : 'popover-inner',
9632                     cn : [
9633                         {
9634                             tag: 'h3',
9635                             cls: 'popover-title',
9636                             html : this.title
9637                         },
9638                         {
9639                             cls : 'popover-content',
9640                             html : this.html
9641                         }
9642                     ]
9643                     
9644                 }
9645            ]
9646         };
9647         
9648         return cfg;
9649     },
9650     setTitle: function(str)
9651     {
9652         this.el.select('.popover-title',true).first().dom.innerHTML = str;
9653     },
9654     setContent: function(str)
9655     {
9656         this.el.select('.popover-content',true).first().dom.innerHTML = str;
9657     },
9658     // as it get's added to the bottom of the page.
9659     onRender : function(ct, position)
9660     {
9661         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
9662         if(!this.el){
9663             var cfg = Roo.apply({},  this.getAutoCreate());
9664             cfg.id = Roo.id();
9665             
9666             if (this.cls) {
9667                 cfg.cls += ' ' + this.cls;
9668             }
9669             if (this.style) {
9670                 cfg.style = this.style;
9671             }
9672             Roo.log("adding to ")
9673             this.el = Roo.get(document.body).createChild(cfg, position);
9674             Roo.log(this.el);
9675         }
9676         this.initEvents();
9677     },
9678     
9679     initEvents : function()
9680     {
9681         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
9682         this.el.enableDisplayMode('block');
9683         this.el.hide();
9684         if (this.over === false) {
9685             return; 
9686         }
9687         if (this.triggers === false) {
9688             return;
9689         }
9690         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
9691         var triggers = this.trigger ? this.trigger.split(' ') : [];
9692         Roo.each(triggers, function(trigger) {
9693         
9694             if (trigger == 'click') {
9695                 on_el.on('click', this.toggle, this);
9696             } else if (trigger != 'manual') {
9697                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
9698                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
9699       
9700                 on_el.on(eventIn  ,this.enter, this);
9701                 on_el.on(eventOut, this.leave, this);
9702             }
9703         }, this);
9704         
9705     },
9706     
9707     
9708     // private
9709     timeout : null,
9710     hoverState : null,
9711     
9712     toggle : function () {
9713         this.hoverState == 'in' ? this.leave() : this.enter();
9714     },
9715     
9716     enter : function () {
9717        
9718     
9719         clearTimeout(this.timeout);
9720     
9721         this.hoverState = 'in'
9722     
9723         if (!this.delay || !this.delay.show) {
9724             this.show();
9725             return 
9726         }
9727         var _t = this;
9728         this.timeout = setTimeout(function () {
9729             if (_t.hoverState == 'in') {
9730                 _t.show();
9731             }
9732         }, this.delay.show)
9733     },
9734     leave : function() {
9735         clearTimeout(this.timeout);
9736     
9737         this.hoverState = 'out'
9738     
9739         if (!this.delay || !this.delay.hide) {
9740             this.hide();
9741             return 
9742         }
9743         var _t = this;
9744         this.timeout = setTimeout(function () {
9745             if (_t.hoverState == 'out') {
9746                 _t.hide();
9747             }
9748         }, this.delay.hide)
9749     },
9750     
9751     show : function (on_el)
9752     {
9753         if (!on_el) {
9754             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
9755         }
9756         // set content.
9757         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
9758         if (this.html !== false) {
9759             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
9760         }
9761         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
9762         if (!this.title.length) {
9763             this.el.select('.popover-title',true).hide();
9764         }
9765         
9766         var placement = typeof this.placement == 'function' ?
9767             this.placement.call(this, this.el, on_el) :
9768             this.placement;
9769             
9770         var autoToken = /\s?auto?\s?/i;
9771         var autoPlace = autoToken.test(placement);
9772         if (autoPlace) {
9773             placement = placement.replace(autoToken, '') || 'top';
9774         }
9775         
9776         //this.el.detach()
9777         //this.el.setXY([0,0]);
9778         this.el.show();
9779         this.el.dom.style.display='block';
9780         this.el.addClass(placement);
9781         
9782         //this.el.appendTo(on_el);
9783         
9784         var p = this.getPosition();
9785         var box = this.el.getBox();
9786         
9787         if (autoPlace) {
9788             // fixme..
9789         }
9790         var align = Roo.bootstrap.Popover.alignment[placement]
9791         this.el.alignTo(on_el, align[0],align[1]);
9792         //var arrow = this.el.select('.arrow',true).first();
9793         //arrow.set(align[2], 
9794         
9795         this.el.addClass('in');
9796         this.hoverState = null;
9797         
9798         if (this.el.hasClass('fade')) {
9799             // fade it?
9800         }
9801         
9802     },
9803     hide : function()
9804     {
9805         this.el.setXY([0,0]);
9806         this.el.removeClass('in');
9807         this.el.hide();
9808         
9809     }
9810     
9811 });
9812
9813 Roo.bootstrap.Popover.alignment = {
9814     'left' : ['r-l', [-10,0], 'right'],
9815     'right' : ['l-r', [10,0], 'left'],
9816     'bottom' : ['t-b', [0,10], 'top'],
9817     'top' : [ 'b-t', [0,-10], 'bottom']
9818 };
9819
9820  /*
9821  * - LGPL
9822  *
9823  * Progress
9824  * 
9825  */
9826
9827 /**
9828  * @class Roo.bootstrap.Progress
9829  * @extends Roo.bootstrap.Component
9830  * Bootstrap Progress class
9831  * @cfg {Boolean} striped striped of the progress bar
9832  * @cfg {Boolean} active animated of the progress bar
9833  * 
9834  * 
9835  * @constructor
9836  * Create a new Progress
9837  * @param {Object} config The config object
9838  */
9839
9840 Roo.bootstrap.Progress = function(config){
9841     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
9842 };
9843
9844 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
9845     
9846     striped : false,
9847     active: false,
9848     
9849     getAutoCreate : function(){
9850         var cfg = {
9851             tag: 'div',
9852             cls: 'progress'
9853         };
9854         
9855         
9856         if(this.striped){
9857             cfg.cls += ' progress-striped';
9858         }
9859       
9860         if(this.active){
9861             cfg.cls += ' active';
9862         }
9863         
9864         
9865         return cfg;
9866     }
9867    
9868 });
9869
9870  
9871
9872  /*
9873  * - LGPL
9874  *
9875  * ProgressBar
9876  * 
9877  */
9878
9879 /**
9880  * @class Roo.bootstrap.ProgressBar
9881  * @extends Roo.bootstrap.Component
9882  * Bootstrap ProgressBar class
9883  * @cfg {Number} aria_valuenow aria-value now
9884  * @cfg {Number} aria_valuemin aria-value min
9885  * @cfg {Number} aria_valuemax aria-value max
9886  * @cfg {String} label label for the progress bar
9887  * @cfg {String} panel (success | info | warning | danger )
9888  * @cfg {String} role role of the progress bar
9889  * @cfg {String} sr_only text
9890  * 
9891  * 
9892  * @constructor
9893  * Create a new ProgressBar
9894  * @param {Object} config The config object
9895  */
9896
9897 Roo.bootstrap.ProgressBar = function(config){
9898     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
9899 };
9900
9901 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
9902     
9903     aria_valuenow : 0,
9904     aria_valuemin : 0,
9905     aria_valuemax : 100,
9906     label : false,
9907     panel : false,
9908     role : false,
9909     sr_only: false,
9910     
9911     getAutoCreate : function()
9912     {
9913         
9914         var cfg = {
9915             tag: 'div',
9916             cls: 'progress-bar',
9917             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
9918         };
9919         
9920         if(this.sr_only){
9921             cfg.cn = {
9922                 tag: 'span',
9923                 cls: 'sr-only',
9924                 html: this.sr_only
9925             }
9926         }
9927         
9928         if(this.role){
9929             cfg.role = this.role;
9930         }
9931         
9932         if(this.aria_valuenow){
9933             cfg['aria-valuenow'] = this.aria_valuenow;
9934         }
9935         
9936         if(this.aria_valuemin){
9937             cfg['aria-valuemin'] = this.aria_valuemin;
9938         }
9939         
9940         if(this.aria_valuemax){
9941             cfg['aria-valuemax'] = this.aria_valuemax;
9942         }
9943         
9944         if(this.label && !this.sr_only){
9945             cfg.html = this.label;
9946         }
9947         
9948         if(this.panel){
9949             cfg.cls += ' progress-bar-' + this.panel;
9950         }
9951         
9952         return cfg;
9953     },
9954     
9955     update : function(aria_valuenow)
9956     {
9957         this.aria_valuenow = aria_valuenow;
9958         
9959         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
9960     }
9961    
9962 });
9963
9964  
9965
9966  /*
9967  * - LGPL
9968  *
9969  * TabPanel
9970  * 
9971  */
9972
9973 /**
9974  * @class Roo.bootstrap.TabPanel
9975  * @extends Roo.bootstrap.Component
9976  * Bootstrap TabPanel class
9977  * @cfg {Boolean} active panel active
9978  * @cfg {String} html panel content
9979  * @cfg {String} tabId tab relate id
9980  * 
9981  * 
9982  * @constructor
9983  * Create a new TabPanel
9984  * @param {Object} config The config object
9985  */
9986
9987 Roo.bootstrap.TabPanel = function(config){
9988     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
9989 };
9990
9991 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
9992     
9993     active: false,
9994     html: false,
9995     tabId: false,
9996     
9997     getAutoCreate : function(){
9998         var cfg = {
9999             tag: 'div',
10000             cls: 'tab-pane',
10001             html: this.html || ''
10002         };
10003         
10004         if(this.active){
10005             cfg.cls += ' active';
10006         }
10007         
10008         if(this.tabId){
10009             cfg.tabId = this.tabId;
10010         }
10011         
10012         return cfg;
10013     }
10014    
10015 });
10016
10017  
10018
10019  /*
10020  * - LGPL
10021  *
10022  * DateField
10023  * 
10024  */
10025
10026 /**
10027  * @class Roo.bootstrap.DateField
10028  * @extends Roo.bootstrap.Input
10029  * Bootstrap DateField class
10030  * @cfg {Number} weekStart default 0
10031  * @cfg {Number} weekStart default 0
10032  * @cfg {Number} viewMode default empty, (months|years)
10033  * @cfg {Number} minViewMode default empty, (months|years)
10034  * @cfg {Number} startDate default -Infinity
10035  * @cfg {Number} endDate default Infinity
10036  * @cfg {Boolean} todayHighlight default false
10037  * @cfg {Boolean} todayBtn default false
10038  * @cfg {Boolean} calendarWeeks default false
10039  * @cfg {Object} daysOfWeekDisabled default empty
10040  * 
10041  * @cfg {Boolean} keyboardNavigation default true
10042  * @cfg {String} language default en
10043  * 
10044  * @constructor
10045  * Create a new DateField
10046  * @param {Object} config The config object
10047  */
10048
10049 Roo.bootstrap.DateField = function(config){
10050     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10051 };
10052
10053 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10054     
10055     /**
10056      * @cfg {String} format
10057      * The default date format string which can be overriden for localization support.  The format must be
10058      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10059      */
10060     format : "m/d/y",
10061     /**
10062      * @cfg {String} altFormats
10063      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10064      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10065      */
10066     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10067     
10068     weekStart : 0,
10069     
10070     viewMode : '',
10071     
10072     minViewMode : '',
10073     
10074     todayHighlight : false,
10075     
10076     todayBtn: false,
10077     
10078     language: 'en',
10079     
10080     keyboardNavigation: true,
10081     
10082     calendarWeeks: false,
10083     
10084     startDate: -Infinity,
10085     
10086     endDate: Infinity,
10087     
10088     daysOfWeekDisabled: [],
10089     
10090     _events: [],
10091     
10092     UTCDate: function()
10093     {
10094         return new Date(Date.UTC.apply(Date, arguments));
10095     },
10096     
10097     UTCToday: function()
10098     {
10099         var today = new Date();
10100         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
10101     },
10102     
10103     getDate: function() {
10104             var d = this.getUTCDate();
10105             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
10106     },
10107     
10108     getUTCDate: function() {
10109             return this.date;
10110     },
10111     
10112     setDate: function(d) {
10113             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
10114     },
10115     
10116     setUTCDate: function(d) {
10117             this.date = d;
10118             this.setValue(this.formatDate(this.date));
10119     },
10120         
10121     onRender: function(ct, position)
10122     {
10123         
10124         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
10125         
10126         this.language = this.language || 'en';
10127         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
10128         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
10129         
10130         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
10131         this.format = this.format || 'm/d/y';
10132         this.isInline = false;
10133         this.isInput = true;
10134         this.component = this.el.select('.add-on', true).first() || false;
10135         this.component = (this.component && this.component.length === 0) ? false : this.component;
10136         this.hasInput = this.component && this.inputEL().length;
10137         
10138         if (typeof(this.minViewMode === 'string')) {
10139             switch (this.minViewMode) {
10140                 case 'months':
10141                     this.minViewMode = 1;
10142                     break;
10143                 case 'years':
10144                     this.minViewMode = 2;
10145                     break;
10146                 default:
10147                     this.minViewMode = 0;
10148                     break;
10149             }
10150         }
10151         
10152         if (typeof(this.viewMode === 'string')) {
10153             switch (this.viewMode) {
10154                 case 'months':
10155                     this.viewMode = 1;
10156                     break;
10157                 case 'years':
10158                     this.viewMode = 2;
10159                     break;
10160                 default:
10161                     this.viewMode = 0;
10162                     break;
10163             }
10164         }
10165                 
10166         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
10167         
10168         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10169         
10170         this.picker().on('mousedown', this.onMousedown, this);
10171         this.picker().on('click', this.onClick, this);
10172         
10173         this.picker().addClass('datepicker-dropdown');
10174         
10175         this.startViewMode = this.viewMode;
10176         
10177         
10178         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
10179             if(!this.calendarWeeks){
10180                 v.remove();
10181                 return;
10182             };
10183             
10184             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
10185             v.attr('colspan', function(i, val){
10186                 return parseInt(val) + 1;
10187             });
10188         })
10189                         
10190         
10191         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
10192         
10193         this.setStartDate(this.startDate);
10194         this.setEndDate(this.endDate);
10195         
10196         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
10197         
10198         this.fillDow();
10199         this.fillMonths();
10200         this.update();
10201         this.showMode();
10202         
10203         if(this.isInline) {
10204             this.show();
10205         }
10206     },
10207     
10208     picker : function()
10209     {
10210         return this.el.select('.datepicker', true).first();
10211     },
10212     
10213     fillDow: function()
10214     {
10215         var dowCnt = this.weekStart;
10216         
10217         var dow = {
10218             tag: 'tr',
10219             cn: [
10220                 
10221             ]
10222         };
10223         
10224         if(this.calendarWeeks){
10225             dow.cn.push({
10226                 tag: 'th',
10227                 cls: 'cw',
10228                 html: '&nbsp;'
10229             })
10230         }
10231         
10232         while (dowCnt < this.weekStart + 7) {
10233             dow.cn.push({
10234                 tag: 'th',
10235                 cls: 'dow',
10236                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
10237             });
10238         }
10239         
10240         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
10241     },
10242     
10243     fillMonths: function()
10244     {    
10245         var i = 0
10246         var months = this.picker().select('>.datepicker-months td', true).first();
10247         
10248         months.dom.innerHTML = '';
10249         
10250         while (i < 12) {
10251             var month = {
10252                 tag: 'span',
10253                 cls: 'month',
10254                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
10255             }
10256             
10257             months.createChild(month);
10258         }
10259         
10260     },
10261     
10262     update: function(){
10263         
10264         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
10265         
10266         if (this.date < this.startDate) {
10267             this.viewDate = new Date(this.startDate);
10268         } else if (this.date > this.endDate) {
10269             this.viewDate = new Date(this.endDate);
10270         } else {
10271             this.viewDate = new Date(this.date);
10272         }
10273         
10274         this.fill();
10275     },
10276     
10277     fill: function() {
10278         var d = new Date(this.viewDate),
10279                 year = d.getUTCFullYear(),
10280                 month = d.getUTCMonth(),
10281                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
10282                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
10283                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
10284                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
10285                 currentDate = this.date && this.date.valueOf(),
10286                 today = this.UTCToday();
10287         
10288         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
10289         
10290 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
10291         
10292 //        this.picker.select('>tfoot th.today').
10293 //                                              .text(dates[this.language].today)
10294 //                                              .toggle(this.todayBtn !== false);
10295     
10296         this.updateNavArrows();
10297         this.fillMonths();
10298                                                 
10299         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
10300         
10301         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
10302          
10303         prevMonth.setUTCDate(day);
10304         
10305         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
10306         
10307         var nextMonth = new Date(prevMonth);
10308         
10309         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
10310         
10311         nextMonth = nextMonth.valueOf();
10312         
10313         var fillMonths = false;
10314         
10315         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
10316         
10317         while(prevMonth.valueOf() < nextMonth) {
10318             var clsName = '';
10319             
10320             if (prevMonth.getUTCDay() === this.weekStart) {
10321                 if(fillMonths){
10322                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
10323                 }
10324                     
10325                 fillMonths = {
10326                     tag: 'tr',
10327                     cn: []
10328                 };
10329                 
10330                 if(this.calendarWeeks){
10331                     // ISO 8601: First week contains first thursday.
10332                     // ISO also states week starts on Monday, but we can be more abstract here.
10333                     var
10334                     // Start of current week: based on weekstart/current date
10335                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
10336                     // Thursday of this week
10337                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
10338                     // First Thursday of year, year from thursday
10339                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
10340                     // Calendar week: ms between thursdays, div ms per day, div 7 days
10341                     calWeek =  (th - yth) / 864e5 / 7 + 1;
10342                     
10343                     fillMonths.cn.push({
10344                         tag: 'td',
10345                         cls: 'cw',
10346                         html: calWeek
10347                     });
10348                 }
10349             }
10350             
10351             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
10352                 clsName += ' old';
10353             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
10354                 clsName += ' new';
10355             }
10356             if (this.todayHighlight &&
10357                 prevMonth.getUTCFullYear() == today.getFullYear() &&
10358                 prevMonth.getUTCMonth() == today.getMonth() &&
10359                 prevMonth.getUTCDate() == today.getDate()) {
10360                 clsName += ' today';
10361             }
10362             
10363             if (currentDate && prevMonth.valueOf() === currentDate) {
10364                 clsName += ' active';
10365             }
10366             
10367             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
10368                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
10369                     clsName += ' disabled';
10370             }
10371             
10372             fillMonths.cn.push({
10373                 tag: 'td',
10374                 cls: 'day ' + clsName,
10375                 html: prevMonth.getDate()
10376             })
10377             
10378             prevMonth.setDate(prevMonth.getDate()+1);
10379         }
10380           
10381         var currentYear = this.date && this.date.getUTCFullYear();
10382         var currentMonth = this.date && this.date.getUTCMonth();
10383         
10384         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
10385         
10386         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
10387             v.removeClass('active');
10388             
10389             if(currentYear === year && k === currentMonth){
10390                 v.addClass('active');
10391             }
10392             
10393             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
10394                 v.addClass('disabled');
10395             }
10396             
10397         });
10398         
10399         
10400         year = parseInt(year/10, 10) * 10;
10401         
10402         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
10403         
10404         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
10405         
10406         year -= 1;
10407         for (var i = -1; i < 11; i++) {
10408             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
10409                 tag: 'span',
10410                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
10411                 html: year
10412             })
10413             
10414             year += 1;
10415         }
10416     },
10417     
10418     showMode: function(dir) {
10419         if (dir) {
10420             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
10421         }
10422         Roo.each(this.picker().select('>div',true).elements, function(v){
10423             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10424             v.hide();
10425         });
10426         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
10427     },
10428     
10429     place: function()
10430     {
10431         if(this.isInline) return;
10432         
10433         this.picker().removeClass(['bottom', 'top']);
10434         
10435         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
10436             /*
10437              * place to the top of element!
10438              *
10439              */
10440             
10441             this.picker().addClass('top');
10442             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
10443             
10444             return;
10445         }
10446         
10447         this.picker().addClass('bottom');
10448         
10449         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
10450     },
10451     
10452     parseDate : function(value){
10453         if(!value || value instanceof Date){
10454             return value;
10455         }
10456         var v = Date.parseDate(value, this.format);
10457         if (!v && this.useIso) {
10458             v = Date.parseDate(value, 'Y-m-d');
10459         }
10460         if(!v && this.altFormats){
10461             if(!this.altFormatsArray){
10462                 this.altFormatsArray = this.altFormats.split("|");
10463             }
10464             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
10465                 v = Date.parseDate(value, this.altFormatsArray[i]);
10466             }
10467         }
10468         return v;
10469     },
10470     
10471     formatDate : function(date, fmt){
10472         return (!date || !(date instanceof Date)) ?
10473         date : date.dateFormat(fmt || this.format);
10474     },
10475     
10476     onFocus : function()
10477     {
10478         Roo.bootstrap.DateField.superclass.onFocus.call(this);
10479         this.show();
10480     },
10481     
10482     onBlur : function()
10483     {
10484         Roo.bootstrap.DateField.superclass.onBlur.call(this);
10485         this.hide();
10486     },
10487     
10488     show : function()
10489     {
10490         this.picker().show();
10491         this.update();
10492         this.place();
10493     },
10494     
10495     hide : function()
10496     {
10497         if(this.isInline) return;
10498         this.picker().hide();
10499         this.viewMode = this.startViewMode;
10500         this.showMode();
10501         
10502     },
10503     
10504     onMousedown: function(e){
10505         e.stopPropagation();
10506         e.preventDefault();
10507     },
10508     
10509     keyup: function(e){
10510         Roo.bootstrap.DateField.superclass.keyup.call(this);
10511         this.update();
10512         
10513     },
10514     
10515     fireKey: function(e){
10516         if (!this.picker().isVisible()){
10517             if (e.keyCode == 27) // allow escape to hide and re-show picker
10518                 this.show();
10519             return;
10520         }
10521         var dateChanged = false,
10522         dir, day, month,
10523         newDate, newViewDate;
10524         switch(e.keyCode){
10525             case 27: // escape
10526                 this.hide();
10527                 e.preventDefault();
10528                 break;
10529             case 37: // left
10530             case 39: // right
10531                 if (!this.keyboardNavigation) break;
10532                 dir = e.keyCode == 37 ? -1 : 1;
10533                 
10534                 if (e.ctrlKey){
10535                     newDate = this.moveYear(this.date, dir);
10536                     newViewDate = this.moveYear(this.viewDate, dir);
10537                 } else if (e.shiftKey){
10538                     newDate = this.moveMonth(this.date, dir);
10539                     newViewDate = this.moveMonth(this.viewDate, dir);
10540                 } else {
10541                     newDate = new Date(this.date);
10542                     newDate.setUTCDate(this.date.getUTCDate() + dir);
10543                     newViewDate = new Date(this.viewDate);
10544                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
10545                 }
10546                 if (this.dateWithinRange(newDate)){
10547                     this.date = newDate;
10548                     this.viewDate = newViewDate;
10549                     this.setValue(this.formatDate(this.date));
10550                     this.update();
10551                     e.preventDefault();
10552                     dateChanged = true;
10553                 }
10554                 break;
10555             case 38: // up
10556             case 40: // down
10557                 if (!this.keyboardNavigation) break;
10558                 dir = e.keyCode == 38 ? -1 : 1;
10559                 if (e.ctrlKey){
10560                     newDate = this.moveYear(this.date, dir);
10561                     newViewDate = this.moveYear(this.viewDate, dir);
10562                 } else if (e.shiftKey){
10563                     newDate = this.moveMonth(this.date, dir);
10564                     newViewDate = this.moveMonth(this.viewDate, dir);
10565                 } else {
10566                     newDate = new Date(this.date);
10567                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
10568                     newViewDate = new Date(this.viewDate);
10569                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
10570                 }
10571                 if (this.dateWithinRange(newDate)){
10572                     this.date = newDate;
10573                     this.viewDate = newViewDate;
10574                     this.setValue(this.formatDate(this.date));
10575                     this.update();
10576                     e.preventDefault();
10577                     dateChanged = true;
10578                 }
10579                 break;
10580             case 13: // enter
10581                 this.setValue(this.formatDate(this.date));
10582                 this.hide();
10583                 e.preventDefault();
10584                 break;
10585             case 9: // tab
10586                 this.setValue(this.formatDate(this.date));
10587                 this.hide();
10588                 break;
10589         }
10590     },
10591     
10592     
10593     onClick: function(e) {
10594         e.stopPropagation();
10595         e.preventDefault();
10596         
10597         var target = e.getTarget();
10598         
10599         if(target.nodeName.toLowerCase() === 'i'){
10600             target = Roo.get(target).dom.parentNode;
10601         }
10602         
10603         var nodeName = target.nodeName;
10604         var className = target.className;
10605         var html = target.innerHTML;
10606         
10607         switch(nodeName.toLowerCase()) {
10608             case 'th':
10609                 switch(className) {
10610                     case 'switch':
10611                         this.showMode(1);
10612                         break;
10613                     case 'prev':
10614                     case 'next':
10615                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
10616                         switch(this.viewMode){
10617                                 case 0:
10618                                         this.viewDate = this.moveMonth(this.viewDate, dir);
10619                                         break;
10620                                 case 1:
10621                                 case 2:
10622                                         this.viewDate = this.moveYear(this.viewDate, dir);
10623                                         break;
10624                         }
10625                         this.fill();
10626                         break;
10627                     case 'today':
10628                         var date = new Date();
10629                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
10630                         this.fill()
10631                         this.setValue(this.formatDate(this.date));
10632                         this.hide();
10633                         break;
10634                 }
10635                 break;
10636             case 'span':
10637                 if (className.indexOf('disabled') === -1) {
10638                     this.viewDate.setUTCDate(1);
10639                     if (className.indexOf('month') !== -1) {
10640                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
10641                     } else {
10642                         var year = parseInt(html, 10) || 0;
10643                         this.viewDate.setUTCFullYear(year);
10644                         
10645                     }
10646                     this.showMode(-1);
10647                     this.fill();
10648                 }
10649                 break;
10650                 
10651             case 'td':
10652                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
10653                     var day = parseInt(html, 10) || 1;
10654                     var year = this.viewDate.getUTCFullYear(),
10655                         month = this.viewDate.getUTCMonth();
10656
10657                     if (className.indexOf('old') !== -1) {
10658                         if(month === 0 ){
10659                             month = 11;
10660                             year -= 1;
10661                         }else{
10662                             month -= 1;
10663                         }
10664                     } else if (className.indexOf('new') !== -1) {
10665                         if (month == 11) {
10666                             month = 0;
10667                             year += 1;
10668                         } else {
10669                             month += 1;
10670                         }
10671                     }
10672                     this.date = this.UTCDate(year, month, day,0,0,0,0);
10673                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
10674                     this.fill();
10675                     this.setValue(this.formatDate(this.date));
10676                     this.hide();
10677                 }
10678                 break;
10679         }
10680     },
10681     
10682     setStartDate: function(startDate){
10683         this.startDate = startDate || -Infinity;
10684         if (this.startDate !== -Infinity) {
10685             this.startDate = this.parseDate(this.startDate);
10686         }
10687         this.update();
10688         this.updateNavArrows();
10689     },
10690
10691     setEndDate: function(endDate){
10692         this.endDate = endDate || Infinity;
10693         if (this.endDate !== Infinity) {
10694             this.endDate = this.parseDate(this.endDate);
10695         }
10696         this.update();
10697         this.updateNavArrows();
10698     },
10699     
10700     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
10701         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
10702         if (typeof(this.daysOfWeekDisabled) !== 'object') {
10703             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
10704         }
10705         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
10706             return parseInt(d, 10);
10707         });
10708         this.update();
10709         this.updateNavArrows();
10710     },
10711     
10712     updateNavArrows: function() {
10713         var d = new Date(this.viewDate),
10714         year = d.getUTCFullYear(),
10715         month = d.getUTCMonth();
10716         
10717         Roo.each(this.picker().select('.prev', true).elements, function(v){
10718             v.show();
10719             switch (this.viewMode) {
10720                 case 0:
10721
10722                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
10723                         v.hide();
10724                     }
10725                     break;
10726                 case 1:
10727                 case 2:
10728                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
10729                         v.hide();
10730                     }
10731                     break;
10732             }
10733         });
10734         
10735         Roo.each(this.picker().select('.next', true).elements, function(v){
10736             v.show();
10737             switch (this.viewMode) {
10738                 case 0:
10739
10740                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
10741                         v.hide();
10742                     }
10743                     break;
10744                 case 1:
10745                 case 2:
10746                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
10747                         v.hide();
10748                     }
10749                     break;
10750             }
10751         })
10752     },
10753     
10754     moveMonth: function(date, dir){
10755         if (!dir) return date;
10756         var new_date = new Date(date.valueOf()),
10757         day = new_date.getUTCDate(),
10758         month = new_date.getUTCMonth(),
10759         mag = Math.abs(dir),
10760         new_month, test;
10761         dir = dir > 0 ? 1 : -1;
10762         if (mag == 1){
10763             test = dir == -1
10764             // If going back one month, make sure month is not current month
10765             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
10766             ? function(){
10767                 return new_date.getUTCMonth() == month;
10768             }
10769             // If going forward one month, make sure month is as expected
10770             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
10771             : function(){
10772                 return new_date.getUTCMonth() != new_month;
10773             };
10774             new_month = month + dir;
10775             new_date.setUTCMonth(new_month);
10776             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
10777             if (new_month < 0 || new_month > 11)
10778                 new_month = (new_month + 12) % 12;
10779         } else {
10780             // For magnitudes >1, move one month at a time...
10781             for (var i=0; i<mag; i++)
10782                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
10783                 new_date = this.moveMonth(new_date, dir);
10784             // ...then reset the day, keeping it in the new month
10785             new_month = new_date.getUTCMonth();
10786             new_date.setUTCDate(day);
10787             test = function(){
10788                 return new_month != new_date.getUTCMonth();
10789             };
10790         }
10791         // Common date-resetting loop -- if date is beyond end of month, make it
10792         // end of month
10793         while (test()){
10794             new_date.setUTCDate(--day);
10795             new_date.setUTCMonth(new_month);
10796         }
10797         return new_date;
10798     },
10799
10800     moveYear: function(date, dir){
10801         return this.moveMonth(date, dir*12);
10802     },
10803
10804     dateWithinRange: function(date){
10805         return date >= this.startDate && date <= this.endDate;
10806     },
10807
10808     
10809     remove: function() {
10810         this.picker().remove();
10811     }
10812    
10813 });
10814
10815 Roo.apply(Roo.bootstrap.DateField,  {
10816     
10817     head : {
10818         tag: 'thead',
10819         cn: [
10820         {
10821             tag: 'tr',
10822             cn: [
10823             {
10824                 tag: 'th',
10825                 cls: 'prev',
10826                 html: '<i class="icon-arrow-left"/>'
10827             },
10828             {
10829                 tag: 'th',
10830                 cls: 'switch',
10831                 colspan: '5'
10832             },
10833             {
10834                 tag: 'th',
10835                 cls: 'next',
10836                 html: '<i class="icon-arrow-right"/>'
10837             }
10838
10839             ]
10840         }
10841         ]
10842     },
10843     
10844     content : {
10845         tag: 'tbody',
10846         cn: [
10847         {
10848             tag: 'tr',
10849             cn: [
10850             {
10851                 tag: 'td',
10852                 colspan: '7'
10853             }
10854             ]
10855         }
10856         ]
10857     },
10858     
10859     footer : {
10860         tag: 'tfoot',
10861         cn: [
10862         {
10863             tag: 'tr',
10864             cn: [
10865             {
10866                 tag: 'th',
10867                 colspan: '7',
10868                 cls: 'today'
10869             }
10870                     
10871             ]
10872         }
10873         ]
10874     },
10875     
10876     dates:{
10877         en: {
10878             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
10879             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
10880             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
10881             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
10882             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
10883             today: "Today"
10884         }
10885     },
10886     
10887     modes: [
10888     {
10889         clsName: 'days',
10890         navFnc: 'Month',
10891         navStep: 1
10892     },
10893     {
10894         clsName: 'months',
10895         navFnc: 'FullYear',
10896         navStep: 1
10897     },
10898     {
10899         clsName: 'years',
10900         navFnc: 'FullYear',
10901         navStep: 10
10902     }]
10903 });
10904
10905 Roo.apply(Roo.bootstrap.DateField,  {
10906   
10907     template : {
10908         tag: 'div',
10909         cls: 'datepicker dropdown-menu',
10910         cn: [
10911         {
10912             tag: 'div',
10913             cls: 'datepicker-days',
10914             cn: [
10915             {
10916                 tag: 'table',
10917                 cls: 'table-condensed',
10918                 cn:[
10919                 Roo.bootstrap.DateField.head,
10920                 {
10921                     tag: 'tbody'
10922                 },
10923                 Roo.bootstrap.DateField.footer
10924                 ]
10925             }
10926             ]
10927         },
10928         {
10929             tag: 'div',
10930             cls: 'datepicker-months',
10931             cn: [
10932             {
10933                 tag: 'table',
10934                 cls: 'table-condensed',
10935                 cn:[
10936                 Roo.bootstrap.DateField.head,
10937                 Roo.bootstrap.DateField.content,
10938                 Roo.bootstrap.DateField.footer
10939                 ]
10940             }
10941             ]
10942         },
10943         {
10944             tag: 'div',
10945             cls: 'datepicker-years',
10946             cn: [
10947             {
10948                 tag: 'table',
10949                 cls: 'table-condensed',
10950                 cn:[
10951                 Roo.bootstrap.DateField.head,
10952                 Roo.bootstrap.DateField.content,
10953                 Roo.bootstrap.DateField.footer
10954                 ]
10955             }
10956             ]
10957         }
10958         ]
10959     }
10960 });
10961
10962  
10963
10964  /*
10965  * - LGPL
10966  *
10967  * CheckBox
10968  * 
10969  */
10970
10971 /**
10972  * @class Roo.bootstrap.CheckBox
10973  * @extends Roo.bootstrap.Input
10974  * Bootstrap CheckBox class
10975  * 
10976  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
10977  * @cfg {String} boxLabel The text that appears beside the checkbox
10978  * @cfg {Boolean} checked initnal the element
10979  * 
10980  * @constructor
10981  * Create a new CheckBox
10982  * @param {Object} config The config object
10983  */
10984
10985 Roo.bootstrap.CheckBox = function(config){
10986     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
10987    
10988         this.addEvents({
10989             /**
10990             * @event check
10991             * Fires when the element is checked or unchecked.
10992             * @param {Roo.bootstrap.CheckBox} this This input
10993             * @param {Boolean} checked The new checked value
10994             */
10995            check : true
10996         });
10997 };
10998
10999 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
11000     
11001     inputType: 'checkbox',
11002     value: 1,
11003     valueOff: 0,
11004     boxLabel: false,
11005     checked: false,
11006     
11007     getAutoCreate : function()
11008     {
11009         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11010         
11011         var id = Roo.id();
11012         
11013         var cfg = {};
11014         
11015         cfg.cls = 'form-group' //input-group
11016         
11017         var input =  {
11018             tag: 'input',
11019             id : id,
11020             type : this.inputType,
11021             value : (!this.checked) ? this.valueOff : this.value,
11022             cls : 'form-box',
11023             placeholder : this.placeholder || ''
11024             
11025         };
11026         
11027         if (this.disabled) {
11028             input.disabled=true;
11029         }
11030         
11031         if(this.checked){
11032             input.checked = this.checked;
11033         }
11034         
11035         if (this.name) {
11036             input.name = this.name;
11037         }
11038         
11039         if (this.size) {
11040             input.cls += ' input-' + this.size;
11041         }
11042         
11043         var settings=this;
11044         ['xs','sm','md','lg'].map(function(size){
11045             if (settings[size]) {
11046                 cfg.cls += ' col-' + size + '-' + settings[size];
11047             }
11048         });
11049         
11050         var inputblock = input;
11051         
11052         if (this.before || this.after) {
11053             
11054             inputblock = {
11055                 cls : 'input-group',
11056                 cn :  [] 
11057             };
11058             if (this.before) {
11059                 inputblock.cn.push({
11060                     tag :'span',
11061                     cls : 'input-group-addon',
11062                     html : this.before
11063                 });
11064             }
11065             inputblock.cn.push(input);
11066             if (this.after) {
11067                 inputblock.cn.push({
11068                     tag :'span',
11069                     cls : 'input-group-addon',
11070                     html : this.after
11071                 });
11072             }
11073             
11074         };
11075         
11076         if (align ==='left' && this.fieldLabel.length) {
11077                 Roo.log("left and has label");
11078                 cfg.cn = [
11079                     
11080                     {
11081                         tag: 'label',
11082                         'for' :  id,
11083                         cls : 'control-label col-md-' + this.labelWidth,
11084                         html : this.fieldLabel
11085                         
11086                     },
11087                     {
11088                         cls : "col-md-" + (12 - this.labelWidth), 
11089                         cn: [
11090                             inputblock
11091                         ]
11092                     }
11093                     
11094                 ];
11095         } else if ( this.fieldLabel.length) {
11096                 Roo.log(" label");
11097                  cfg.cn = [
11098                    
11099                     {
11100                         tag: 'label',
11101                         'for': id,
11102                         cls: 'control-label box-input-label',
11103                         //cls : 'input-group-addon',
11104                         html : this.fieldLabel
11105                         
11106                     },
11107                     
11108                     inputblock
11109                     
11110                 ];
11111
11112         } else {
11113             
11114                    Roo.log(" no label && no align");
11115                 cfg.cn = [
11116                     
11117                         inputblock
11118                     
11119                 ];
11120                 
11121                 
11122         };
11123         
11124         if(this.boxLabel){
11125             cfg.cn.push({
11126                 tag: 'span',
11127                 'for': id,
11128                 cls: 'box-label',
11129                 html: this.boxLabel
11130             })
11131         }
11132         
11133         return cfg;
11134         
11135     },
11136     
11137     /**
11138      * return the real input element.
11139      */
11140     inputEl: function ()
11141     {
11142         return this.el.select('input.form-box',true).first();
11143     },
11144     
11145     initEvents : function()
11146     {
11147         Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
11148         
11149         this.inputEl().on('click', this.onClick,  this);
11150         
11151     },
11152     
11153     onClick : function()
11154     {   
11155         this.setChecked(!this.checked);
11156     },
11157     
11158     setChecked : function(state,suppressEvent)
11159     {
11160         this.checked = state;
11161         
11162         if(suppressEvent !== true){
11163             this.fireEvent('check', this, state);
11164         }
11165         
11166         this.inputEl().dom.value = state ? this.value : this.valueOff;
11167         
11168     }
11169 });
11170
11171  
11172 /*
11173  * - LGPL
11174  *
11175  * Radio
11176  * 
11177  */
11178
11179 /**
11180  * @class Roo.bootstrap.Radio
11181  * @extends Roo.bootstrap.CheckBox
11182  * Bootstrap Radio class
11183
11184  * @constructor
11185  * Create a new Radio
11186  * @param {Object} config The config object
11187  */
11188
11189 Roo.bootstrap.Radio = function(config){
11190     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
11191    
11192 };
11193
11194 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
11195     
11196     inputType: 'radio',
11197     
11198     getAutoCreate : function()
11199     {
11200         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11201         
11202         var id = Roo.id();
11203         
11204         var cfg = {};
11205         
11206         cfg.cls = 'form-group' //input-group
11207         
11208         var input =  {
11209             tag: 'input',
11210             id : id,
11211             type : this.inputType,
11212             value : (!this.checked) ? this.valueOff : this.value,
11213             cls : 'form-box',
11214             placeholder : this.placeholder || ''
11215             
11216         };
11217         
11218         if (this.disabled) {
11219             input.disabled=true;
11220         }
11221         
11222         if(this.checked){
11223             input.checked = this.checked;
11224         }
11225         
11226         if (this.name) {
11227             input.name = this.name;
11228         }
11229         
11230         if (this.size) {
11231             input.cls += ' input-' + this.size;
11232         }
11233         
11234         var settings=this;
11235         ['xs','sm','md','lg'].map(function(size){
11236             if (settings[size]) {
11237                 cfg.cls += ' col-' + size + '-' + settings[size];
11238             }
11239         });
11240         
11241         var inputblock = input;
11242         
11243         if (this.before || this.after) {
11244             
11245             inputblock = {
11246                 cls : 'input-group',
11247                 cn :  [] 
11248             };
11249             if (this.before) {
11250                 inputblock.cn.push({
11251                     tag :'span',
11252                     cls : 'input-group-addon',
11253                     html : this.before
11254                 });
11255             }
11256             inputblock.cn.push(input);
11257             if (this.after) {
11258                 inputblock.cn.push({
11259                     tag :'span',
11260                     cls : 'input-group-addon',
11261                     html : this.after
11262                 });
11263             }
11264             
11265         };
11266         
11267         if (align ==='left' && this.fieldLabel.length) {
11268                 Roo.log("left and has label");
11269                 cfg.cn = [
11270                     
11271                     {
11272                         tag: 'label',
11273                         'for' :  id,
11274                         cls : 'control-label col-md-' + this.labelWidth,
11275                         html : this.fieldLabel
11276                         
11277                     },
11278                     {
11279                         cls : "col-md-" + (12 - this.labelWidth), 
11280                         cn: [
11281                             inputblock
11282                         ]
11283                     }
11284                     
11285                 ];
11286         } else if ( this.fieldLabel.length) {
11287                 Roo.log(" label");
11288                  cfg.cn = [
11289                    
11290                     {
11291                         tag: 'label',
11292                         'for': id,
11293                         cls: 'control-label box-input-label',
11294                         //cls : 'input-group-addon',
11295                         html : this.fieldLabel
11296                         
11297                     },
11298                     
11299                     inputblock
11300                     
11301                 ];
11302
11303         } else {
11304             
11305                    Roo.log(" no label && no align");
11306                 cfg.cn = [
11307                     
11308                         inputblock
11309                     
11310                 ];
11311                 
11312                 
11313         };
11314         
11315         if(this.boxLabel){
11316             cfg.cn.push({
11317                 tag: 'span',
11318                 'for': id,
11319                 cls: 'box-label',
11320                 html: this.boxLabel
11321             })
11322         }
11323         
11324         return cfg;
11325         
11326     },
11327     
11328     onClick : function()
11329     {   
11330         this.setChecked(true);
11331     },
11332     
11333     setChecked : function(state,suppressEvent)
11334     {
11335         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
11336             v.checked = false;
11337         });
11338         
11339         this.checked = state;
11340         
11341         if(suppressEvent !== true){
11342             this.fireEvent('check', this, state);
11343         }
11344         
11345         this.inputEl().dom.value = state ? this.value : this.valueOff;
11346         
11347     },
11348     
11349     getGroupValue : function()
11350     {
11351         if(typeof(this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true)) == 'undefined'){
11352             return '';
11353         }
11354         
11355         return this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true).value;
11356     },
11357     
11358     /**
11359      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11360      * @return {Mixed} value The field value
11361      */
11362     getValue : function(){
11363         return this.getGroupValue();
11364     }
11365     
11366 });
11367
11368  
11369 /*
11370  * - LGPL
11371  *
11372  * HtmlEditor
11373  * 
11374  */
11375
11376 /**
11377  * @class Roo.bootstrap.HtmlEditor
11378  * @extends Roo.bootstrap.Component
11379  * Bootstrap HtmlEditor class
11380
11381  * @constructor
11382  * Create a new HtmlEditor
11383  * @param {Object} config The config object
11384  */
11385
11386 Roo.bootstrap.HtmlEditor = function(config){
11387     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
11388     
11389 };
11390
11391
11392 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.Component,  {
11393     
11394     getAutoCreate : function()
11395     {
11396 //        var cfg = {};
11397 //        
11398 //        var toolbar = new Roo.bootstrap.ButtonGroup({
11399 //            toolbar: true,
11400 //            cn: [
11401 //                {
11402 //                    new Roo.bootstrap.Button({
11403 //                        
11404 //                    })
11405 //                }
11406 //            ]
11407 //        });
11408 //        
11409 //        return cfg;
11410         
11411     }
11412 });
11413
11414 /**
11415  * @class Roo.bootstrap.Table.AbstractSelectionModel
11416  * @extends Roo.util.Observable
11417  * Abstract base class for grid SelectionModels.  It provides the interface that should be
11418  * implemented by descendant classes.  This class should not be directly instantiated.
11419  * @constructor
11420  */
11421 Roo.bootstrap.Table.AbstractSelectionModel = function(){
11422     this.locked = false;
11423     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
11424 };
11425
11426
11427 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
11428     /** @ignore Called by the grid automatically. Do not call directly. */
11429     init : function(grid){
11430         this.grid = grid;
11431         this.initEvents();
11432     },
11433
11434     /**
11435      * Locks the selections.
11436      */
11437     lock : function(){
11438         this.locked = true;
11439     },
11440
11441     /**
11442      * Unlocks the selections.
11443      */
11444     unlock : function(){
11445         this.locked = false;
11446     },
11447
11448     /**
11449      * Returns true if the selections are locked.
11450      * @return {Boolean}
11451      */
11452     isLocked : function(){
11453         return this.locked;
11454     }
11455 });
11456 /**
11457  * @class Roo.bootstrap.Table.ColumnModel
11458  * @extends Roo.util.Observable
11459  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
11460  * the columns in the table.
11461  
11462  * @constructor
11463  * @param {Object} config An Array of column config objects. See this class's
11464  * config objects for details.
11465 */
11466 Roo.bootstrap.Table.ColumnModel = function(config){
11467         /**
11468      * The config passed into the constructor
11469      */
11470     this.config = config;
11471     this.lookup = {};
11472
11473     // if no id, create one
11474     // if the column does not have a dataIndex mapping,
11475     // map it to the order it is in the config
11476     for(var i = 0, len = config.length; i < len; i++){
11477         var c = config[i];
11478         if(typeof c.dataIndex == "undefined"){
11479             c.dataIndex = i;
11480         }
11481         if(typeof c.renderer == "string"){
11482             c.renderer = Roo.util.Format[c.renderer];
11483         }
11484         if(typeof c.id == "undefined"){
11485             c.id = Roo.id();
11486         }
11487 //        if(c.editor && c.editor.xtype){
11488 //            c.editor  = Roo.factory(c.editor, Roo.grid);
11489 //        }
11490 //        if(c.editor && c.editor.isFormField){
11491 //            c.editor = new Roo.grid.GridEditor(c.editor);
11492 //        }
11493
11494         this.lookup[c.id] = c;
11495     }
11496
11497     /**
11498      * The width of columns which have no width specified (defaults to 100)
11499      * @type Number
11500      */
11501     this.defaultWidth = 100;
11502
11503     /**
11504      * Default sortable of columns which have no sortable specified (defaults to false)
11505      * @type Boolean
11506      */
11507     this.defaultSortable = false;
11508
11509     this.addEvents({
11510         /**
11511              * @event widthchange
11512              * Fires when the width of a column changes.
11513              * @param {ColumnModel} this
11514              * @param {Number} columnIndex The column index
11515              * @param {Number} newWidth The new width
11516              */
11517             "widthchange": true,
11518         /**
11519              * @event headerchange
11520              * Fires when the text of a header changes.
11521              * @param {ColumnModel} this
11522              * @param {Number} columnIndex The column index
11523              * @param {Number} newText The new header text
11524              */
11525             "headerchange": true,
11526         /**
11527              * @event hiddenchange
11528              * Fires when a column is hidden or "unhidden".
11529              * @param {ColumnModel} this
11530              * @param {Number} columnIndex The column index
11531              * @param {Boolean} hidden true if hidden, false otherwise
11532              */
11533             "hiddenchange": true,
11534             /**
11535          * @event columnmoved
11536          * Fires when a column is moved.
11537          * @param {ColumnModel} this
11538          * @param {Number} oldIndex
11539          * @param {Number} newIndex
11540          */
11541         "columnmoved" : true,
11542         /**
11543          * @event columlockchange
11544          * Fires when a column's locked state is changed
11545          * @param {ColumnModel} this
11546          * @param {Number} colIndex
11547          * @param {Boolean} locked true if locked
11548          */
11549         "columnlockchange" : true
11550     });
11551     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
11552 };
11553 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
11554     /**
11555      * @cfg {String} header The header text to display in the Grid view.
11556      */
11557     /**
11558      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
11559      * {@link Roo.data.Record} definition from which to draw the column's value. If not
11560      * specified, the column's index is used as an index into the Record's data Array.
11561      */
11562     /**
11563      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
11564      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
11565      */
11566     /**
11567      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
11568      * Defaults to the value of the {@link #defaultSortable} property.
11569      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
11570      */
11571     /**
11572      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
11573      */
11574     /**
11575      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
11576      */
11577     /**
11578      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
11579      */
11580     /**
11581      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
11582      */
11583     /**
11584      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
11585      * given the cell's data value. See {@link #setRenderer}. If not specified, the
11586      * default renderer uses the raw data value.
11587      */
11588     /**
11589      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
11590      */
11591
11592     /**
11593      * Returns the id of the column at the specified index.
11594      * @param {Number} index The column index
11595      * @return {String} the id
11596      */
11597     getColumnId : function(index){
11598         return this.config[index].id;
11599     },
11600
11601     /**
11602      * Returns the column for a specified id.
11603      * @param {String} id The column id
11604      * @return {Object} the column
11605      */
11606     getColumnById : function(id){
11607         return this.lookup[id];
11608     },
11609
11610     
11611     /**
11612      * Returns the column for a specified dataIndex.
11613      * @param {String} dataIndex The column dataIndex
11614      * @return {Object|Boolean} the column or false if not found
11615      */
11616     getColumnByDataIndex: function(dataIndex){
11617         var index = this.findColumnIndex(dataIndex);
11618         return index > -1 ? this.config[index] : false;
11619     },
11620     
11621     /**
11622      * Returns the index for a specified column id.
11623      * @param {String} id The column id
11624      * @return {Number} the index, or -1 if not found
11625      */
11626     getIndexById : function(id){
11627         for(var i = 0, len = this.config.length; i < len; i++){
11628             if(this.config[i].id == id){
11629                 return i;
11630             }
11631         }
11632         return -1;
11633     },
11634     
11635     /**
11636      * Returns the index for a specified column dataIndex.
11637      * @param {String} dataIndex The column dataIndex
11638      * @return {Number} the index, or -1 if not found
11639      */
11640     
11641     findColumnIndex : function(dataIndex){
11642         for(var i = 0, len = this.config.length; i < len; i++){
11643             if(this.config[i].dataIndex == dataIndex){
11644                 return i;
11645             }
11646         }
11647         return -1;
11648     },
11649     
11650     
11651     moveColumn : function(oldIndex, newIndex){
11652         var c = this.config[oldIndex];
11653         this.config.splice(oldIndex, 1);
11654         this.config.splice(newIndex, 0, c);
11655         this.dataMap = null;
11656         this.fireEvent("columnmoved", this, oldIndex, newIndex);
11657     },
11658
11659     isLocked : function(colIndex){
11660         return this.config[colIndex].locked === true;
11661     },
11662
11663     setLocked : function(colIndex, value, suppressEvent){
11664         if(this.isLocked(colIndex) == value){
11665             return;
11666         }
11667         this.config[colIndex].locked = value;
11668         if(!suppressEvent){
11669             this.fireEvent("columnlockchange", this, colIndex, value);
11670         }
11671     },
11672
11673     getTotalLockedWidth : function(){
11674         var totalWidth = 0;
11675         for(var i = 0; i < this.config.length; i++){
11676             if(this.isLocked(i) && !this.isHidden(i)){
11677                 this.totalWidth += this.getColumnWidth(i);
11678             }
11679         }
11680         return totalWidth;
11681     },
11682
11683     getLockedCount : function(){
11684         for(var i = 0, len = this.config.length; i < len; i++){
11685             if(!this.isLocked(i)){
11686                 return i;
11687             }
11688         }
11689     },
11690
11691     /**
11692      * Returns the number of columns.
11693      * @return {Number}
11694      */
11695     getColumnCount : function(visibleOnly){
11696         if(visibleOnly === true){
11697             var c = 0;
11698             for(var i = 0, len = this.config.length; i < len; i++){
11699                 if(!this.isHidden(i)){
11700                     c++;
11701                 }
11702             }
11703             return c;
11704         }
11705         return this.config.length;
11706     },
11707
11708     /**
11709      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
11710      * @param {Function} fn
11711      * @param {Object} scope (optional)
11712      * @return {Array} result
11713      */
11714     getColumnsBy : function(fn, scope){
11715         var r = [];
11716         for(var i = 0, len = this.config.length; i < len; i++){
11717             var c = this.config[i];
11718             if(fn.call(scope||this, c, i) === true){
11719                 r[r.length] = c;
11720             }
11721         }
11722         return r;
11723     },
11724
11725     /**
11726      * Returns true if the specified column is sortable.
11727      * @param {Number} col The column index
11728      * @return {Boolean}
11729      */
11730     isSortable : function(col){
11731         if(typeof this.config[col].sortable == "undefined"){
11732             return this.defaultSortable;
11733         }
11734         return this.config[col].sortable;
11735     },
11736
11737     /**
11738      * Returns the rendering (formatting) function defined for the column.
11739      * @param {Number} col The column index.
11740      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
11741      */
11742     getRenderer : function(col){
11743         if(!this.config[col].renderer){
11744             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
11745         }
11746         return this.config[col].renderer;
11747     },
11748
11749     /**
11750      * Sets the rendering (formatting) function for a column.
11751      * @param {Number} col The column index
11752      * @param {Function} fn The function to use to process the cell's raw data
11753      * to return HTML markup for the grid view. The render function is called with
11754      * the following parameters:<ul>
11755      * <li>Data value.</li>
11756      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
11757      * <li>css A CSS style string to apply to the table cell.</li>
11758      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
11759      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
11760      * <li>Row index</li>
11761      * <li>Column index</li>
11762      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
11763      */
11764     setRenderer : function(col, fn){
11765         this.config[col].renderer = fn;
11766     },
11767
11768     /**
11769      * Returns the width for the specified column.
11770      * @param {Number} col The column index
11771      * @return {Number}
11772      */
11773     getColumnWidth : function(col){
11774         return this.config[col].width * 1 || this.defaultWidth;
11775     },
11776
11777     /**
11778      * Sets the width for a column.
11779      * @param {Number} col The column index
11780      * @param {Number} width The new width
11781      */
11782     setColumnWidth : function(col, width, suppressEvent){
11783         this.config[col].width = width;
11784         this.totalWidth = null;
11785         if(!suppressEvent){
11786              this.fireEvent("widthchange", this, col, width);
11787         }
11788     },
11789
11790     /**
11791      * Returns the total width of all columns.
11792      * @param {Boolean} includeHidden True to include hidden column widths
11793      * @return {Number}
11794      */
11795     getTotalWidth : function(includeHidden){
11796         if(!this.totalWidth){
11797             this.totalWidth = 0;
11798             for(var i = 0, len = this.config.length; i < len; i++){
11799                 if(includeHidden || !this.isHidden(i)){
11800                     this.totalWidth += this.getColumnWidth(i);
11801                 }
11802             }
11803         }
11804         return this.totalWidth;
11805     },
11806
11807     /**
11808      * Returns the header for the specified column.
11809      * @param {Number} col The column index
11810      * @return {String}
11811      */
11812     getColumnHeader : function(col){
11813         return this.config[col].header;
11814     },
11815
11816     /**
11817      * Sets the header for a column.
11818      * @param {Number} col The column index
11819      * @param {String} header The new header
11820      */
11821     setColumnHeader : function(col, header){
11822         this.config[col].header = header;
11823         this.fireEvent("headerchange", this, col, header);
11824     },
11825
11826     /**
11827      * Returns the tooltip for the specified column.
11828      * @param {Number} col The column index
11829      * @return {String}
11830      */
11831     getColumnTooltip : function(col){
11832             return this.config[col].tooltip;
11833     },
11834     /**
11835      * Sets the tooltip for a column.
11836      * @param {Number} col The column index
11837      * @param {String} tooltip The new tooltip
11838      */
11839     setColumnTooltip : function(col, tooltip){
11840             this.config[col].tooltip = tooltip;
11841     },
11842
11843     /**
11844      * Returns the dataIndex for the specified column.
11845      * @param {Number} col The column index
11846      * @return {Number}
11847      */
11848     getDataIndex : function(col){
11849         return this.config[col].dataIndex;
11850     },
11851
11852     /**
11853      * Sets the dataIndex for a column.
11854      * @param {Number} col The column index
11855      * @param {Number} dataIndex The new dataIndex
11856      */
11857     setDataIndex : function(col, dataIndex){
11858         this.config[col].dataIndex = dataIndex;
11859     },
11860
11861     
11862     
11863     /**
11864      * Returns true if the cell is editable.
11865      * @param {Number} colIndex The column index
11866      * @param {Number} rowIndex The row index
11867      * @return {Boolean}
11868      */
11869     isCellEditable : function(colIndex, rowIndex){
11870         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
11871     },
11872
11873     /**
11874      * Returns the editor defined for the cell/column.
11875      * return false or null to disable editing.
11876      * @param {Number} colIndex The column index
11877      * @param {Number} rowIndex The row index
11878      * @return {Object}
11879      */
11880     getCellEditor : function(colIndex, rowIndex){
11881         return this.config[colIndex].editor;
11882     },
11883
11884     /**
11885      * Sets if a column is editable.
11886      * @param {Number} col The column index
11887      * @param {Boolean} editable True if the column is editable
11888      */
11889     setEditable : function(col, editable){
11890         this.config[col].editable = editable;
11891     },
11892
11893
11894     /**
11895      * Returns true if the column is hidden.
11896      * @param {Number} colIndex The column index
11897      * @return {Boolean}
11898      */
11899     isHidden : function(colIndex){
11900         return this.config[colIndex].hidden;
11901     },
11902
11903
11904     /**
11905      * Returns true if the column width cannot be changed
11906      */
11907     isFixed : function(colIndex){
11908         return this.config[colIndex].fixed;
11909     },
11910
11911     /**
11912      * Returns true if the column can be resized
11913      * @return {Boolean}
11914      */
11915     isResizable : function(colIndex){
11916         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
11917     },
11918     /**
11919      * Sets if a column is hidden.
11920      * @param {Number} colIndex The column index
11921      * @param {Boolean} hidden True if the column is hidden
11922      */
11923     setHidden : function(colIndex, hidden){
11924         this.config[colIndex].hidden = hidden;
11925         this.totalWidth = null;
11926         this.fireEvent("hiddenchange", this, colIndex, hidden);
11927     },
11928
11929     /**
11930      * Sets the editor for a column.
11931      * @param {Number} col The column index
11932      * @param {Object} editor The editor object
11933      */
11934     setEditor : function(col, editor){
11935         this.config[col].editor = editor;
11936     }
11937 });
11938
11939 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
11940         if(typeof value == "string" && value.length < 1){
11941             return "&#160;";
11942         }
11943         return value;
11944 };
11945
11946 // Alias for backwards compatibility
11947 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
11948
11949 /**
11950  * @extends Roo.bootstrap.Table.AbstractSelectionModel
11951  * @class Roo.bootstrap.Table.RowSelectionModel
11952  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
11953  * It supports multiple selections and keyboard selection/navigation. 
11954  * @constructor
11955  * @param {Object} config
11956  */
11957
11958 Roo.bootstrap.Table.RowSelectionModel = function(config){
11959     Roo.apply(this, config);
11960     this.selections = new Roo.util.MixedCollection(false, function(o){
11961         return o.id;
11962     });
11963
11964     this.last = false;
11965     this.lastActive = false;
11966
11967     this.addEvents({
11968         /**
11969              * @event selectionchange
11970              * Fires when the selection changes
11971              * @param {SelectionModel} this
11972              */
11973             "selectionchange" : true,
11974         /**
11975              * @event afterselectionchange
11976              * Fires after the selection changes (eg. by key press or clicking)
11977              * @param {SelectionModel} this
11978              */
11979             "afterselectionchange" : true,
11980         /**
11981              * @event beforerowselect
11982              * Fires when a row is selected being selected, return false to cancel.
11983              * @param {SelectionModel} this
11984              * @param {Number} rowIndex The selected index
11985              * @param {Boolean} keepExisting False if other selections will be cleared
11986              */
11987             "beforerowselect" : true,
11988         /**
11989              * @event rowselect
11990              * Fires when a row is selected.
11991              * @param {SelectionModel} this
11992              * @param {Number} rowIndex The selected index
11993              * @param {Roo.data.Record} r The record
11994              */
11995             "rowselect" : true,
11996         /**
11997              * @event rowdeselect
11998              * Fires when a row is deselected.
11999              * @param {SelectionModel} this
12000              * @param {Number} rowIndex The selected index
12001              */
12002         "rowdeselect" : true
12003     });
12004     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
12005     this.locked = false;
12006 };
12007
12008 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
12009     /**
12010      * @cfg {Boolean} singleSelect
12011      * True to allow selection of only one row at a time (defaults to false)
12012      */
12013     singleSelect : false,
12014
12015     // private
12016     initEvents : function(){
12017
12018         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
12019             this.grid.on("mousedown", this.handleMouseDown, this);
12020         }else{ // allow click to work like normal
12021             this.grid.on("rowclick", this.handleDragableRowClick, this);
12022         }
12023
12024         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
12025             "up" : function(e){
12026                 if(!e.shiftKey){
12027                     this.selectPrevious(e.shiftKey);
12028                 }else if(this.last !== false && this.lastActive !== false){
12029                     var last = this.last;
12030                     this.selectRange(this.last,  this.lastActive-1);
12031                     this.grid.getView().focusRow(this.lastActive);
12032                     if(last !== false){
12033                         this.last = last;
12034                     }
12035                 }else{
12036                     this.selectFirstRow();
12037                 }
12038                 this.fireEvent("afterselectionchange", this);
12039             },
12040             "down" : function(e){
12041                 if(!e.shiftKey){
12042                     this.selectNext(e.shiftKey);
12043                 }else if(this.last !== false && this.lastActive !== false){
12044                     var last = this.last;
12045                     this.selectRange(this.last,  this.lastActive+1);
12046                     this.grid.getView().focusRow(this.lastActive);
12047                     if(last !== false){
12048                         this.last = last;
12049                     }
12050                 }else{
12051                     this.selectFirstRow();
12052                 }
12053                 this.fireEvent("afterselectionchange", this);
12054             },
12055             scope: this
12056         });
12057
12058         var view = this.grid.view;
12059         view.on("refresh", this.onRefresh, this);
12060         view.on("rowupdated", this.onRowUpdated, this);
12061         view.on("rowremoved", this.onRemove, this);
12062     },
12063
12064     // private
12065     onRefresh : function(){
12066         var ds = this.grid.dataSource, i, v = this.grid.view;
12067         var s = this.selections;
12068         s.each(function(r){
12069             if((i = ds.indexOfId(r.id)) != -1){
12070                 v.onRowSelect(i);
12071             }else{
12072                 s.remove(r);
12073             }
12074         });
12075     },
12076
12077     // private
12078     onRemove : function(v, index, r){
12079         this.selections.remove(r);
12080     },
12081
12082     // private
12083     onRowUpdated : function(v, index, r){
12084         if(this.isSelected(r)){
12085             v.onRowSelect(index);
12086         }
12087     },
12088
12089     /**
12090      * Select records.
12091      * @param {Array} records The records to select
12092      * @param {Boolean} keepExisting (optional) True to keep existing selections
12093      */
12094     selectRecords : function(records, keepExisting){
12095         if(!keepExisting){
12096             this.clearSelections();
12097         }
12098         var ds = this.grid.dataSource;
12099         for(var i = 0, len = records.length; i < len; i++){
12100             this.selectRow(ds.indexOf(records[i]), true);
12101         }
12102     },
12103
12104     /**
12105      * Gets the number of selected rows.
12106      * @return {Number}
12107      */
12108     getCount : function(){
12109         return this.selections.length;
12110     },
12111
12112     /**
12113      * Selects the first row in the grid.
12114      */
12115     selectFirstRow : function(){
12116         this.selectRow(0);
12117     },
12118
12119     /**
12120      * Select the last row.
12121      * @param {Boolean} keepExisting (optional) True to keep existing selections
12122      */
12123     selectLastRow : function(keepExisting){
12124         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
12125     },
12126
12127     /**
12128      * Selects the row immediately following the last selected row.
12129      * @param {Boolean} keepExisting (optional) True to keep existing selections
12130      */
12131     selectNext : function(keepExisting){
12132         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
12133             this.selectRow(this.last+1, keepExisting);
12134             this.grid.getView().focusRow(this.last);
12135         }
12136     },
12137
12138     /**
12139      * Selects the row that precedes the last selected row.
12140      * @param {Boolean} keepExisting (optional) True to keep existing selections
12141      */
12142     selectPrevious : function(keepExisting){
12143         if(this.last){
12144             this.selectRow(this.last-1, keepExisting);
12145             this.grid.getView().focusRow(this.last);
12146         }
12147     },
12148
12149     /**
12150      * Returns the selected records
12151      * @return {Array} Array of selected records
12152      */
12153     getSelections : function(){
12154         return [].concat(this.selections.items);
12155     },
12156
12157     /**
12158      * Returns the first selected record.
12159      * @return {Record}
12160      */
12161     getSelected : function(){
12162         return this.selections.itemAt(0);
12163     },
12164
12165
12166     /**
12167      * Clears all selections.
12168      */
12169     clearSelections : function(fast){
12170         if(this.locked) return;
12171         if(fast !== true){
12172             var ds = this.grid.dataSource;
12173             var s = this.selections;
12174             s.each(function(r){
12175                 this.deselectRow(ds.indexOfId(r.id));
12176             }, this);
12177             s.clear();
12178         }else{
12179             this.selections.clear();
12180         }
12181         this.last = false;
12182     },
12183
12184
12185     /**
12186      * Selects all rows.
12187      */
12188     selectAll : function(){
12189         if(this.locked) return;
12190         this.selections.clear();
12191         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
12192             this.selectRow(i, true);
12193         }
12194     },
12195
12196     /**
12197      * Returns True if there is a selection.
12198      * @return {Boolean}
12199      */
12200     hasSelection : function(){
12201         return this.selections.length > 0;
12202     },
12203
12204     /**
12205      * Returns True if the specified row is selected.
12206      * @param {Number/Record} record The record or index of the record to check
12207      * @return {Boolean}
12208      */
12209     isSelected : function(index){
12210         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
12211         return (r && this.selections.key(r.id) ? true : false);
12212     },
12213
12214     /**
12215      * Returns True if the specified record id is selected.
12216      * @param {String} id The id of record to check
12217      * @return {Boolean}
12218      */
12219     isIdSelected : function(id){
12220         return (this.selections.key(id) ? true : false);
12221     },
12222
12223     // private
12224     handleMouseDown : function(e, t){
12225         var view = this.grid.getView(), rowIndex;
12226         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
12227             return;
12228         };
12229         if(e.shiftKey && this.last !== false){
12230             var last = this.last;
12231             this.selectRange(last, rowIndex, e.ctrlKey);
12232             this.last = last; // reset the last
12233             view.focusRow(rowIndex);
12234         }else{
12235             var isSelected = this.isSelected(rowIndex);
12236             if(e.button !== 0 && isSelected){
12237                 view.focusRow(rowIndex);
12238             }else if(e.ctrlKey && isSelected){
12239                 this.deselectRow(rowIndex);
12240             }else if(!isSelected){
12241                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
12242                 view.focusRow(rowIndex);
12243             }
12244         }
12245         this.fireEvent("afterselectionchange", this);
12246     },
12247     // private
12248     handleDragableRowClick :  function(grid, rowIndex, e) 
12249     {
12250         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
12251             this.selectRow(rowIndex, false);
12252             grid.view.focusRow(rowIndex);
12253              this.fireEvent("afterselectionchange", this);
12254         }
12255     },
12256     
12257     /**
12258      * Selects multiple rows.
12259      * @param {Array} rows Array of the indexes of the row to select
12260      * @param {Boolean} keepExisting (optional) True to keep existing selections
12261      */
12262     selectRows : function(rows, keepExisting){
12263         if(!keepExisting){
12264             this.clearSelections();
12265         }
12266         for(var i = 0, len = rows.length; i < len; i++){
12267             this.selectRow(rows[i], true);
12268         }
12269     },
12270
12271     /**
12272      * Selects a range of rows. All rows in between startRow and endRow are also selected.
12273      * @param {Number} startRow The index of the first row in the range
12274      * @param {Number} endRow The index of the last row in the range
12275      * @param {Boolean} keepExisting (optional) True to retain existing selections
12276      */
12277     selectRange : function(startRow, endRow, keepExisting){
12278         if(this.locked) return;
12279         if(!keepExisting){
12280             this.clearSelections();
12281         }
12282         if(startRow <= endRow){
12283             for(var i = startRow; i <= endRow; i++){
12284                 this.selectRow(i, true);
12285             }
12286         }else{
12287             for(var i = startRow; i >= endRow; i--){
12288                 this.selectRow(i, true);
12289             }
12290         }
12291     },
12292
12293     /**
12294      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
12295      * @param {Number} startRow The index of the first row in the range
12296      * @param {Number} endRow The index of the last row in the range
12297      */
12298     deselectRange : function(startRow, endRow, preventViewNotify){
12299         if(this.locked) return;
12300         for(var i = startRow; i <= endRow; i++){
12301             this.deselectRow(i, preventViewNotify);
12302         }
12303     },
12304
12305     /**
12306      * Selects a row.
12307      * @param {Number} row The index of the row to select
12308      * @param {Boolean} keepExisting (optional) True to keep existing selections
12309      */
12310     selectRow : function(index, keepExisting, preventViewNotify){
12311         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
12312         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
12313             if(!keepExisting || this.singleSelect){
12314                 this.clearSelections();
12315             }
12316             var r = this.grid.dataSource.getAt(index);
12317             this.selections.add(r);
12318             this.last = this.lastActive = index;
12319             if(!preventViewNotify){
12320                 this.grid.getView().onRowSelect(index);
12321             }
12322             this.fireEvent("rowselect", this, index, r);
12323             this.fireEvent("selectionchange", this);
12324         }
12325     },
12326
12327     /**
12328      * Deselects a row.
12329      * @param {Number} row The index of the row to deselect
12330      */
12331     deselectRow : function(index, preventViewNotify){
12332         if(this.locked) return;
12333         if(this.last == index){
12334             this.last = false;
12335         }
12336         if(this.lastActive == index){
12337             this.lastActive = false;
12338         }
12339         var r = this.grid.dataSource.getAt(index);
12340         this.selections.remove(r);
12341         if(!preventViewNotify){
12342             this.grid.getView().onRowDeselect(index);
12343         }
12344         this.fireEvent("rowdeselect", this, index);
12345         this.fireEvent("selectionchange", this);
12346     },
12347
12348     // private
12349     restoreLast : function(){
12350         if(this._last){
12351             this.last = this._last;
12352         }
12353     },
12354
12355     // private
12356     acceptsNav : function(row, col, cm){
12357         return !cm.isHidden(col) && cm.isCellEditable(col, row);
12358     },
12359
12360     // private
12361     onEditorKey : function(field, e){
12362         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
12363         if(k == e.TAB){
12364             e.stopEvent();
12365             ed.completeEdit();
12366             if(e.shiftKey){
12367                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
12368             }else{
12369                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
12370             }
12371         }else if(k == e.ENTER && !e.ctrlKey){
12372             e.stopEvent();
12373             ed.completeEdit();
12374             if(e.shiftKey){
12375                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
12376             }else{
12377                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
12378             }
12379         }else if(k == e.ESC){
12380             ed.cancelEdit();
12381         }
12382         if(newCell){
12383             g.startEditing(newCell[0], newCell[1]);
12384         }
12385     }
12386 });/*
12387  * Based on:
12388  * Ext JS Library 1.1.1
12389  * Copyright(c) 2006-2007, Ext JS, LLC.
12390  *
12391  * Originally Released Under LGPL - original licence link has changed is not relivant.
12392  *
12393  * Fork - LGPL
12394  * <script type="text/javascript">
12395  */
12396
12397 /**
12398  * @class Roo.Toolbar
12399  * Basic Toolbar class.
12400  * @constructor
12401  * Creates a new Toolbar
12402  * @param {Object} container The config object
12403  */ 
12404 Roo.Toolbar = function(container, buttons, config)
12405 {
12406     /// old consturctor format still supported..
12407     if(container instanceof Array){ // omit the container for later rendering
12408         buttons = container;
12409         config = buttons;
12410         container = null;
12411     }
12412     if (typeof(container) == 'object' && container.xtype) {
12413         config = container;
12414         container = config.container;
12415         buttons = config.buttons || []; // not really - use items!!
12416     }
12417     var xitems = [];
12418     if (config && config.items) {
12419         xitems = config.items;
12420         delete config.items;
12421     }
12422     Roo.apply(this, config);
12423     this.buttons = buttons;
12424     
12425     if(container){
12426         this.render(container);
12427     }
12428     this.xitems = xitems;
12429     Roo.each(xitems, function(b) {
12430         this.add(b);
12431     }, this);
12432     
12433 };
12434
12435 Roo.Toolbar.prototype = {
12436     /**
12437      * @cfg {Array} items
12438      * array of button configs or elements to add (will be converted to a MixedCollection)
12439      */
12440     
12441     /**
12442      * @cfg {String/HTMLElement/Element} container
12443      * The id or element that will contain the toolbar
12444      */
12445     // private
12446     render : function(ct){
12447         this.el = Roo.get(ct);
12448         if(this.cls){
12449             this.el.addClass(this.cls);
12450         }
12451         // using a table allows for vertical alignment
12452         // 100% width is needed by Safari...
12453         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12454         this.tr = this.el.child("tr", true);
12455         var autoId = 0;
12456         this.items = new Roo.util.MixedCollection(false, function(o){
12457             return o.id || ("item" + (++autoId));
12458         });
12459         if(this.buttons){
12460             this.add.apply(this, this.buttons);
12461             delete this.buttons;
12462         }
12463     },
12464
12465     /**
12466      * Adds element(s) to the toolbar -- this function takes a variable number of 
12467      * arguments of mixed type and adds them to the toolbar.
12468      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12469      * <ul>
12470      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12471      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12472      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12473      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12474      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12475      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12476      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12477      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12478      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12479      * </ul>
12480      * @param {Mixed} arg2
12481      * @param {Mixed} etc.
12482      */
12483     add : function(){
12484         var a = arguments, l = a.length;
12485         for(var i = 0; i < l; i++){
12486             this._add(a[i]);
12487         }
12488     },
12489     // private..
12490     _add : function(el) {
12491         
12492         if (el.xtype) {
12493             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12494         }
12495         
12496         if (el.applyTo){ // some kind of form field
12497             return this.addField(el);
12498         } 
12499         if (el.render){ // some kind of Toolbar.Item
12500             return this.addItem(el);
12501         }
12502         if (typeof el == "string"){ // string
12503             if(el == "separator" || el == "-"){
12504                 return this.addSeparator();
12505             }
12506             if (el == " "){
12507                 return this.addSpacer();
12508             }
12509             if(el == "->"){
12510                 return this.addFill();
12511             }
12512             return this.addText(el);
12513             
12514         }
12515         if(el.tagName){ // element
12516             return this.addElement(el);
12517         }
12518         if(typeof el == "object"){ // must be button config?
12519             return this.addButton(el);
12520         }
12521         // and now what?!?!
12522         return false;
12523         
12524     },
12525     
12526     /**
12527      * Add an Xtype element
12528      * @param {Object} xtype Xtype Object
12529      * @return {Object} created Object
12530      */
12531     addxtype : function(e){
12532         return this.add(e);  
12533     },
12534     
12535     /**
12536      * Returns the Element for this toolbar.
12537      * @return {Roo.Element}
12538      */
12539     getEl : function(){
12540         return this.el;  
12541     },
12542     
12543     /**
12544      * Adds a separator
12545      * @return {Roo.Toolbar.Item} The separator item
12546      */
12547     addSeparator : function(){
12548         return this.addItem(new Roo.Toolbar.Separator());
12549     },
12550
12551     /**
12552      * Adds a spacer element
12553      * @return {Roo.Toolbar.Spacer} The spacer item
12554      */
12555     addSpacer : function(){
12556         return this.addItem(new Roo.Toolbar.Spacer());
12557     },
12558
12559     /**
12560      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12561      * @return {Roo.Toolbar.Fill} The fill item
12562      */
12563     addFill : function(){
12564         return this.addItem(new Roo.Toolbar.Fill());
12565     },
12566
12567     /**
12568      * Adds any standard HTML element to the toolbar
12569      * @param {String/HTMLElement/Element} el The element or id of the element to add
12570      * @return {Roo.Toolbar.Item} The element's item
12571      */
12572     addElement : function(el){
12573         return this.addItem(new Roo.Toolbar.Item(el));
12574     },
12575     /**
12576      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12577      * @type Roo.util.MixedCollection  
12578      */
12579     items : false,
12580      
12581     /**
12582      * Adds any Toolbar.Item or subclass
12583      * @param {Roo.Toolbar.Item} item
12584      * @return {Roo.Toolbar.Item} The item
12585      */
12586     addItem : function(item){
12587         var td = this.nextBlock();
12588         item.render(td);
12589         this.items.add(item);
12590         return item;
12591     },
12592     
12593     /**
12594      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12595      * @param {Object/Array} config A button config or array of configs
12596      * @return {Roo.Toolbar.Button/Array}
12597      */
12598     addButton : function(config){
12599         if(config instanceof Array){
12600             var buttons = [];
12601             for(var i = 0, len = config.length; i < len; i++) {
12602                 buttons.push(this.addButton(config[i]));
12603             }
12604             return buttons;
12605         }
12606         var b = config;
12607         if(!(config instanceof Roo.Toolbar.Button)){
12608             b = config.split ?
12609                 new Roo.Toolbar.SplitButton(config) :
12610                 new Roo.Toolbar.Button(config);
12611         }
12612         var td = this.nextBlock();
12613         b.render(td);
12614         this.items.add(b);
12615         return b;
12616     },
12617     
12618     /**
12619      * Adds text to the toolbar
12620      * @param {String} text The text to add
12621      * @return {Roo.Toolbar.Item} The element's item
12622      */
12623     addText : function(text){
12624         return this.addItem(new Roo.Toolbar.TextItem(text));
12625     },
12626     
12627     /**
12628      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12629      * @param {Number} index The index where the item is to be inserted
12630      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12631      * @return {Roo.Toolbar.Button/Item}
12632      */
12633     insertButton : function(index, item){
12634         if(item instanceof Array){
12635             var buttons = [];
12636             for(var i = 0, len = item.length; i < len; i++) {
12637                buttons.push(this.insertButton(index + i, item[i]));
12638             }
12639             return buttons;
12640         }
12641         if (!(item instanceof Roo.Toolbar.Button)){
12642            item = new Roo.Toolbar.Button(item);
12643         }
12644         var td = document.createElement("td");
12645         this.tr.insertBefore(td, this.tr.childNodes[index]);
12646         item.render(td);
12647         this.items.insert(index, item);
12648         return item;
12649     },
12650     
12651     /**
12652      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12653      * @param {Object} config
12654      * @return {Roo.Toolbar.Item} The element's item
12655      */
12656     addDom : function(config, returnEl){
12657         var td = this.nextBlock();
12658         Roo.DomHelper.overwrite(td, config);
12659         var ti = new Roo.Toolbar.Item(td.firstChild);
12660         ti.render(td);
12661         this.items.add(ti);
12662         return ti;
12663     },
12664
12665     /**
12666      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12667      * @type Roo.util.MixedCollection  
12668      */
12669     fields : false,
12670     
12671     /**
12672      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12673      * Note: the field should not have been rendered yet. For a field that has already been
12674      * rendered, use {@link #addElement}.
12675      * @param {Roo.form.Field} field
12676      * @return {Roo.ToolbarItem}
12677      */
12678      
12679       
12680     addField : function(field) {
12681         if (!this.fields) {
12682             var autoId = 0;
12683             this.fields = new Roo.util.MixedCollection(false, function(o){
12684                 return o.id || ("item" + (++autoId));
12685             });
12686
12687         }
12688         
12689         var td = this.nextBlock();
12690         field.render(td);
12691         var ti = new Roo.Toolbar.Item(td.firstChild);
12692         ti.render(td);
12693         this.items.add(ti);
12694         this.fields.add(field);
12695         return ti;
12696     },
12697     /**
12698      * Hide the toolbar
12699      * @method hide
12700      */
12701      
12702       
12703     hide : function()
12704     {
12705         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12706         this.el.child('div').hide();
12707     },
12708     /**
12709      * Show the toolbar
12710      * @method show
12711      */
12712     show : function()
12713     {
12714         this.el.child('div').show();
12715     },
12716       
12717     // private
12718     nextBlock : function(){
12719         var td = document.createElement("td");
12720         this.tr.appendChild(td);
12721         return td;
12722     },
12723
12724     // private
12725     destroy : function(){
12726         if(this.items){ // rendered?
12727             Roo.destroy.apply(Roo, this.items.items);
12728         }
12729         if(this.fields){ // rendered?
12730             Roo.destroy.apply(Roo, this.fields.items);
12731         }
12732         Roo.Element.uncache(this.el, this.tr);
12733     }
12734 };
12735
12736 /**
12737  * @class Roo.Toolbar.Item
12738  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12739  * @constructor
12740  * Creates a new Item
12741  * @param {HTMLElement} el 
12742  */
12743 Roo.Toolbar.Item = function(el){
12744     this.el = Roo.getDom(el);
12745     this.id = Roo.id(this.el);
12746     this.hidden = false;
12747 };
12748
12749 Roo.Toolbar.Item.prototype = {
12750     
12751     /**
12752      * Get this item's HTML Element
12753      * @return {HTMLElement}
12754      */
12755     getEl : function(){
12756        return this.el;  
12757     },
12758
12759     // private
12760     render : function(td){
12761         this.td = td;
12762         td.appendChild(this.el);
12763     },
12764     
12765     /**
12766      * Removes and destroys this item.
12767      */
12768     destroy : function(){
12769         this.td.parentNode.removeChild(this.td);
12770     },
12771     
12772     /**
12773      * Shows this item.
12774      */
12775     show: function(){
12776         this.hidden = false;
12777         this.td.style.display = "";
12778     },
12779     
12780     /**
12781      * Hides this item.
12782      */
12783     hide: function(){
12784         this.hidden = true;
12785         this.td.style.display = "none";
12786     },
12787     
12788     /**
12789      * Convenience function for boolean show/hide.
12790      * @param {Boolean} visible true to show/false to hide
12791      */
12792     setVisible: function(visible){
12793         if(visible) {
12794             this.show();
12795         }else{
12796             this.hide();
12797         }
12798     },
12799     
12800     /**
12801      * Try to focus this item.
12802      */
12803     focus : function(){
12804         Roo.fly(this.el).focus();
12805     },
12806     
12807     /**
12808      * Disables this item.
12809      */
12810     disable : function(){
12811         Roo.fly(this.td).addClass("x-item-disabled");
12812         this.disabled = true;
12813         this.el.disabled = true;
12814     },
12815     
12816     /**
12817      * Enables this item.
12818      */
12819     enable : function(){
12820         Roo.fly(this.td).removeClass("x-item-disabled");
12821         this.disabled = false;
12822         this.el.disabled = false;
12823     }
12824 };
12825
12826
12827 /**
12828  * @class Roo.Toolbar.Separator
12829  * @extends Roo.Toolbar.Item
12830  * A simple toolbar separator class
12831  * @constructor
12832  * Creates a new Separator
12833  */
12834 Roo.Toolbar.Separator = function(){
12835     var s = document.createElement("span");
12836     s.className = "ytb-sep";
12837     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12838 };
12839 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12840     enable:Roo.emptyFn,
12841     disable:Roo.emptyFn,
12842     focus:Roo.emptyFn
12843 });
12844
12845 /**
12846  * @class Roo.Toolbar.Spacer
12847  * @extends Roo.Toolbar.Item
12848  * A simple element that adds extra horizontal space to a toolbar.
12849  * @constructor
12850  * Creates a new Spacer
12851  */
12852 Roo.Toolbar.Spacer = function(){
12853     var s = document.createElement("div");
12854     s.className = "ytb-spacer";
12855     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12856 };
12857 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12858     enable:Roo.emptyFn,
12859     disable:Roo.emptyFn,
12860     focus:Roo.emptyFn
12861 });
12862
12863 /**
12864  * @class Roo.Toolbar.Fill
12865  * @extends Roo.Toolbar.Spacer
12866  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12867  * @constructor
12868  * Creates a new Spacer
12869  */
12870 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12871     // private
12872     render : function(td){
12873         td.style.width = '100%';
12874         Roo.Toolbar.Fill.superclass.render.call(this, td);
12875     }
12876 });
12877
12878 /**
12879  * @class Roo.Toolbar.TextItem
12880  * @extends Roo.Toolbar.Item
12881  * A simple class that renders text directly into a toolbar.
12882  * @constructor
12883  * Creates a new TextItem
12884  * @param {String} text
12885  */
12886 Roo.Toolbar.TextItem = function(text){
12887     if (typeof(text) == 'object') {
12888         text = text.text;
12889     }
12890     var s = document.createElement("span");
12891     s.className = "ytb-text";
12892     s.innerHTML = text;
12893     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12894 };
12895 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12896     enable:Roo.emptyFn,
12897     disable:Roo.emptyFn,
12898     focus:Roo.emptyFn
12899 });
12900
12901 /**
12902  * @class Roo.Toolbar.Button
12903  * @extends Roo.Button
12904  * A button that renders into a toolbar.
12905  * @constructor
12906  * Creates a new Button
12907  * @param {Object} config A standard {@link Roo.Button} config object
12908  */
12909 Roo.Toolbar.Button = function(config){
12910     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12911 };
12912 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12913     render : function(td){
12914         this.td = td;
12915         Roo.Toolbar.Button.superclass.render.call(this, td);
12916     },
12917     
12918     /**
12919      * Removes and destroys this button
12920      */
12921     destroy : function(){
12922         Roo.Toolbar.Button.superclass.destroy.call(this);
12923         this.td.parentNode.removeChild(this.td);
12924     },
12925     
12926     /**
12927      * Shows this button
12928      */
12929     show: function(){
12930         this.hidden = false;
12931         this.td.style.display = "";
12932     },
12933     
12934     /**
12935      * Hides this button
12936      */
12937     hide: function(){
12938         this.hidden = true;
12939         this.td.style.display = "none";
12940     },
12941
12942     /**
12943      * Disables this item
12944      */
12945     disable : function(){
12946         Roo.fly(this.td).addClass("x-item-disabled");
12947         this.disabled = true;
12948     },
12949
12950     /**
12951      * Enables this item
12952      */
12953     enable : function(){
12954         Roo.fly(this.td).removeClass("x-item-disabled");
12955         this.disabled = false;
12956     }
12957 });
12958 // backwards compat
12959 Roo.ToolbarButton = Roo.Toolbar.Button;
12960
12961 /**
12962  * @class Roo.Toolbar.SplitButton
12963  * @extends Roo.SplitButton
12964  * A menu button that renders into a toolbar.
12965  * @constructor
12966  * Creates a new SplitButton
12967  * @param {Object} config A standard {@link Roo.SplitButton} config object
12968  */
12969 Roo.Toolbar.SplitButton = function(config){
12970     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12971 };
12972 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12973     render : function(td){
12974         this.td = td;
12975         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12976     },
12977     
12978     /**
12979      * Removes and destroys this button
12980      */
12981     destroy : function(){
12982         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12983         this.td.parentNode.removeChild(this.td);
12984     },
12985     
12986     /**
12987      * Shows this button
12988      */
12989     show: function(){
12990         this.hidden = false;
12991         this.td.style.display = "";
12992     },
12993     
12994     /**
12995      * Hides this button
12996      */
12997     hide: function(){
12998         this.hidden = true;
12999         this.td.style.display = "none";
13000     }
13001 });
13002
13003 // backwards compat
13004 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13005  * Based on:
13006  * Ext JS Library 1.1.1
13007  * Copyright(c) 2006-2007, Ext JS, LLC.
13008  *
13009  * Originally Released Under LGPL - original licence link has changed is not relivant.
13010  *
13011  * Fork - LGPL
13012  * <script type="text/javascript">
13013  */
13014  
13015 /**
13016  * @class Roo.PagingToolbar
13017  * @extends Roo.Toolbar
13018  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13019  * @constructor
13020  * Create a new PagingToolbar
13021  * @param {Object} config The config object
13022  */
13023 Roo.PagingToolbar = function(el, ds, config)
13024 {
13025     // old args format still supported... - xtype is prefered..
13026     if (typeof(el) == 'object' && el.xtype) {
13027         // created from xtype...
13028         config = el;
13029         ds = el.dataSource;
13030         el = config.container;
13031     }
13032     var items = [];
13033     if (config.items) {
13034         items = config.items;
13035         config.items = [];
13036     }
13037     
13038     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13039     this.ds = ds;
13040     this.cursor = 0;
13041     this.renderButtons(this.el);
13042     this.bind(ds);
13043     
13044     // supprot items array.
13045    
13046     Roo.each(items, function(e) {
13047         this.add(Roo.factory(e));
13048     },this);
13049     
13050 };
13051
13052 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13053     /**
13054      * @cfg {Roo.data.Store} dataSource
13055      * The underlying data store providing the paged data
13056      */
13057     /**
13058      * @cfg {String/HTMLElement/Element} container
13059      * container The id or element that will contain the toolbar
13060      */
13061     /**
13062      * @cfg {Boolean} displayInfo
13063      * True to display the displayMsg (defaults to false)
13064      */
13065     /**
13066      * @cfg {Number} pageSize
13067      * The number of records to display per page (defaults to 20)
13068      */
13069     pageSize: 20,
13070     /**
13071      * @cfg {String} displayMsg
13072      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13073      */
13074     displayMsg : 'Displaying {0} - {1} of {2}',
13075     /**
13076      * @cfg {String} emptyMsg
13077      * The message to display when no records are found (defaults to "No data to display")
13078      */
13079     emptyMsg : 'No data to display',
13080     /**
13081      * Customizable piece of the default paging text (defaults to "Page")
13082      * @type String
13083      */
13084     beforePageText : "Page",
13085     /**
13086      * Customizable piece of the default paging text (defaults to "of %0")
13087      * @type String
13088      */
13089     afterPageText : "of {0}",
13090     /**
13091      * Customizable piece of the default paging text (defaults to "First Page")
13092      * @type String
13093      */
13094     firstText : "First Page",
13095     /**
13096      * Customizable piece of the default paging text (defaults to "Previous Page")
13097      * @type String
13098      */
13099     prevText : "Previous Page",
13100     /**
13101      * Customizable piece of the default paging text (defaults to "Next Page")
13102      * @type String
13103      */
13104     nextText : "Next Page",
13105     /**
13106      * Customizable piece of the default paging text (defaults to "Last Page")
13107      * @type String
13108      */
13109     lastText : "Last Page",
13110     /**
13111      * Customizable piece of the default paging text (defaults to "Refresh")
13112      * @type String
13113      */
13114     refreshText : "Refresh",
13115
13116     // private
13117     renderButtons : function(el){
13118         Roo.PagingToolbar.superclass.render.call(this, el);
13119         this.first = this.addButton({
13120             tooltip: this.firstText,
13121             cls: "x-btn-icon x-grid-page-first",
13122             disabled: true,
13123             handler: this.onClick.createDelegate(this, ["first"])
13124         });
13125         this.prev = this.addButton({
13126             tooltip: this.prevText,
13127             cls: "x-btn-icon x-grid-page-prev",
13128             disabled: true,
13129             handler: this.onClick.createDelegate(this, ["prev"])
13130         });
13131         //this.addSeparator();
13132         this.add(this.beforePageText);
13133         this.field = Roo.get(this.addDom({
13134            tag: "input",
13135            type: "text",
13136            size: "3",
13137            value: "1",
13138            cls: "x-grid-page-number"
13139         }).el);
13140         this.field.on("keydown", this.onPagingKeydown, this);
13141         this.field.on("focus", function(){this.dom.select();});
13142         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13143         this.field.setHeight(18);
13144         //this.addSeparator();
13145         this.next = this.addButton({
13146             tooltip: this.nextText,
13147             cls: "x-btn-icon x-grid-page-next",
13148             disabled: true,
13149             handler: this.onClick.createDelegate(this, ["next"])
13150         });
13151         this.last = this.addButton({
13152             tooltip: this.lastText,
13153             cls: "x-btn-icon x-grid-page-last",
13154             disabled: true,
13155             handler: this.onClick.createDelegate(this, ["last"])
13156         });
13157         //this.addSeparator();
13158         this.loading = this.addButton({
13159             tooltip: this.refreshText,
13160             cls: "x-btn-icon x-grid-loading",
13161             handler: this.onClick.createDelegate(this, ["refresh"])
13162         });
13163
13164         if(this.displayInfo){
13165             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13166         }
13167     },
13168
13169     // private
13170     updateInfo : function(){
13171         if(this.displayEl){
13172             var count = this.ds.getCount();
13173             var msg = count == 0 ?
13174                 this.emptyMsg :
13175                 String.format(
13176                     this.displayMsg,
13177                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13178                 );
13179             this.displayEl.update(msg);
13180         }
13181     },
13182
13183     // private
13184     onLoad : function(ds, r, o){
13185        this.cursor = o.params ? o.params.start : 0;
13186        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13187
13188        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13189        this.field.dom.value = ap;
13190        this.first.setDisabled(ap == 1);
13191        this.prev.setDisabled(ap == 1);
13192        this.next.setDisabled(ap == ps);
13193        this.last.setDisabled(ap == ps);
13194        this.loading.enable();
13195        this.updateInfo();
13196     },
13197
13198     // private
13199     getPageData : function(){
13200         var total = this.ds.getTotalCount();
13201         return {
13202             total : total,
13203             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13204             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13205         };
13206     },
13207
13208     // private
13209     onLoadError : function(){
13210         this.loading.enable();
13211     },
13212
13213     // private
13214     onPagingKeydown : function(e){
13215         var k = e.getKey();
13216         var d = this.getPageData();
13217         if(k == e.RETURN){
13218             var v = this.field.dom.value, pageNum;
13219             if(!v || isNaN(pageNum = parseInt(v, 10))){
13220                 this.field.dom.value = d.activePage;
13221                 return;
13222             }
13223             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13224             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13225             e.stopEvent();
13226         }
13227         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
13228         {
13229           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13230           this.field.dom.value = pageNum;
13231           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13232           e.stopEvent();
13233         }
13234         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13235         {
13236           var v = this.field.dom.value, pageNum; 
13237           var increment = (e.shiftKey) ? 10 : 1;
13238           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13239             increment *= -1;
13240           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13241             this.field.dom.value = d.activePage;
13242             return;
13243           }
13244           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13245           {
13246             this.field.dom.value = parseInt(v, 10) + increment;
13247             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13248             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13249           }
13250           e.stopEvent();
13251         }
13252     },
13253
13254     // private
13255     beforeLoad : function(){
13256         if(this.loading){
13257             this.loading.disable();
13258         }
13259     },
13260
13261     // private
13262     onClick : function(which){
13263         var ds = this.ds;
13264         switch(which){
13265             case "first":
13266                 ds.load({params:{start: 0, limit: this.pageSize}});
13267             break;
13268             case "prev":
13269                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13270             break;
13271             case "next":
13272                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13273             break;
13274             case "last":
13275                 var total = ds.getTotalCount();
13276                 var extra = total % this.pageSize;
13277                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13278                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13279             break;
13280             case "refresh":
13281                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13282             break;
13283         }
13284     },
13285
13286     /**
13287      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13288      * @param {Roo.data.Store} store The data store to unbind
13289      */
13290     unbind : function(ds){
13291         ds.un("beforeload", this.beforeLoad, this);
13292         ds.un("load", this.onLoad, this);
13293         ds.un("loadexception", this.onLoadError, this);
13294         ds.un("remove", this.updateInfo, this);
13295         ds.un("add", this.updateInfo, this);
13296         this.ds = undefined;
13297     },
13298
13299     /**
13300      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13301      * @param {Roo.data.Store} store The data store to bind
13302      */
13303     bind : function(ds){
13304         ds.on("beforeload", this.beforeLoad, this);
13305         ds.on("load", this.onLoad, this);
13306         ds.on("loadexception", this.onLoadError, this);
13307         ds.on("remove", this.updateInfo, this);
13308         ds.on("add", this.updateInfo, this);
13309         this.ds = ds;
13310     }
13311 });