Roo/bootstrap/Button.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * 
19  * @constructor
20  * Do not use directly - it does not do anything..
21  * @param {Object} config The config object
22  */
23
24
25
26 Roo.bootstrap.Component = function(config){
27     Roo.bootstrap.Component.superclass.constructor.call(this, config);
28 };
29
30 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
31     
32     
33     allowDomMove : false, // to stop relocations in parent onRender...
34     
35     cls : false,
36     
37     style : false,
38     
39     autoCreate : false,
40     
41     initEvents : function() {  },
42     
43     xattr : false,
44     
45     parentId : false,
46     
47     can_build_overlaid : true,
48     
49     dataId : false,
50     
51     parent: function() {
52         // returns the parent component..
53         return Roo.ComponentMgr.get(this.parentId)
54         
55         
56     },
57     
58     // private
59     onRender : function(ct, position)
60     {
61        // Roo.log("Call onRender: " + this.xtype);
62         
63         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
64         
65         if(this.el){
66             if (this.el.attr('xtype')) {
67                 this.el.attr('xtypex', this.el.attr('xtype'));
68                 this.el.dom.removeAttribute('xtype');
69                 
70                 this.initEvents();
71             }
72             
73             return;
74         }
75         
76          
77         
78         var cfg = Roo.apply({},  this.getAutoCreate());
79         cfg.id = Roo.id();
80         
81         // fill in the extra attributes 
82         if (this.xattr && typeof(this.xattr) =='object') {
83             for (var i in this.xattr) {
84                 cfg[i] = this.xattr[i];
85             }
86         }
87         
88         if(this.dataId){
89             cfg.dataId = this.dataId;
90         }
91         
92         if (this.cls) {
93             cfg.cls += ' ' + this.cls;
94         }
95         if (this.style) { // fixme needs to support more complex style data.
96             cfg.style = this.style;
97         }
98         this.el = ct.createChild(cfg, position);
99         
100         if(this.tabIndex !== undefined){
101             this.el.dom.setAttribute('tabIndex', this.tabIndex);
102         }
103         this.initEvents();
104         
105         
106     },
107     
108     getChildContainer : function()
109     {
110         return this.el;
111     },
112     
113     
114     addxtype  : function(tree,cntr)
115     {
116         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
117                     (typeof(tree['flexy:foreach']) != 'undefined');
118         
119         var build_from_html =  Roo.XComponent.build_from_html;
120           
121         var is_body  = (tree.xtype == 'Body') ;
122           
123         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
124           
125         if (!has_flexy || !build_from_html || is_body || !page_has_body  ) {
126             
127             return this.addxtypeChild(tree,cntr);
128         }
129         
130         
131         var cn = this;
132         cntr = typeof(cntr == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         cn = Roo.factory(tree);
135            
136         cn.parentType = this.xtype; //??
137         cn.parentId = this.id;
138
139         var self_cntr_el = Roo.get(this[cntr]());
140         var ret = false;
141         
142         while (true) {
143             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
144             
145             if (!echild) {
146                 break;
147             }
148             
149             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
150                 break;
151             }
152             
153             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
154         }
155         return ret;
156     },
157     
158     addxtypeChild : function (tree, cntr)
159     {
160         var cn = this;
161         cntr = typeof(cntr == 'undefined' ) ? 'getChildContainer' : cntr;
162         
163         
164         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
165                     (typeof(tree['flexy:foreach']) != 'undefined');
166           
167         
168         
169         
170         // render the element if it's not BODY.
171         if (tree.xtype != 'Body') {
172             
173             cn = Roo.factory(tree);
174            
175             cn.parentType = this.xtype; //??
176             cn.parentId = this.id;
177             
178             var build_from_html =  Roo.XComponent.build_from_html;
179             
180             
181             // does the container contain child eleemnts with 'xtype' attributes.
182             // that match this xtype..
183             // note - when we render we create these as well..
184             // so we should check to see if body has xtype set.
185             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
186                
187                 var self_cntr_el = Roo.get(this[cntr]());
188                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
189                 
190                  
191                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
192                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
193                   
194                   
195                   
196                     cn.el = echild;
197                   //  Roo.log("GOT");
198                     //echild.dom.removeAttribute('xtype');
199                 } else {
200                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
201                    
202                 }
203             }
204            
205             
206                
207             // if object has flexy:if - then it may or may not be rendered.
208             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
209                 // skip a flexy if element.
210                 Roo.log('skipping render');
211              } else {
212                  
213                  
214                  
215                 // actually if flexy:foreach is found, we really want to create 
216                 // multiple copies here...
217                 cn.render(this[cntr]());
218              }
219             // then add the element..
220         }
221         
222         
223         // handle the kids..
224         
225         var nitems = [];
226         if (typeof (tree.menu) != 'undefined') {
227             tree.menu.parentType = cn.xtype;
228             tree.menu.triggerEl = cn.el;
229             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
230             
231         }
232         
233         if (!tree.items || !tree.items.length) {
234             cn.items = nitems;
235             return cn;
236         }
237         var items = tree.items;
238         delete tree.items;
239         
240         //Roo.log(items.length);
241             // add the items..
242         for(var i =0;i < items.length;i++) {
243             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
244         }
245         
246         cn.items = nitems;
247         
248         return cn;
249     }
250     
251     
252     
253     
254 });
255
256  /*
257  * - LGPL
258  *
259  * page container.
260  * 
261  */ 
262 Roo.bootstrap.Body = function(config){
263     Roo.bootstrap.Body.superclass.constructor.call(this, config);
264     this.el = Roo.get(document.body);
265 };
266
267 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
268       
269         autoCreate : {
270         cls: 'container'
271     },
272     onRender : function(ct, position){
273         
274         
275         //this.el.addClass([this.fieldClass, this.cls]);
276         
277     }
278     
279     
280  
281    
282 });
283
284  /*
285  * - LGPL
286  *
287  * button group
288  * 
289  */
290
291
292 /**
293  * @class Roo.bootstrap.ButtonGroup
294  * @extends Roo.bootstrap.Component
295  * Bootstrap ButtonGroup class
296  * @cfg {String} size lg | sm | xs (default empty normal)
297  * @cfg {String} align vertical | justified  (default none)
298  * @cfg {String} direction up | down (default down)
299  * @cfg {Boolean} toolbar false | true
300  * @cfg {Boolean} btn true | false
301  * 
302  * 
303  * @constructor
304  * Create a new Input
305  * @param {Object} config The config object
306  */
307
308 Roo.bootstrap.ButtonGroup = function(config){
309     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
310 };
311
312 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
313     
314     size: '',
315     align: '',
316     direction: '',
317     toolbar: false,
318     btn: true,
319
320     getAutoCreate : function(){
321         var cfg = {
322             cls: 'btn-group',
323             html : null
324         }
325         
326         cfg.html = this.html || cfg.html;
327         
328         if (this.toolbar) {
329             cfg = {
330                 cls: 'btn-toolbar',
331                 html: null
332             }
333             
334             return cfg;
335         }
336         
337         if (['vertical','justified'].indexOf(this.align)!==-1) {
338             cfg.cls = 'btn-group-' + this.align;
339             
340             if (this.align == 'justified') {
341                 console.log(this.items);
342             }
343         }
344         
345         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
346             cfg.cls += ' btn-group-' + this.size;
347         }
348         
349         if (this.direction == 'up') {
350             cfg.cls += ' dropup' ;
351         }
352         
353         return cfg;
354     }
355    
356 });
357
358  /*
359  * - LGPL
360  *
361  * button
362  * 
363  */
364
365 /**
366  * @class Roo.bootstrap.Button
367  * @extends Roo.bootstrap.Component
368  * Bootstrap Button class
369  * @cfg {String} html The button content
370  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger
371  * @cfg {String} size empty | lg | sm | xs
372  * @cfg {String} tag empty | a | input | submit
373  * @cfg {String} href empty or href
374  * @cfg {Boolean} disabled false | true
375  * @cfg {Boolean} isClose false | true
376  * @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
377  * @cfg {String} badge text for badge
378  * @cfg {String} theme default (or empty) | glow
379  * @cfg {Boolean} inverse false | true
380  * @cfg {Boolean} toggle false | true
381  * @cfg {String} ontext text for on toggle state
382  * @cfg {String} offtext text for off toggle state
383  * @cfg {Boolean} defaulton true | false
384  * @cfg {Boolean} preventDefault true | false
385  * 
386  * @constructor
387  * Create a new button
388  * @param {Object} config The config object
389  */
390
391
392 Roo.bootstrap.Button = function(config){
393     Roo.bootstrap.Button.superclass.constructor.call(this, config);
394     this.addEvents({
395         // raw events
396         /**
397          * @event click
398          * The raw click event for the entire grid.
399          * @param {Roo.EventObject} e
400          */
401         "click" : true
402     });
403 };
404
405 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
406     html: false,
407     active: false,
408     weight: '',
409     size: '',
410     tag: 'button',
411     href: '',
412     disabled: false,
413     isClose: false,
414     glyphicon: '',
415     badge: '',
416     theme: 'default',
417     inverse: false,
418     
419     toggle: false,
420     ontext: 'ON',
421     offtext: 'OFF',
422     defaulton: true,
423     preventDefault : true,
424     
425     getAutoCreate : function(){
426         
427         var cfg = {
428             tag : 'button',
429             cls : 'roo-button',
430             html: 'hello'
431         };
432         
433         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
434             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
435             this.tag = 'button';
436         } else {
437             cfg.tag = this.tag;
438         }
439         cfg.html = this.html || cfg.html;
440         
441         if (this.toggle===true) {
442             cfg={
443                 tag: 'div',
444                 cls: 'slider-frame roo-button',
445                 cn: [
446                     {
447                         tag: 'span',
448                         'data-on-text':'ON',
449                         'data-off-text':'OFF',
450                         cls: 'slider-button',
451                         html: this.offtext
452                     }
453                 ]
454             };
455             
456             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
457                 cfg.cls += ' '+this.weight;
458             }
459             
460             return cfg;
461         }
462         
463         if (this.isClose) {
464             cfg.cls += ' close';
465             
466             cfg["aria-hidden"] = true;
467             
468             cfg.html = "&times;";
469             
470             return cfg;
471         }
472         
473          
474         if (this.theme==='default') {
475             cfg.cls = 'btn roo-button';
476             
477             if (this.parentType != 'Navbar') {
478                 this.weight = this.weight.length ?  this.weight : 'default';
479             }
480             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
481                 
482                 cfg.cls += ' btn-' + this.weight;
483             }
484         } else if (this.theme==='glow') {
485             
486             cfg.tag = 'a';
487             cfg.cls = 'btn-glow roo-button';
488             
489             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
490                 
491                 cfg.cls += ' ' + this.weight;
492             }
493         }
494    
495         
496         if (this.inverse) {
497             this.cls += ' inverse';
498         }
499         
500         
501         if (this.active) {
502             cfg.cls += ' active';
503         }
504         
505         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
506          
507         //gsRoo.log(this.parentType);
508         if (this.parentType === 'Navbar') {
509             cfg.tag = 'li';
510             
511             cfg.cls = '';
512             cfg.cn =  [{
513                 tag : 'a',
514                 cls : 'roo-button',
515                 html : this.html,
516                 href : this.href || '#'
517             }];
518             if (this.menu) {
519                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
520                 cfg.cls += ' dropdown';
521             }   
522             
523             delete cfg.html;
524             
525         } else if (this.menu) {
526             cfg.tag = 'a';
527             cfg.cls += ' dropdown test';
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         if (this.glyphicon) {
541             cfg.html = ' ' + cfg.html;
542             
543             cfg.cn = [
544                 {
545                     tag: 'span',
546                     cls: 'glyphicon glyphicon-' + this.glyphicon
547                 }
548             ];
549         }
550         
551         if (this.badge) {
552             cfg.html += ' ';
553             
554             cfg.tag = 'a';
555             
556             cfg.cls='btn roo-button';
557             
558             cfg.href=this.href;
559             
560             cfg.cn = [
561                 cfg.html,
562                 {
563                     tag: 'span',
564                     cls: 'badge',
565                     html: this.badge
566                 }
567             ];
568             
569             cfg.html='';
570         }
571         
572         if (cfg.tag !== 'a' && this.href !== '') {
573             throw "Tag must be a to set href.";
574         } else if (this.href.length > 0) {
575             cfg.href = this.href;
576         }
577         
578         return cfg;
579     },
580     initEvents: function() {
581        // Roo.log('init events?');
582 //        Roo.log(this.el.dom);
583        if (this.el.hasClass('roo-button')) {
584             this.el.on('click', this.onClick, this);
585        } else {
586             this.el.select('.roo-button').on('click', this.onClick, this);
587        }
588        
589        
590         
591     },
592     onClick : function(e)
593     {
594         Roo.log('button on click ');
595         if(this.preventDefault){
596             e.preventDefault();
597         }
598         
599         this.fireEvent('click', this, e);
600     }
601     
602     
603 });
604
605  /*
606  * - LGPL
607  *
608  * column
609  * 
610  */
611
612 /**
613  * @class Roo.bootstrap.Column
614  * @extends Roo.bootstrap.Component
615  * Bootstrap Column class
616  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
617  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
618  * @cfg {Number} md colspan out of 12 for computer-sized screens
619  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
620  * @cfg {String} html content of column.
621  * 
622  * @constructor
623  * Create a new Column
624  * @param {Object} config The config object
625  */
626
627 Roo.bootstrap.Column = function(config){
628     Roo.bootstrap.Column.superclass.constructor.call(this, config);
629 };
630
631 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
632     
633     xs: null,
634     sm: null,
635     md: null,
636     lg: null,
637     html: '',
638     offset: 0,
639     
640     getAutoCreate : function(){
641         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
642         
643         cfg = {
644             tag: 'div',
645             cls: 'column'
646         };
647         
648         var settings=this;
649         ['xs','sm','md','lg'].map(function(size){
650             if (settings[size]) {
651                 cfg.cls += ' col-' + size + '-' + settings[size];
652             }
653         });
654         if (this.html.length) {
655             cfg.html = this.html;
656         }
657         
658         return cfg;
659     }
660    
661 });
662
663  
664
665  /*
666  * - LGPL
667  *
668  * page container.
669  * 
670  */
671
672
673 /**
674  * @class Roo.bootstrap.Container
675  * @extends Roo.bootstrap.Component
676  * Bootstrap Container class
677  * @cfg {Boolean} jumbotron is it a jumbotron element
678  * @cfg {String} html content of element
679  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
680  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
681  * @cfg {String} header content of header (for panel)
682  * @cfg {String} footer content of footer (for panel)
683  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
684  *     
685  * @constructor
686  * Create a new Container
687  * @param {Object} config The config object
688  */
689
690 Roo.bootstrap.Container = function(config){
691     Roo.bootstrap.Container.superclass.constructor.call(this, config);
692 };
693
694 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
695     
696     jumbotron : false,
697     well: '',
698     panel : '',
699     header: '',
700     footer : '',
701     sticky: '',
702   
703      
704     getChildContainer : function() {
705         
706         if(!this.el){
707             return false;
708         }
709         
710         if (this.panel.length) {
711             return this.el.select('.panel-body',true).first();
712         }
713         
714         return this.el;
715     },
716     
717     
718     getAutoCreate : function(){
719         
720         var cfg = {
721             html : '',
722             cls : ''
723         };
724         if (this.jumbotron) {
725             cfg.cls = 'jumbotron';
726         }
727         if (this.cls) {
728             cfg.cls = this.cls + '';
729         }
730         
731         if (this.sticky.length) {
732             var bd = Roo.get(document.body);
733             if (!bd.hasClass('bootstrap-sticky')) {
734                 bd.addClass('bootstrap-sticky');
735                 Roo.select('html',true).setStyle('height', '100%');
736             }
737              
738             cfg.cls += 'bootstrap-sticky-' + this.sticky;
739         }
740         
741         
742         if (this.well.length) {
743             switch (this.well) {
744                 case 'lg':
745                 case 'sm':
746                     cfg.cls +=' well well-' +this.well;
747                     break;
748                 default:
749                     cfg.cls +=' well';
750                     break;
751             }
752         }
753         
754         var body = cfg;
755         
756         if (this.panel.length) {
757             cfg.cls += 'panel panel-' + this.panel;
758             cfg.cn = [];
759             if (this.header.length) {
760                 cfg.cn.push({
761                     
762                     cls : 'panel-heading',
763                     cn : [{
764                         tag: 'h3',
765                         cls : 'panel-title',
766                         html : this.header
767                     }]
768                     
769                 });
770             }
771             body = false;
772             cfg.cn.push({
773                 cls : 'panel-body',
774                 html : this.html
775             });
776             
777             
778             if (this.footer.length) {
779                 cfg.cn.push({
780                     cls : 'panel-footer',
781                     html : this.footer
782                     
783                 });
784             }
785             
786         }
787         if (body) {
788             body.html = this.html || cfg.html;
789         }
790         if (!cfg.cls.length) {
791             cfg.cls =  'container';
792         }
793         
794         return cfg;
795     }
796    
797 });
798
799  /*
800  * - LGPL
801  *
802  * image
803  * 
804  */
805
806
807 /**
808  * @class Roo.bootstrap.Img
809  * @extends Roo.bootstrap.Component
810  * Bootstrap Img class
811  * @cfg {Boolean} imgResponsive false | true
812  * @cfg {String} border rounded | circle | thumbnail
813  * @cfg {String} src image source
814  * @cfg {String} alt image alternative text
815  * @cfg {String} href a tag href
816  * 
817  * @constructor
818  * Create a new Input
819  * @param {Object} config The config object
820  */
821
822 Roo.bootstrap.Img = function(config){
823     Roo.bootstrap.Img.superclass.constructor.call(this, config);
824     
825     this.addEvents({
826         // img events
827         /**
828          * @event click
829          * The img click event for the img.
830          * @param {Roo.EventObject} e
831          */
832         "click" : true
833     });
834 };
835
836 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
837     
838     imgResponsive: true,
839     border: '',
840     src: '',
841     href: false,
842
843     getAutoCreate : function(){
844         
845         var cfg = {
846             tag: 'img',
847             cls: 'img-responsive',
848             html : null
849         }
850         
851         cfg.html = this.html || cfg.html;
852         
853         cfg.src = this.src || cfg.src;
854         
855         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
856             cfg.cls += ' img-' + this.border;
857         }
858         
859         if(this.alt){
860             cfg.alt = this.alt;
861         }
862         
863         if(this.href){
864             var a = {
865                 tag: 'a',
866                 href: this.href,
867                 cn: [
868                     cfg
869                 ]
870             }
871         }
872         
873         
874         return (this.href) ? a : cfg;
875     },
876     
877     initEvents: function() {
878         
879         if(!this.href){
880             this.el.on('click', this.onClick, this);
881         }
882     },
883     
884     onClick : function(e)
885     {
886         Roo.log('img onclick');
887         this.fireEvent('click', this, e);
888     }
889    
890 });
891
892  /*
893  * - LGPL
894  *
895  * header
896  * 
897  */
898
899 /**
900  * @class Roo.bootstrap.Header
901  * @extends Roo.bootstrap.Component
902  * Bootstrap Header class
903  * @cfg {String} html content of header
904  * @cfg {Number} level (1|2|3|4|5|6) default 1
905  * 
906  * @constructor
907  * Create a new Header
908  * @param {Object} config The config object
909  */
910
911
912 Roo.bootstrap.Header  = function(config){
913     Roo.bootstrap.Header.superclass.constructor.call(this, config);
914 };
915
916 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
917     
918     //href : false,
919     html : false,
920     level : 1,
921     
922     
923     
924     getAutoCreate : function(){
925         
926         var cfg = {
927             tag: 'h' + (1 *this.level),
928             html: this.html || 'fill in html'
929         } ;
930         
931         return cfg;
932     }
933    
934 });
935
936  
937
938  /*
939  * - LGPL
940  *
941  * menu
942  * 
943  */
944
945 /**
946  * @class Roo.bootstrap.Menu
947  * @extends Roo.bootstrap.Component
948  * Bootstrap Menu class - container for MenuItems
949  * @cfg {String} type type of menu
950  * 
951  * @constructor
952  * Create a new Menu
953  * @param {Object} config The config object
954  */
955
956
957 Roo.bootstrap.Menu = function(config){
958     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
959 };
960
961 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
962     
963    /// html : false,
964     //align : '',
965     triggerEl : false,
966     type: false,
967     
968     
969     getChildContainer : function() {
970         return this.el;  
971     },
972     
973     getAutoCreate : function(){
974          
975         //if (['right'].indexOf(this.align)!==-1) {
976         //    cfg.cn[1].cls += ' pull-right'
977         //}
978         var cfg = {
979             tag : 'ul',
980             cls : 'dropdown-menu' 
981             
982         }
983         
984         if (this.type==='submenu') {
985             cfg.cls='submenu active'
986         }
987         
988         return cfg;
989     },
990     initEvents : function() {
991        // Roo.log("ADD event");
992        // Roo.log(this.triggerEl.dom);
993         this.triggerEl.on('click', this.toggle, this);
994         this.triggerEl.addClass('dropdown-toggle');
995         
996     },
997     toggle  : function(e)
998     {
999         //Roo.log(e.getTarget());
1000        // Roo.log(this.triggerEl.dom);
1001         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1002             return;
1003         }
1004         var isActive = this.triggerEl.hasClass('open');
1005         // if disabled.. ingore
1006         this.clearMenus(e);
1007         //if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
1008          // if mobile we use a backdrop because click events don't delegate
1009         // $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
1010         // }
1011  
1012        //var relatedTarget = { relatedTarget: this }
1013        //$parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
1014  
1015        //if (e.isDefaultPrevented()) return;
1016         
1017        this.triggerEl[isActive ? 'removeClass' : 'addClass']('open');
1018        
1019        //  .trigger('shown.bs.dropdown', relatedTarget)
1020  
1021        this.triggerEl.focus();
1022        Roo.log(e);
1023        e.preventDefault(); 
1024         
1025         
1026     },
1027     clearMenus : function()
1028     {
1029         //$(backdrop).remove()
1030         Roo.select('.dropdown-toggle',true).each(function(aa) {
1031             if (!aa.hasClass('open')) {
1032                 return;
1033             }
1034             // triger close...
1035             aa.removeClass('open');
1036           //var parent = getParent($(this))
1037           //var relatedTarget = { relatedTarget: this }
1038           
1039            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1040           //if (e.isDefaultPrevented()) return
1041            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1042         })
1043     }
1044     
1045    
1046 });
1047
1048  
1049
1050  /*
1051  * - LGPL
1052  *
1053  * menu item
1054  * 
1055  */
1056
1057
1058 /**
1059  * @class Roo.bootstrap.MenuItem
1060  * @extends Roo.bootstrap.Component
1061  * Bootstrap MenuItem class
1062  * @cfg {String} html the menu label
1063  * @cfg {String} href the link
1064  * 
1065  * 
1066  * @constructor
1067  * Create a new MenuItem
1068  * @param {Object} config The config object
1069  */
1070
1071
1072 Roo.bootstrap.MenuItem = function(config){
1073     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1074 };
1075
1076 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1077     
1078     href : false,
1079     html : false,
1080     
1081     getAutoCreate : function(){
1082         var cfg= {
1083             tag: 'li',
1084             cn: [
1085                 {
1086                     tag : 'a',
1087                     href : '#',
1088                     html : 'Link'
1089                 }
1090             ]
1091         };
1092         
1093         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1094         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1095         return cfg;
1096     }
1097    
1098 });
1099
1100  
1101
1102  /*
1103  * - LGPL
1104  *
1105  * menu separator
1106  * 
1107  */
1108
1109
1110 /**
1111  * @class Roo.bootstrap.MenuSeparator
1112  * @extends Roo.bootstrap.Component
1113  * Bootstrap MenuSeparator class
1114  * 
1115  * @constructor
1116  * Create a new MenuItem
1117  * @param {Object} config The config object
1118  */
1119
1120
1121 Roo.bootstrap.MenuSeparator = function(config){
1122     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1123 };
1124
1125 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1126     
1127     getAutoCreate : function(){
1128         var cfg = {
1129             cls: 'divider',
1130             tag : 'li'
1131         };
1132         
1133         return cfg;
1134     }
1135    
1136 });
1137
1138  
1139
1140  
1141 /*
1142 <div class="modal fade">
1143   <div class="modal-dialog">
1144     <div class="modal-content">
1145       <div class="modal-header">
1146         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1147         <h4 class="modal-title">Modal title</h4>
1148       </div>
1149       <div class="modal-body">
1150         <p>One fine body&hellip;</p>
1151       </div>
1152       <div class="modal-footer">
1153         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1154         <button type="button" class="btn btn-primary">Save changes</button>
1155       </div>
1156     </div><!-- /.modal-content -->
1157   </div><!-- /.modal-dialog -->
1158 </div><!-- /.modal -->
1159 */
1160 /*
1161  * - LGPL
1162  *
1163  * page contgainer.
1164  * 
1165  */
1166
1167 /**
1168  * @class Roo.bootstrap.Modal
1169  * @extends Roo.bootstrap.Component
1170  * Bootstrap Modal class
1171  * @cfg {String} title Title of dialog
1172  * @cfg {Array} buttons Array of buttons or standard button set..
1173  * 
1174  * @constructor
1175  * Create a new Modal Dialog
1176  * @param {Object} config The config object
1177  */
1178
1179 Roo.bootstrap.Modal = function(config){
1180     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1181     this.addEvents({
1182         // raw events
1183         /**
1184          * @event btnclick
1185          * The raw btnclick event for the button
1186          * @param {Roo.EventObject} e
1187          */
1188         "btnclick" : true
1189     });
1190 };
1191
1192 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1193     
1194     title : 'test dialog',
1195    
1196     buttons : false,
1197
1198     onRender : function(ct, position)
1199     {
1200         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1201      
1202         if(!this.el){
1203             var cfg = Roo.apply({},  this.getAutoCreate());
1204             cfg.id = Roo.id();
1205             //if(!cfg.name){
1206             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1207             //}
1208             //if (!cfg.name.length) {
1209             //    delete cfg.name;
1210            // }
1211             if (this.cls) {
1212                 cfg.cls += ' ' + this.cls;
1213             }
1214             if (this.style) {
1215                 cfg.style = this.style;
1216             }
1217             this.el = Roo.get(document.body).createChild(cfg, position);
1218         }
1219         //var type = this.el.dom.type;
1220         
1221         if(this.tabIndex !== undefined){
1222             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1223         }
1224         
1225         
1226         
1227         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1228         this.maskEl.enableDisplayMode("block");
1229         this.maskEl.hide();
1230         //this.el.addClass("x-dlg-modal");
1231     
1232         
1233         if (this.buttons) {
1234             Roo.each(this.buttons, function(bb) {
1235                 b = Roo.apply({}, bb);
1236                 b.xns = b.xns || Roo.bootstrap;
1237                 b.xtype = b.xtype || 'Button';
1238                 if (typeof(b.listeners) == 'undefined') {
1239                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1240                 }
1241                 
1242                 var btn = Roo.factory(b);
1243                 
1244                 btn.onRender(this.el.select('.modal-footer').first());
1245                 
1246             },this);
1247         }
1248         // render the children.
1249         
1250         var items = this.items;
1251         delete this.items;
1252         var nitems = [];
1253         for(var i =0;i < items.length;i++) {
1254             nitems.push(this.addxtype(Roo.apply({}, items[i])));
1255         }
1256         this.items = nitems;
1257         this.initEvents();
1258         //this.el.addClass([this.fieldClass, this.cls]);
1259         
1260     },
1261     getAutoCreate : function(){
1262         
1263         
1264         var bdy = {
1265                 cls : 'modal-body',
1266                 html : this.html || ''
1267         };
1268         
1269          
1270         return {
1271             cls: "modal fade",
1272             cn : [
1273                 {
1274                     cls: "modal-dialog",
1275                     cn : [
1276                         {
1277                             cls : "modal-content",
1278                             cn : [
1279                                 {
1280                                     cls : 'modal-header',
1281                                     cn : [
1282                                         {
1283                                             tag: 'button',
1284                                             cls : 'close',
1285                                             html : '&times'
1286                                         },
1287                                         {
1288                                             tag: 'h4',
1289                                             cls : 'modal-title',
1290                                             html : this.title
1291                                         }
1292                                     
1293                                     ]
1294                                 },
1295                                 bdy,
1296                                 {
1297                                     cls : 'modal-footer' 
1298                                 }
1299                                 
1300                                 
1301                             ]
1302                             
1303                         }
1304                     ]
1305                         
1306                 }
1307             ]
1308             
1309             
1310         };
1311           
1312     },
1313     getChildContainer : function() {
1314          
1315          return this.el.select('.modal-body',true).first();
1316         
1317     },
1318     getButtonContainer : function() {
1319          return this.el.select('.modal-footer',true).first();
1320         
1321     },
1322     initEvents : function()
1323     {
1324         this.el.select('.modal-header .close').on('click', this.hide, this);
1325 //        
1326 //        this.addxtype(this);
1327     },
1328     show : function() {
1329         
1330         if (!this.rendered) {
1331             this.render();
1332         }
1333        
1334         this.el.addClass('on');
1335         this.el.removeClass('fade');
1336         this.el.setStyle('display', 'block');
1337         Roo.get(document.body).addClass("x-body-masked");
1338         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1339         this.maskEl.show();
1340         this.el.setStyle('zIndex', '10001');
1341         
1342         
1343     },
1344     hide : function()
1345     {
1346         Roo.log('Modal hide?!');
1347         this.maskEl.hide();
1348         this.el.removeClass('on');
1349         this.el.addClass('fade');
1350         this.el.setStyle('display', 'none');
1351     },
1352     onButtonClick: function(btn,e)
1353     {
1354         //Roo.log([a,b,c]);
1355         this.fireEvent('btnclick', btn.name, e);
1356     }
1357 });
1358
1359
1360 Roo.apply(Roo.bootstrap.Modal,  {
1361     /**
1362          * Button config that displays a single OK button
1363          * @type Object
1364          */
1365         OK :  [{
1366             name : 'ok',
1367             weight : 'primary',
1368             html : 'OK'
1369         }], 
1370         /**
1371          * Button config that displays Yes and No buttons
1372          * @type Object
1373          */
1374         YESNO : [
1375             {
1376                 name  :'yes',
1377                 weight : 'primary',
1378                 html : 'Yes'
1379             },
1380             {
1381                 name  : 'no',
1382                 html : 'No'
1383             }
1384         ],
1385         
1386         /**
1387          * Button config that displays OK and Cancel buttons
1388          * @type Object
1389          */
1390         OKCANCEL : [
1391             {
1392                 name : 'ok',
1393                 weight : 'primary',
1394                 html : 'OK'
1395             },
1396             {
1397                name : 'cancel',
1398                 html : 'Cancel'
1399             }
1400         ],
1401         /**
1402          * Button config that displays Yes, No and Cancel buttons
1403          * @type Object
1404          */
1405         YESNOCANCEL : [
1406             {
1407                 name : 'yes',
1408                 weight : 'primary',
1409                 html : 'Yes'
1410             },
1411             {
1412                 name : 'no',
1413                 html : 'No'
1414             },
1415             {
1416                 name : 'cancel',
1417                 html : 'Cancel'
1418             }
1419         ]
1420 });
1421  /*
1422  * - LGPL
1423  *
1424  * navbar
1425  * 
1426  */
1427
1428 /**
1429  * @class Roo.bootstrap.Navbar
1430  * @extends Roo.bootstrap.Component
1431  * Bootstrap Navbar class
1432  * @cfg {Boolean} sidebar has side bar
1433  * @cfg {Boolean} bar is a bar?
1434  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
1435  * @cfg {String} brand what is brand
1436  * @cfg {Boolean} inverse is inverted color
1437  * @cfg {String} type (nav | pills | tabs)
1438  * @cfg {Boolean} arrangement stacked | justified
1439  * @cfg {String} align (left | right) alignment
1440  *
1441  * 
1442  * @constructor
1443  * Create a new Navbar
1444  * @param {Object} config The config object
1445  */
1446
1447
1448 Roo.bootstrap.Navbar = function(config){
1449     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
1450 };
1451
1452 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
1453     
1454     sidebar: false,
1455     
1456     bar: false,
1457     brand: '',
1458     inverse: false,
1459     position: '',
1460     align : false,
1461     type: 'nav',
1462     arrangement: '',
1463     
1464     getAutoCreate : function(){
1465         var cfg = {
1466             cls : 'navbar'
1467         };
1468         
1469         if (this.sidebar === true) {
1470             cfg = {
1471                 tag: 'div',
1472                 cls: 'sidebar-nav'
1473             };
1474             return cfg;
1475         }
1476         
1477         if (this.bar === true) {
1478             cfg = {
1479                 tag: 'nav',
1480                 cls: 'navbar',
1481                 role: 'navigation',
1482                 cn: [
1483                     {
1484                         tag: 'div',
1485                         cls: 'navbar-header',
1486                         cn: [
1487                             {
1488                             tag: 'button',
1489                             type: 'button',
1490                             cls: 'navbar-toggle',
1491                             'data-toggle': 'collapse',
1492                             cn: [
1493                                 {
1494                                     tag: 'span',
1495                                     cls: 'sr-only',
1496                                     html: 'Toggle navigation'
1497                                 },
1498                                 {
1499                                     tag: 'span',
1500                                     cls: 'icon-bar'
1501                                 },
1502                                 {
1503                                     tag: 'span',
1504                                     cls: 'icon-bar'
1505                                 },
1506                                 {
1507                                     tag: 'span',
1508                                     cls: 'icon-bar'
1509                                 }
1510                             ]
1511                             }
1512                         ]
1513                     },
1514                     {
1515                     tag: 'div',
1516                     cls: 'collapse navbar-collapse'
1517                     }
1518                 ]
1519             };
1520             
1521             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
1522             
1523             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
1524             cfg.cls += ' navbar-' + this.position;
1525             cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
1526             }
1527             
1528             if (this.brand !== '') {
1529                 cfg.cn[0].cn.push({
1530                     tag: 'a',
1531                     href: '#',
1532                     cls: 'navbar-brand',
1533                     cn: [
1534                     this.brand
1535                     ]
1536                 });
1537             }
1538             
1539             return cfg;
1540         
1541         } else if (this.bar === false) {
1542             
1543         } else {
1544             Roo.log('Property \'bar\' in of Navbar must be either true or false')
1545         }
1546         
1547         cfg.cn = [
1548             {
1549                 cls: 'nav',
1550                 tag : 'ul'
1551             }
1552         ];
1553         
1554         if (['tabs','pills'].indexOf(this.type)!==-1) {
1555             cfg.cn[0].cls += ' nav-' + this.type
1556         } else {
1557             if (this.type!=='nav') {
1558             Roo.log('nav type must be nav/tabs/pills')
1559             }
1560             cfg.cn[0].cls += ' navbar-nav'
1561         }
1562         
1563         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
1564             cfg.cn[0].cls += ' nav-' + this.arrangement;
1565         }
1566         
1567         if (this.align === 'right') {
1568             cfg.cn[0].cls += ' navbar-right';
1569         }
1570         if (this.inverse) {
1571             cfg.cls += ' navbar-inverse';
1572             
1573         }
1574         
1575         
1576         return cfg;
1577     },
1578     
1579     initEvents :function ()
1580     {
1581         //Roo.log(this.el.select('.navbar-toggle',true));
1582         this.el.select('.navbar-toggle',true).on('click', function() {
1583            // Roo.log('click');
1584             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
1585         }, this);
1586     },
1587     
1588     
1589     getChildContainer : function()
1590     {
1591         if (this.bar === true) {
1592             return this.el.select('.collapse',true).first();
1593         }
1594         console.log(this);
1595         return this.el;
1596     }
1597    
1598 });
1599
1600  
1601
1602  /*
1603  * - LGPL
1604  *
1605  * nav group
1606  * 
1607  */
1608
1609 /**
1610  * @class Roo.bootstrap.NavGroup
1611  * @extends Roo.bootstrap.Component
1612  * Bootstrap NavGroup class
1613  * @cfg {String} align left | right
1614  * @cfg {Boolean} inverse false | true
1615  * 
1616  * @constructor
1617  * Create a new nav group
1618  * @param {Object} config The config object
1619  */
1620
1621 Roo.bootstrap.NavGroup = function(config){
1622     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
1623 };
1624
1625 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
1626     
1627     align: '',
1628     inverse: false,
1629     form: false,
1630     
1631     getAutoCreate : function(){
1632         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
1633         
1634         cfg = {
1635             tag : 'ul',
1636             cls: 'nav navbar-nav' 
1637         }
1638         
1639         if (this.parent().sidebar === true) {
1640             cfg = {
1641                 tag: 'ul',
1642                 cls: 'dashboard-menu'
1643             }
1644             
1645             return cfg;
1646         }
1647         
1648         if (this.form === true) {
1649             cfg = {
1650                 tag: 'form',
1651                 cls: 'navbar-form'
1652             }
1653             
1654             if (this.align === 'right') {
1655                 cfg.cls += ' navbar-right';
1656             } else {
1657                 cfg.cls += ' navbar-left';
1658             }
1659         }
1660         
1661         
1662         if (this.align === 'right') {
1663             cfg.cls += ' navbar-right';
1664         }
1665         
1666         if (this.inverse) {
1667             cfg.cls += ' navbar-inverse';
1668             
1669         }
1670         
1671         
1672         return cfg;
1673     }
1674    
1675 });
1676
1677  
1678
1679  /*
1680  * - LGPL
1681  *
1682  * row
1683  * 
1684  */
1685
1686 /**
1687  * @class Roo.bootstrap.Navbar.Item
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap Navbar.Button class
1690  * @cfg {String} href  link to
1691  * @cfg {String} html content of button
1692  * @cfg {String} badge text inside badge
1693  * @cfg {String} glyphicon name of glyphicon
1694   
1695  * @constructor
1696  * Create a new Navbar Button
1697  * @param {Object} config The config object
1698  */
1699 Roo.bootstrap.Navbar.Item = function(config){
1700     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
1701 };
1702
1703 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
1704     
1705     href: false,
1706     html: '',
1707     badge: '',
1708     icon: false,
1709     glyphicon: false,
1710     
1711     getAutoCreate : function(){
1712         
1713         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
1714         
1715         if (this.parent().parent().sidebar === true) {
1716             cfg = {
1717                 tag: 'li',
1718                 cls: '',
1719                 cn: [
1720                     {
1721                         tag: 'p',
1722                         cls: ''
1723                     }
1724                 ]
1725             }
1726             
1727             if (this.html) {
1728                 cfg.cn[0].html = this.html;
1729             }
1730             
1731             if (this.active) {
1732                 this.cls += ' active';
1733             }
1734             
1735             if (this.menu) {
1736                 cfg.cn[0].cls += ' dropdown-toggle';
1737                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
1738             }
1739             
1740             if (this.href) {
1741                 cfg.cn[0].tag = 'a',
1742                 cfg.cn[0].href = this.href;
1743             }
1744             
1745             if (this.glyphicon) {
1746                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
1747             }
1748             
1749             return cfg;
1750         }
1751         
1752         cfg = {
1753             tag: 'li'
1754         }
1755         cfg.cn = [
1756             {
1757                 tag: 'p',
1758                 html: 'Text'
1759             }
1760         ];
1761         
1762         if (this.glyphicon) {
1763             if(cfg.html){cfg.html = ' ' + this.html};
1764             cfg.cn=[
1765                 {
1766                     tag: 'span',
1767                     cls: 'glyphicon glyphicon-' + this.glyphicon
1768                 }
1769             ];
1770         }
1771         
1772         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1773         if (this.menu) {
1774             cfg.cn[0].tag='a';
1775             cfg.cn[0].href='#';
1776             cfg.cn[0].html += " <span class='caret'></span>";
1777         //}else if (!this.href) {
1778         //    cfg.cn[0].tag='p';
1779         //    cfg.cn[0].cls='navbar-text';
1780         } else {
1781             cfg.cn[0].tag='a';
1782             cfg.cn[0].href=this.href||'#';
1783             cfg.cn[0].html=this.html;
1784         }
1785         
1786         if (this.badge !== '') {
1787             
1788             cfg.cn[0].cn=[
1789                 cfg.cn[0].html + ' ',
1790                 {
1791                     tag: 'span',
1792                     cls: 'badge',
1793                     html: this.badge
1794                 }
1795             ];
1796             cfg.cn[0].html=''
1797         }
1798          
1799         
1800         return cfg;
1801     },
1802     initEvents: function() {
1803        // Roo.log('init events?');
1804        // Roo.log(this.el.dom);
1805         this.el.select('a',true).on('click',
1806                 function(e) {
1807                     this.fireEvent('click', this);
1808                 },
1809                 this
1810         );
1811     }
1812    
1813 });
1814  
1815
1816  /*
1817  * - LGPL
1818  *
1819  * row
1820  * 
1821  */
1822
1823 /**
1824  * @class Roo.bootstrap.Row
1825  * @extends Roo.bootstrap.Component
1826  * Bootstrap Row class (contains columns...)
1827  * 
1828  * @constructor
1829  * Create a new Row
1830  * @param {Object} config The config object
1831  */
1832
1833 Roo.bootstrap.Row = function(config){
1834     Roo.bootstrap.Row.superclass.constructor.call(this, config);
1835 };
1836
1837 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
1838     
1839     getAutoCreate : function(){
1840        return {
1841             cls: 'row clearfix'
1842        };
1843     }
1844     
1845     
1846 });
1847
1848  
1849
1850  /*
1851  * - LGPL
1852  *
1853  * element
1854  * 
1855  */
1856
1857 /**
1858  * @class Roo.bootstrap.Element
1859  * @extends Roo.bootstrap.Component
1860  * Bootstrap Element class
1861  * @cfg {String} html contents of the element
1862  * @cfg {String} tag tag of the element
1863  * @cfg {String} cls class of the element
1864  * 
1865  * @constructor
1866  * Create a new Element
1867  * @param {Object} config The config object
1868  */
1869
1870 Roo.bootstrap.Element = function(config){
1871     Roo.bootstrap.Element.superclass.constructor.call(this, config);
1872 };
1873
1874 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
1875     
1876     tag: 'div',
1877     cls: '',
1878     html: '',
1879      
1880     
1881     getAutoCreate : function(){
1882         
1883         var cfg = {
1884             tag: this.tag,
1885             cls: '',
1886             html: this.html
1887         }
1888         
1889         return cfg;
1890     }
1891    
1892 });
1893
1894  
1895
1896  /*
1897  * - LGPL
1898  *
1899  * pagination
1900  * 
1901  */
1902
1903 /**
1904  * @class Roo.bootstrap.Pagination
1905  * @extends Roo.bootstrap.Component
1906  * Bootstrap Pagination class
1907  * @cfg {String} size xs | sm | md | lg
1908  * @cfg {Boolean} inverse false | true
1909  * @cfg {Number} from pagination starting number
1910  * @cfg {Number} to pagination ending number
1911  * @cfg {String} align empty or left | right
1912  * @cfg {Number} active active page number
1913  * 
1914  * @constructor
1915  * Create a new Pagination
1916  * @param {Object} config The config object
1917  */
1918
1919 Roo.bootstrap.Pagination = function(config){
1920     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
1921 };
1922
1923 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
1924     
1925     cls: false,
1926     size: false,
1927     inverse: false,
1928     from: 1,
1929     to: 4,
1930     align: false,
1931     active: 1,
1932     
1933     getAutoCreate : function(){
1934         cfg = {
1935             tag: 'ul',
1936                 cls: 'pagination',
1937                 cn: []
1938         };
1939         if (this.inverse) {
1940             cfg.cls += ' inverse';
1941         }
1942         if (this.html) {
1943             cfg.html=this.html;
1944         }
1945         if (this.cls) {
1946             cfg.cls=this.cls;
1947         }
1948         cfg.cn[0]={
1949             tag: 'li',
1950             cn: [
1951                 {
1952                     tag: 'a',
1953                     href:'#',
1954                     html: '&laquo;'
1955                 }
1956             ]
1957         };
1958         var from=this.from>0?this.from:1;
1959         var to=this.to-from<=10?this.to:from+10;
1960         var active=this.active>=from&&this.active<=to?this.active:null;
1961         for (var i=from;i<=to;i++) {
1962             cfg.cn.push(
1963                 {
1964                     tag: 'li',
1965                     cls: active===i?'active':'',
1966                     cn: [
1967                         {
1968                             tag: 'a',
1969                             href: '#',
1970                             html: i
1971                         }
1972                     ]
1973                 }
1974             );
1975         }
1976         
1977         cfg.cn.push(
1978             {
1979                 tag: 'li',
1980                 cn: [
1981                     {
1982                        tag: 'a',
1983                        href: '#',
1984                        html: '&raquo;'
1985                     }
1986                 ]
1987             }
1988         );
1989         
1990         return cfg;
1991     }
1992    
1993 });
1994
1995  
1996
1997  /*
1998  * - LGPL
1999  *
2000  * slider
2001  * 
2002  */
2003
2004
2005 /**
2006  * @class Roo.bootstrap.Slider
2007  * @extends Roo.bootstrap.Component
2008  * Bootstrap Slider class
2009  *    
2010  * @constructor
2011  * Create a new Slider
2012  * @param {Object} config The config object
2013  */
2014
2015 Roo.bootstrap.Slider = function(config){
2016     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2017 };
2018
2019 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2020     
2021     getAutoCreate : function(){
2022         
2023         var cfg = {
2024             tag: 'div',
2025             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2026             cn: [
2027                 {
2028                     tag: 'a',
2029                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2030                 }
2031             ]
2032         }
2033         
2034         return cfg;
2035     }
2036    
2037 });
2038
2039  /*
2040  * - LGPL
2041  *
2042  * table
2043  * 
2044  */
2045
2046 /**
2047  * @class Roo.bootstrap.Table
2048  * @extends Roo.bootstrap.Component
2049  * Bootstrap Table class
2050  * 
2051  * @constructor
2052  * Create a new Table
2053  * @param {Object} config The config object
2054  */
2055
2056 Roo.bootstrap.Table = function(config){
2057     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2058 };
2059
2060 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2061     
2062     html: false,
2063     cls: false,
2064     
2065     getAutoCreate : function(){
2066         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2067         
2068         cfg = {
2069             tag: 'table',
2070             cn: [
2071                 {
2072                     tag: 'tbody'
2073                 }
2074             ]
2075         }
2076         if (this.html) {
2077             cfg.html=this.html
2078         }
2079         if (this.cls) {
2080             cfg.cls=this.cls
2081         }
2082         
2083         return cfg;
2084     }
2085    
2086 });
2087
2088  
2089
2090  /*
2091  * - LGPL
2092  *
2093  * table cell
2094  * 
2095  */
2096
2097 /**
2098  * @class Roo.bootstrap.TableCell
2099  * @extends Roo.bootstrap.Component
2100  * Bootstrap TableCell class
2101  * 
2102  * @constructor
2103  * Create a new TableCell
2104  * @param {Object} config The config object
2105  */
2106
2107 Roo.bootstrap.TableCell = function(config){
2108     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
2109 };
2110
2111 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
2112     
2113     getAutoCreate : function(){
2114         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
2115         
2116         cfg = {
2117             tag: 'td'
2118         }
2119         if (this.html) {
2120             cfg.html=this.html
2121         }
2122         if (this.cls) {
2123             cfg.cls=this.cls
2124         }
2125         
2126         return cfg;
2127     }
2128    
2129 });
2130
2131  
2132
2133  /*
2134  * - LGPL
2135  *
2136  * table row
2137  * 
2138  */
2139
2140 /**
2141  * @class Roo.bootstrap.TableRow
2142  * @extends Roo.bootstrap.Component
2143  * Bootstrap TableRow class
2144  * 
2145  * @constructor
2146  * Create a new TableRow
2147  * @param {Object} config The config object
2148  */
2149
2150 Roo.bootstrap.TableRow = function(config){
2151     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
2152 };
2153
2154 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
2155     
2156     getAutoCreate : function(){
2157         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
2158         
2159         cfg = {
2160             tag: 'tr'
2161         }
2162         
2163         return cfg;
2164     }
2165    
2166 });
2167
2168  
2169
2170  /*
2171  * Based on:
2172  * Ext JS Library 1.1.1
2173  * Copyright(c) 2006-2007, Ext JS, LLC.
2174  *
2175  * Originally Released Under LGPL - original licence link has changed is not relivant.
2176  *
2177  * Fork - LGPL
2178  * <script type="text/javascript">
2179  */
2180
2181 // as we use this in bootstrap.
2182 Roo.namespace('Roo.form');
2183  /**
2184  * @class Roo.form.Action
2185  * Internal Class used to handle form actions
2186  * @constructor
2187  * @param {Roo.form.BasicForm} el The form element or its id
2188  * @param {Object} config Configuration options
2189  */
2190
2191  
2192  
2193 // define the action interface
2194 Roo.form.Action = function(form, options){
2195     this.form = form;
2196     this.options = options || {};
2197 };
2198 /**
2199  * Client Validation Failed
2200  * @const 
2201  */
2202 Roo.form.Action.CLIENT_INVALID = 'client';
2203 /**
2204  * Server Validation Failed
2205  * @const 
2206  */
2207 Roo.form.Action.SERVER_INVALID = 'server';
2208  /**
2209  * Connect to Server Failed
2210  * @const 
2211  */
2212 Roo.form.Action.CONNECT_FAILURE = 'connect';
2213 /**
2214  * Reading Data from Server Failed
2215  * @const 
2216  */
2217 Roo.form.Action.LOAD_FAILURE = 'load';
2218
2219 Roo.form.Action.prototype = {
2220     type : 'default',
2221     failureType : undefined,
2222     response : undefined,
2223     result : undefined,
2224
2225     // interface method
2226     run : function(options){
2227
2228     },
2229
2230     // interface method
2231     success : function(response){
2232
2233     },
2234
2235     // interface method
2236     handleResponse : function(response){
2237
2238     },
2239
2240     // default connection failure
2241     failure : function(response){
2242         
2243         this.response = response;
2244         this.failureType = Roo.form.Action.CONNECT_FAILURE;
2245         this.form.afterAction(this, false);
2246     },
2247
2248     processResponse : function(response){
2249         this.response = response;
2250         if(!response.responseText){
2251             return true;
2252         }
2253         this.result = this.handleResponse(response);
2254         return this.result;
2255     },
2256
2257     // utility functions used internally
2258     getUrl : function(appendParams){
2259         var url = this.options.url || this.form.url || this.form.el.dom.action;
2260         if(appendParams){
2261             var p = this.getParams();
2262             if(p){
2263                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
2264             }
2265         }
2266         return url;
2267     },
2268
2269     getMethod : function(){
2270         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
2271     },
2272
2273     getParams : function(){
2274         var bp = this.form.baseParams;
2275         var p = this.options.params;
2276         if(p){
2277             if(typeof p == "object"){
2278                 p = Roo.urlEncode(Roo.applyIf(p, bp));
2279             }else if(typeof p == 'string' && bp){
2280                 p += '&' + Roo.urlEncode(bp);
2281             }
2282         }else if(bp){
2283             p = Roo.urlEncode(bp);
2284         }
2285         return p;
2286     },
2287
2288     createCallback : function(){
2289         return {
2290             success: this.success,
2291             failure: this.failure,
2292             scope: this,
2293             timeout: (this.form.timeout*1000),
2294             upload: this.form.fileUpload ? this.success : undefined
2295         };
2296     }
2297 };
2298
2299 Roo.form.Action.Submit = function(form, options){
2300     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
2301 };
2302
2303 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
2304     type : 'submit',
2305
2306     haveProgress : false,
2307     uploadComplete : false,
2308     
2309     // uploadProgress indicator.
2310     uploadProgress : function()
2311     {
2312         if (!this.form.progressUrl) {
2313             return;
2314         }
2315         
2316         if (!this.haveProgress) {
2317             Roo.MessageBox.progress("Uploading", "Uploading");
2318         }
2319         if (this.uploadComplete) {
2320            Roo.MessageBox.hide();
2321            return;
2322         }
2323         
2324         this.haveProgress = true;
2325    
2326         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
2327         
2328         var c = new Roo.data.Connection();
2329         c.request({
2330             url : this.form.progressUrl,
2331             params: {
2332                 id : uid
2333             },
2334             method: 'GET',
2335             success : function(req){
2336                //console.log(data);
2337                 var rdata = false;
2338                 var edata;
2339                 try  {
2340                    rdata = Roo.decode(req.responseText)
2341                 } catch (e) {
2342                     Roo.log("Invalid data from server..");
2343                     Roo.log(edata);
2344                     return;
2345                 }
2346                 if (!rdata || !rdata.success) {
2347                     Roo.log(rdata);
2348                     Roo.MessageBox.alert(Roo.encode(rdata));
2349                     return;
2350                 }
2351                 var data = rdata.data;
2352                 
2353                 if (this.uploadComplete) {
2354                    Roo.MessageBox.hide();
2355                    return;
2356                 }
2357                    
2358                 if (data){
2359                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
2360                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
2361                     );
2362                 }
2363                 this.uploadProgress.defer(2000,this);
2364             },
2365        
2366             failure: function(data) {
2367                 Roo.log('progress url failed ');
2368                 Roo.log(data);
2369             },
2370             scope : this
2371         });
2372            
2373     },
2374     
2375     
2376     run : function()
2377     {
2378         // run get Values on the form, so it syncs any secondary forms.
2379         this.form.getValues();
2380         
2381         var o = this.options;
2382         var method = this.getMethod();
2383         var isPost = method == 'POST';
2384         if(o.clientValidation === false || this.form.isValid()){
2385             
2386             if (this.form.progressUrl) {
2387                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
2388                     (new Date() * 1) + '' + Math.random());
2389                     
2390             } 
2391             
2392             
2393             Roo.Ajax.request(Roo.apply(this.createCallback(), {
2394                 form:this.form.el.dom,
2395                 url:this.getUrl(!isPost),
2396                 method: method,
2397                 params:isPost ? this.getParams() : null,
2398                 isUpload: this.form.fileUpload
2399             }));
2400             
2401             this.uploadProgress();
2402
2403         }else if (o.clientValidation !== false){ // client validation failed
2404             this.failureType = Roo.form.Action.CLIENT_INVALID;
2405             this.form.afterAction(this, false);
2406         }
2407     },
2408
2409     success : function(response)
2410     {
2411         this.uploadComplete= true;
2412         if (this.haveProgress) {
2413             Roo.MessageBox.hide();
2414         }
2415         
2416         
2417         var result = this.processResponse(response);
2418         if(result === true || result.success){
2419             this.form.afterAction(this, true);
2420             return;
2421         }
2422         if(result.errors){
2423             this.form.markInvalid(result.errors);
2424             this.failureType = Roo.form.Action.SERVER_INVALID;
2425         }
2426         this.form.afterAction(this, false);
2427     },
2428     failure : function(response)
2429     {
2430         this.uploadComplete= true;
2431         if (this.haveProgress) {
2432             Roo.MessageBox.hide();
2433         }
2434         
2435         this.response = response;
2436         this.failureType = Roo.form.Action.CONNECT_FAILURE;
2437         this.form.afterAction(this, false);
2438     },
2439     
2440     handleResponse : function(response){
2441         if(this.form.errorReader){
2442             var rs = this.form.errorReader.read(response);
2443             var errors = [];
2444             if(rs.records){
2445                 for(var i = 0, len = rs.records.length; i < len; i++) {
2446                     var r = rs.records[i];
2447                     errors[i] = r.data;
2448                 }
2449             }
2450             if(errors.length < 1){
2451                 errors = null;
2452             }
2453             return {
2454                 success : rs.success,
2455                 errors : errors
2456             };
2457         }
2458         var ret = false;
2459         try {
2460             ret = Roo.decode(response.responseText);
2461         } catch (e) {
2462             ret = {
2463                 success: false,
2464                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
2465                 errors : []
2466             };
2467         }
2468         return ret;
2469         
2470     }
2471 });
2472
2473
2474 Roo.form.Action.Load = function(form, options){
2475     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
2476     this.reader = this.form.reader;
2477 };
2478
2479 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
2480     type : 'load',
2481
2482     run : function(){
2483         
2484         Roo.Ajax.request(Roo.apply(
2485                 this.createCallback(), {
2486                     method:this.getMethod(),
2487                     url:this.getUrl(false),
2488                     params:this.getParams()
2489         }));
2490     },
2491
2492     success : function(response){
2493         
2494         var result = this.processResponse(response);
2495         if(result === true || !result.success || !result.data){
2496             this.failureType = Roo.form.Action.LOAD_FAILURE;
2497             this.form.afterAction(this, false);
2498             return;
2499         }
2500         this.form.clearInvalid();
2501         this.form.setValues(result.data);
2502         this.form.afterAction(this, true);
2503     },
2504
2505     handleResponse : function(response){
2506         if(this.form.reader){
2507             var rs = this.form.reader.read(response);
2508             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
2509             return {
2510                 success : rs.success,
2511                 data : data
2512             };
2513         }
2514         return Roo.decode(response.responseText);
2515     }
2516 });
2517
2518 Roo.form.Action.ACTION_TYPES = {
2519     'load' : Roo.form.Action.Load,
2520     'submit' : Roo.form.Action.Submit
2521 };/*
2522  * - LGPL
2523  *
2524  * form
2525  * 
2526  */
2527
2528 /**
2529  * @class Roo.bootstrap.Form
2530  * @extends Roo.bootstrap.Component
2531  * Bootstrap Form class
2532  * @cfg {String} method  GET | POST (default POST)
2533  * @cfg {String} labelAlign top | left (default top)
2534   * @cfg {String} align left  | right - for navbars
2535
2536  * 
2537  * @constructor
2538  * Create a new Form
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.Form = function(config){
2544     Roo.bootstrap.Form.superclass.constructor.call(this, config);
2545     this.addEvents({
2546         /**
2547          * @event clientvalidation
2548          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
2549          * @param {Form} this
2550          * @param {Boolean} valid true if the form has passed client-side validation
2551          */
2552         clientvalidation: true,
2553         /**
2554          * @event beforeaction
2555          * Fires before any action is performed. Return false to cancel the action.
2556          * @param {Form} this
2557          * @param {Action} action The action to be performed
2558          */
2559         beforeaction: true,
2560         /**
2561          * @event actionfailed
2562          * Fires when an action fails.
2563          * @param {Form} this
2564          * @param {Action} action The action that failed
2565          */
2566         actionfailed : true,
2567         /**
2568          * @event actioncomplete
2569          * Fires when an action is completed.
2570          * @param {Form} this
2571          * @param {Action} action The action that completed
2572          */
2573         actioncomplete : true
2574     });
2575     
2576 };
2577
2578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
2579       
2580      /**
2581      * @cfg {String} method
2582      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
2583      */
2584     method : 'POST',
2585     /**
2586      * @cfg {String} url
2587      * The URL to use for form actions if one isn't supplied in the action options.
2588      */
2589     /**
2590      * @cfg {Boolean} fileUpload
2591      * Set to true if this form is a file upload.
2592      */
2593      
2594     /**
2595      * @cfg {Object} baseParams
2596      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
2597      */
2598       
2599     /**
2600      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
2601      */
2602     timeout: 30,
2603     /**
2604      * @cfg {Sting} align (left|right) for navbar forms
2605      */
2606     align : 'left',
2607
2608     // private
2609     activeAction : null,
2610  
2611     /**
2612      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
2613      * element by passing it or its id or mask the form itself by passing in true.
2614      * @type Mixed
2615      */
2616     waitMsgTarget : false,
2617     
2618      
2619     
2620     /**
2621      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
2622      * element by passing it or its id or mask the form itself by passing in true.
2623      * @type Mixed
2624      */
2625     
2626     getAutoCreate : function(){
2627         
2628         var cfg = {
2629             tag: 'form',
2630             method : this.method || 'POST',
2631             id : this.id || Roo.id(),
2632             cls : ''
2633         }
2634         if (this.parent().xtype.match(/^Nav/)) {
2635             cfg.cls = 'navbar-form navbar-' + this.align;
2636             
2637         }
2638         
2639         if (this.labelAlign == 'left' ) {
2640             cfg.cls += ' form-horizontal';
2641         }
2642         
2643         
2644         return cfg;
2645     },
2646     initEvents : function()
2647     {
2648         this.el.on('submit', this.onSubmit, this);
2649         
2650         
2651     },
2652     // private
2653     onSubmit : function(e){
2654         e.stopEvent();
2655     },
2656     
2657      /**
2658      * Returns true if client-side validation on the form is successful.
2659      * @return Boolean
2660      */
2661     isValid : function(){
2662         var items = this.getItems();
2663         var valid = true;
2664         items.each(function(f){
2665            if(!f.validate()){
2666                valid = false;
2667                
2668            }
2669         });
2670         return valid;
2671     },
2672     /**
2673      * Returns true if any fields in this form have changed since their original load.
2674      * @return Boolean
2675      */
2676     isDirty : function(){
2677         var dirty = false;
2678         var items = this.getItems();
2679         items.each(function(f){
2680            if(f.isDirty()){
2681                dirty = true;
2682                return false;
2683            }
2684            return true;
2685         });
2686         return dirty;
2687     },
2688      /**
2689      * Performs a predefined action (submit or load) or custom actions you define on this form.
2690      * @param {String} actionName The name of the action type
2691      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
2692      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
2693      * accept other config options):
2694      * <pre>
2695 Property          Type             Description
2696 ----------------  ---------------  ----------------------------------------------------------------------------------
2697 url               String           The url for the action (defaults to the form's url)
2698 method            String           The form method to use (defaults to the form's method, or POST if not defined)
2699 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
2700 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
2701                                    validate the form on the client (defaults to false)
2702      * </pre>
2703      * @return {BasicForm} this
2704      */
2705     doAction : function(action, options){
2706         if(typeof action == 'string'){
2707             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
2708         }
2709         if(this.fireEvent('beforeaction', this, action) !== false){
2710             this.beforeAction(action);
2711             action.run.defer(100, action);
2712         }
2713         return this;
2714     },
2715     
2716     // private
2717     beforeAction : function(action){
2718         var o = action.options;
2719         
2720         // not really supported yet.. ??
2721         
2722         //if(this.waitMsgTarget === true){
2723             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
2724         //}else if(this.waitMsgTarget){
2725         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
2726         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
2727         //}else {
2728         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
2729        // }
2730          
2731     },
2732
2733     // private
2734     afterAction : function(action, success){
2735         this.activeAction = null;
2736         var o = action.options;
2737         
2738         //if(this.waitMsgTarget === true){
2739             this.el.unmask();
2740         //}else if(this.waitMsgTarget){
2741         //    this.waitMsgTarget.unmask();
2742         //}else{
2743         //    Roo.MessageBox.updateProgress(1);
2744         //    Roo.MessageBox.hide();
2745        // }
2746         // 
2747         if(success){
2748             if(o.reset){
2749                 this.reset();
2750             }
2751             Roo.callback(o.success, o.scope, [this, action]);
2752             this.fireEvent('actioncomplete', this, action);
2753             
2754         }else{
2755             
2756             // failure condition..
2757             // we have a scenario where updates need confirming.
2758             // eg. if a locking scenario exists..
2759             // we look for { errors : { needs_confirm : true }} in the response.
2760             if (
2761                 (typeof(action.result) != 'undefined')  &&
2762                 (typeof(action.result.errors) != 'undefined')  &&
2763                 (typeof(action.result.errors.needs_confirm) != 'undefined')
2764            ){
2765                 var _t = this;
2766                 Roo.log("not supported yet");
2767                  /*
2768                 
2769                 Roo.MessageBox.confirm(
2770                     "Change requires confirmation",
2771                     action.result.errorMsg,
2772                     function(r) {
2773                         if (r != 'yes') {
2774                             return;
2775                         }
2776                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
2777                     }
2778                     
2779                 );
2780                 */
2781                 
2782                 
2783                 return;
2784             }
2785             
2786             Roo.callback(o.failure, o.scope, [this, action]);
2787             // show an error message if no failed handler is set..
2788             if (!this.hasListener('actionfailed')) {
2789                 Roo.log("need to add dialog support");
2790                 /*
2791                 Roo.MessageBox.alert("Error",
2792                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
2793                         action.result.errorMsg :
2794                         "Saving Failed, please check your entries or try again"
2795                 );
2796                 */
2797             }
2798             
2799             this.fireEvent('actionfailed', this, action);
2800         }
2801         
2802     },
2803     /**
2804      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
2805      * @param {String} id The value to search for
2806      * @return Field
2807      */
2808     findField : function(id){
2809         var items = this.getItems();
2810         var field = items.get(id);
2811         if(!field){
2812              items.each(function(f){
2813                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
2814                     field = f;
2815                     return false;
2816                 }
2817                 return true;
2818             });
2819         }
2820         return field || null;
2821     },
2822      /**
2823      * Mark fields in this form invalid in bulk.
2824      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
2825      * @return {BasicForm} this
2826      */
2827     markInvalid : function(errors){
2828         if(errors instanceof Array){
2829             for(var i = 0, len = errors.length; i < len; i++){
2830                 var fieldError = errors[i];
2831                 var f = this.findField(fieldError.id);
2832                 if(f){
2833                     f.markInvalid(fieldError.msg);
2834                 }
2835             }
2836         }else{
2837             var field, id;
2838             for(id in errors){
2839                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
2840                     field.markInvalid(errors[id]);
2841                 }
2842             }
2843         }
2844         //Roo.each(this.childForms || [], function (f) {
2845         //    f.markInvalid(errors);
2846         //});
2847         
2848         return this;
2849     },
2850
2851     /**
2852      * Set values for fields in this form in bulk.
2853      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
2854      * @return {BasicForm} this
2855      */
2856     setValues : function(values){
2857         if(values instanceof Array){ // array of objects
2858             for(var i = 0, len = values.length; i < len; i++){
2859                 var v = values[i];
2860                 var f = this.findField(v.id);
2861                 if(f){
2862                     f.setValue(v.value);
2863                     if(this.trackResetOnLoad){
2864                         f.originalValue = f.getValue();
2865                     }
2866                 }
2867             }
2868         }else{ // object hash
2869             var field, id;
2870             for(id in values){
2871                 if(typeof values[id] != 'function' && (field = this.findField(id))){
2872                     
2873                     if (field.setFromData && 
2874                         field.valueField && 
2875                         field.displayField &&
2876                         // combos' with local stores can 
2877                         // be queried via setValue()
2878                         // to set their value..
2879                         (field.store && !field.store.isLocal)
2880                         ) {
2881                         // it's a combo
2882                         var sd = { };
2883                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
2884                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
2885                         field.setFromData(sd);
2886                         
2887                     } else {
2888                         field.setValue(values[id]);
2889                     }
2890                     
2891                     
2892                     if(this.trackResetOnLoad){
2893                         field.originalValue = field.getValue();
2894                     }
2895                 }
2896             }
2897         }
2898          
2899         //Roo.each(this.childForms || [], function (f) {
2900         //    f.setValues(values);
2901         //});
2902                 
2903         return this;
2904     },
2905
2906     /**
2907      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
2908      * they are returned as an array.
2909      * @param {Boolean} asString
2910      * @return {Object}
2911      */
2912     getValues : function(asString){
2913         //if (this.childForms) {
2914             // copy values from the child forms
2915         //    Roo.each(this.childForms, function (f) {
2916         //        this.setValues(f.getValues());
2917         //    }, this);
2918         //}
2919         
2920         
2921         
2922         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
2923         if(asString === true){
2924             return fs;
2925         }
2926         return Roo.urlDecode(fs);
2927     },
2928     
2929     /**
2930      * Returns the fields in this form as an object with key/value pairs. 
2931      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
2932      * @return {Object}
2933      */
2934     getFieldValues : function(with_hidden)
2935     {
2936         var items = this.getItems();
2937         var ret = {};
2938         items.each(function(f){
2939             if (!f.getName()) {
2940                 return;
2941             }
2942             var v = f.getValue();
2943             if (f.inputType =='radio') {
2944                 if (typeof(ret[f.getName()]) == 'undefined') {
2945                     ret[f.getName()] = ''; // empty..
2946                 }
2947                 
2948                 if (!f.el.dom.checked) {
2949                     return;
2950                     
2951                 }
2952                 v = f.el.dom.value;
2953                 
2954             }
2955             
2956             // not sure if this supported any more..
2957             if ((typeof(v) == 'object') && f.getRawValue) {
2958                 v = f.getRawValue() ; // dates..
2959             }
2960             // combo boxes where name != hiddenName...
2961             if (f.name != f.getName()) {
2962                 ret[f.name] = f.getRawValue();
2963             }
2964             ret[f.getName()] = v;
2965         });
2966         
2967         return ret;
2968     },
2969
2970     /**
2971      * Clears all invalid messages in this form.
2972      * @return {BasicForm} this
2973      */
2974     clearInvalid : function(){
2975         var items = this.getItems();
2976         
2977         items.each(function(f){
2978            f.clearInvalid();
2979         });
2980         
2981         
2982         
2983         return this;
2984     },
2985
2986     /**
2987      * Resets this form.
2988      * @return {BasicForm} this
2989      */
2990     reset : function(){
2991         var items = this.getItems();
2992         items.each(function(f){
2993             f.reset();
2994         });
2995         
2996         Roo.each(this.childForms || [], function (f) {
2997             f.reset();
2998         });
2999        
3000         
3001         return this;
3002     },
3003     getItems : function()
3004     {
3005         var r=new Roo.util.MixedCollection(false, function(o){
3006             return o.id || (o.id = Roo.id());
3007         });
3008         var iter = function(el) {
3009             if (el.inputEl) {
3010                 r.add(el);
3011             }
3012             if (!el.items) {
3013                 return;
3014             }
3015             Roo.each(el.items,function(e) {
3016                 iter(e);
3017             });
3018             
3019             
3020         };
3021         iter(this);
3022         return r;
3023         
3024         
3025         
3026         
3027     }
3028     
3029 });
3030
3031  
3032 /*
3033  * Based on:
3034  * Ext JS Library 1.1.1
3035  * Copyright(c) 2006-2007, Ext JS, LLC.
3036  *
3037  * Originally Released Under LGPL - original licence link has changed is not relivant.
3038  *
3039  * Fork - LGPL
3040  * <script type="text/javascript">
3041  */
3042 /**
3043  * @class Roo.form.VTypes
3044  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
3045  * @singleton
3046  */
3047 Roo.form.VTypes = function(){
3048     // closure these in so they are only created once.
3049     var alpha = /^[a-zA-Z_]+$/;
3050     var alphanum = /^[a-zA-Z0-9_]+$/;
3051     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
3052     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
3053
3054     // All these messages and functions are configurable
3055     return {
3056         /**
3057          * The function used to validate email addresses
3058          * @param {String} value The email address
3059          */
3060         'email' : function(v){
3061             return email.test(v);
3062         },
3063         /**
3064          * The error text to display when the email validation function returns false
3065          * @type String
3066          */
3067         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
3068         /**
3069          * The keystroke filter mask to be applied on email input
3070          * @type RegExp
3071          */
3072         'emailMask' : /[a-z0-9_\.\-@]/i,
3073
3074         /**
3075          * The function used to validate URLs
3076          * @param {String} value The URL
3077          */
3078         'url' : function(v){
3079             return url.test(v);
3080         },
3081         /**
3082          * The error text to display when the url validation function returns false
3083          * @type String
3084          */
3085         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
3086         
3087         /**
3088          * The function used to validate alpha values
3089          * @param {String} value The value
3090          */
3091         'alpha' : function(v){
3092             return alpha.test(v);
3093         },
3094         /**
3095          * The error text to display when the alpha validation function returns false
3096          * @type String
3097          */
3098         'alphaText' : 'This field should only contain letters and _',
3099         /**
3100          * The keystroke filter mask to be applied on alpha input
3101          * @type RegExp
3102          */
3103         'alphaMask' : /[a-z_]/i,
3104
3105         /**
3106          * The function used to validate alphanumeric values
3107          * @param {String} value The value
3108          */
3109         'alphanum' : function(v){
3110             return alphanum.test(v);
3111         },
3112         /**
3113          * The error text to display when the alphanumeric validation function returns false
3114          * @type String
3115          */
3116         'alphanumText' : 'This field should only contain letters, numbers and _',
3117         /**
3118          * The keystroke filter mask to be applied on alphanumeric input
3119          * @type RegExp
3120          */
3121         'alphanumMask' : /[a-z0-9_]/i
3122     };
3123 }();/*
3124  * - LGPL
3125  *
3126  * Input
3127  * 
3128  */
3129
3130 /**
3131  * @class Roo.bootstrap.Input
3132  * @extends Roo.bootstrap.Component
3133  * Bootstrap Input class
3134  * @cfg {Boolean} disabled is it disabled
3135  * @cfg {String} fieldLabel - the label associated
3136  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
3137  * @cfg {String} name name of the input
3138  * @cfg {string} fieldLabel - the label associated
3139  * @cfg {string}  inputType - input / file submit ...
3140  * @cfg {string} placeholder - placeholder to put in text.
3141  * @cfg {string}  before - input group add on before
3142  * @cfg {string} after - input group add on after
3143  * @cfg {string} size - (lg|sm) or leave empty..
3144  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
3145  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
3146  * @cfg {Number} md colspan out of 12 for computer-sized screens
3147  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
3148  * @cfg {string} value default value of the input
3149  * @cfg {Number} labelWidth set the width of label (0-12)
3150  * 
3151  * 
3152  * @constructor
3153  * Create a new Input
3154  * @param {Object} config The config object
3155  */
3156
3157 Roo.bootstrap.Input = function(config){
3158     Roo.bootstrap.Input.superclass.constructor.call(this, config);
3159    
3160         this.addEvents({
3161             /**
3162              * @event focus
3163              * Fires when this field receives input focus.
3164              * @param {Roo.form.Field} this
3165              */
3166             focus : true,
3167             /**
3168              * @event blur
3169              * Fires when this field loses input focus.
3170              * @param {Roo.form.Field} this
3171              */
3172             blur : true,
3173             /**
3174              * @event specialkey
3175              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
3176              * {@link Roo.EventObject#getKey} to determine which key was pressed.
3177              * @param {Roo.form.Field} this
3178              * @param {Roo.EventObject} e The event object
3179              */
3180             specialkey : true,
3181             /**
3182              * @event change
3183              * Fires just before the field blurs if the field value has changed.
3184              * @param {Roo.form.Field} this
3185              * @param {Mixed} newValue The new value
3186              * @param {Mixed} oldValue The original value
3187              */
3188             change : true,
3189             /**
3190              * @event invalid
3191              * Fires after the field has been marked as invalid.
3192              * @param {Roo.form.Field} this
3193              * @param {String} msg The validation message
3194              */
3195             invalid : true,
3196             /**
3197              * @event valid
3198              * Fires after the field has been validated with no errors.
3199              * @param {Roo.form.Field} this
3200              */
3201             valid : true,
3202              /**
3203              * @event keyup
3204              * Fires after the key up
3205              * @param {Roo.form.Field} this
3206              * @param {Roo.EventObject}  e The event Object
3207              */
3208             keyup : true
3209         });
3210 };
3211
3212 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
3213      /**
3214      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
3215       automatic validation (defaults to "keyup").
3216      */
3217     validationEvent : "keyup",
3218      /**
3219      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
3220      */
3221     validateOnBlur : true,
3222     /**
3223      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
3224      */
3225     validationDelay : 250,
3226      /**
3227      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
3228      */
3229     focusClass : "x-form-focus",  // not needed???
3230     
3231        
3232     /**
3233      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
3234      */
3235     invalidClass : "has-error",
3236     
3237     /**
3238      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
3239      */
3240     selectOnFocus : false,
3241     
3242      /**
3243      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
3244      */
3245     maskRe : null,
3246        /**
3247      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
3248      */
3249     vtype : null,
3250     
3251       /**
3252      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
3253      */
3254     disableKeyFilter : false,
3255     
3256        /**
3257      * @cfg {Boolean} disabled True to disable the field (defaults to false).
3258      */
3259     disabled : false,
3260      /**
3261      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
3262      */
3263     allowBlank : true,
3264     /**
3265      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
3266      */
3267     blankText : "This field is required",
3268     
3269      /**
3270      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
3271      */
3272     minLength : 0,
3273     /**
3274      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
3275      */
3276     maxLength : Number.MAX_VALUE,
3277     /**
3278      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
3279      */
3280     minLengthText : "The minimum length for this field is {0}",
3281     /**
3282      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
3283      */
3284     maxLengthText : "The maximum length for this field is {0}",
3285   
3286     
3287     /**
3288      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
3289      * If available, this function will be called only after the basic validators all return true, and will be passed the
3290      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
3291      */
3292     validator : null,
3293     /**
3294      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
3295      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
3296      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
3297      */
3298     regex : null,
3299     /**
3300      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
3301      */
3302     regexText : "",
3303     
3304     
3305     
3306     fieldLabel : '',
3307     inputType : 'text',
3308     
3309     name : false,
3310     placeholder: false,
3311     before : false,
3312     after : false,
3313     size : false,
3314     // private
3315     hasFocus : false,
3316     preventMark: false,
3317     isFormField : true,
3318     value : '',
3319     labelWidth : 2,
3320     
3321     
3322     getAutoCreate : function(){
3323         
3324         var parent = this.parent();
3325         
3326         var align = parent.labelAlign;
3327         
3328         var id = Roo.id();
3329         
3330         var cfg = {};
3331         
3332         if(this.inputType != 'hidden'){
3333             cfg.cls = 'form-group' //input-group
3334         }
3335         
3336 //        var cfg = {
3337 //            cls: 'form-group' //input-group
3338 //        };
3339
3340         var input =  {
3341             tag: 'input',
3342             id : id,
3343             type : this.inputType,
3344             value : this.value,
3345             cls : 'form-control',
3346             placeholder : this.placeholder || '' 
3347             
3348         };
3349         if (this.name) {
3350             input.name = this.name;
3351         }
3352         if (this.size) {
3353             input.cls += ' input-' + this.size;
3354         }
3355         var settings=this;
3356         ['xs','sm','md','lg'].map(function(size){
3357             if (settings[size]) {
3358                 cfg.cls += ' col-' + size + '-' + settings[size];
3359             }
3360         });
3361         
3362         var inputblock = input;
3363         
3364         if (this.before || this.after) {
3365             
3366             inputblock = {
3367                 cls : 'input-group',
3368                 cn :  [] 
3369             };
3370             if (this.before) {
3371                 inputblock.cn.push({
3372                     tag :'span',
3373                     cls : 'input-group-addon',
3374                     html : this.before
3375                 });
3376             }
3377             inputblock.cn.push(input);
3378             if (this.after) {
3379                 inputblock.cn.push({
3380                     tag :'span',
3381                     cls : 'input-group-addon',
3382                     html : this.after
3383                 });
3384             }
3385             
3386         }
3387         
3388         Roo.log(align);
3389         Roo.log(this.fieldLabel.length);
3390         
3391         if (align ==='left' && this.fieldLabel.length) {
3392                 Roo.log("left and has label");
3393                 cfg.cn = [
3394                     
3395                     {
3396                         tag: 'label',
3397                         'for' :  id,
3398                         cls : 'control-label col-sm-' + this.labelWidth,
3399                         html : this.fieldLabel
3400                         
3401                     },
3402                     {
3403                         cls : "col-sm-" + (12 - this.labelWidth), 
3404                         cn: [
3405                             inputblock
3406                         ]
3407                     }
3408                     
3409                 ];
3410         } else if ( this.fieldLabel.length) {
3411                 Roo.log(" label");
3412                  cfg.cn = [
3413                    
3414                     {
3415                         tag: 'label',
3416                         //cls : 'input-group-addon',
3417                         html : this.fieldLabel
3418                         
3419                     },
3420                     
3421                     inputblock
3422                     
3423                 ];
3424
3425         } else {
3426             
3427                    Roo.log(" no label && no align");
3428                 cfg.cn = [
3429                     
3430                         inputblock
3431                     
3432                 ];
3433                 
3434                 
3435         }
3436          
3437         
3438         
3439         
3440         if (this.disabled) {
3441             input.disabled=true;
3442         }
3443         return cfg;
3444         
3445     },
3446     /**
3447      * return the real input element.
3448      */
3449     inputEl: function ()
3450     {
3451         return this.el.select('input.form-control',true).first();
3452     },
3453     setDisabled : function(v)
3454     {
3455         var i  = this.inputEl().dom;
3456         if (v) {
3457             i.removeAttribute('disabled');
3458             return;
3459             
3460         }
3461         i.setAttribute('disabled','true');
3462     },
3463     initEvents : function()
3464     {
3465         
3466         this.inputEl().on("keydown" , this.fireKey,  this);
3467         this.inputEl().on("focus", this.onFocus,  this);
3468         this.inputEl().on("blur", this.onBlur,  this);
3469         this.inputEl().relayEvent('keyup', this);
3470
3471         // reference to original value for reset
3472         this.originalValue = this.getValue();
3473         //Roo.form.TextField.superclass.initEvents.call(this);
3474         if(this.validationEvent == 'keyup'){
3475             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
3476             this.inputEl().on('keyup', this.filterValidation, this);
3477         }
3478         else if(this.validationEvent !== false){
3479             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
3480         }
3481         
3482         if(this.selectOnFocus){
3483             this.on("focus", this.preFocus, this);
3484             
3485         }
3486         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
3487             this.inputEl().on("keypress", this.filterKeys, this);
3488         }
3489        /* if(this.grow){
3490             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
3491             this.el.on("click", this.autoSize,  this);
3492         }
3493         */
3494         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
3495             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
3496         }
3497         
3498     },
3499     filterValidation : function(e){
3500         if(!e.isNavKeyPress()){
3501             this.validationTask.delay(this.validationDelay);
3502         }
3503     },
3504      /**
3505      * Validates the field value
3506      * @return {Boolean} True if the value is valid, else false
3507      */
3508     validate : function(){
3509         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
3510         if(this.disabled || this.validateValue(this.getRawValue())){
3511             this.clearInvalid();
3512             return true;
3513         }
3514         return false;
3515     },
3516     
3517     
3518     /**
3519      * Validates a value according to the field's validation rules and marks the field as invalid
3520      * if the validation fails
3521      * @param {Mixed} value The value to validate
3522      * @return {Boolean} True if the value is valid, else false
3523      */
3524     validateValue : function(value){
3525         if(value.length < 1)  { // if it's blank
3526              if(this.allowBlank){
3527                 this.clearInvalid();
3528                 return true;
3529              }else{
3530                 this.markInvalid(this.blankText);
3531                 return false;
3532              }
3533         }
3534         if(value.length < this.minLength){
3535             this.markInvalid(String.format(this.minLengthText, this.minLength));
3536             return false;
3537         }
3538         if(value.length > this.maxLength){
3539             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
3540             return false;
3541         }
3542         if(this.vtype){
3543             var vt = Roo.form.VTypes;
3544             if(!vt[this.vtype](value, this)){
3545                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
3546                 return false;
3547             }
3548         }
3549         if(typeof this.validator == "function"){
3550             var msg = this.validator(value);
3551             if(msg !== true){
3552                 this.markInvalid(msg);
3553                 return false;
3554             }
3555         }
3556         if(this.regex && !this.regex.test(value)){
3557             this.markInvalid(this.regexText);
3558             return false;
3559         }
3560         return true;
3561     },
3562
3563     
3564     
3565      // private
3566     fireKey : function(e){
3567         //Roo.log('field ' + e.getKey());
3568         if(e.isNavKeyPress()){
3569             this.fireEvent("specialkey", this, e);
3570         }
3571     },
3572     focus : function (selectText){
3573         if(this.rendered){
3574             this.inputEl().focus();
3575             if(selectText === true){
3576                 this.inputEl().dom.select();
3577             }
3578         }
3579         return this;
3580     } ,
3581     
3582     onFocus : function(){
3583         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
3584            // this.el.addClass(this.focusClass);
3585         }
3586         if(!this.hasFocus){
3587             this.hasFocus = true;
3588             this.startValue = this.getValue();
3589             this.fireEvent("focus", this);
3590         }
3591     },
3592     
3593     beforeBlur : Roo.emptyFn,
3594
3595     
3596     // private
3597     onBlur : function(){
3598         this.beforeBlur();
3599         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
3600             //this.el.removeClass(this.focusClass);
3601         }
3602         this.hasFocus = false;
3603         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
3604             this.validate();
3605         }
3606         var v = this.getValue();
3607         if(String(v) !== String(this.startValue)){
3608             this.fireEvent('change', this, v, this.startValue);
3609         }
3610         this.fireEvent("blur", this);
3611     },
3612     
3613     /**
3614      * Resets the current field value to the originally loaded value and clears any validation messages
3615      */
3616     reset : function(){
3617         this.setValue(this.originalValue);
3618         this.clearInvalid();
3619     },
3620      /**
3621      * Returns the name of the field
3622      * @return {Mixed} name The name field
3623      */
3624     getName: function(){
3625         return this.name;
3626     },
3627      /**
3628      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
3629      * @return {Mixed} value The field value
3630      */
3631     getValue : function(){
3632         var v = this.inputEl().getValue();
3633         return v;
3634     },
3635     /**
3636      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
3637      * @return {Mixed} value The field value
3638      */
3639     getRawValue : function(){
3640         var v = this.inputEl().getValue();
3641         
3642         return v;
3643     },
3644     
3645     /**
3646      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
3647      * @param {Mixed} value The value to set
3648      */
3649     setRawValue : function(v){
3650         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
3651     },
3652     
3653     selectText : function(start, end){
3654         var v = this.getRawValue();
3655         if(v.length > 0){
3656             start = start === undefined ? 0 : start;
3657             end = end === undefined ? v.length : end;
3658             var d = this.inputEl().dom;
3659             if(d.setSelectionRange){
3660                 d.setSelectionRange(start, end);
3661             }else if(d.createTextRange){
3662                 var range = d.createTextRange();
3663                 range.moveStart("character", start);
3664                 range.moveEnd("character", v.length-end);
3665                 range.select();
3666             }
3667         }
3668     },
3669     
3670     /**
3671      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
3672      * @param {Mixed} value The value to set
3673      */
3674     setValue : function(v){
3675         this.value = v;
3676         if(this.rendered){
3677             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
3678             this.validate();
3679         }
3680     },
3681     
3682     /*
3683     processValue : function(value){
3684         if(this.stripCharsRe){
3685             var newValue = value.replace(this.stripCharsRe, '');
3686             if(newValue !== value){
3687                 this.setRawValue(newValue);
3688                 return newValue;
3689             }
3690         }
3691         return value;
3692     },
3693   */
3694     preFocus : function(){
3695         
3696         if(this.selectOnFocus){
3697             this.inputEl().dom.select();
3698         }
3699     },
3700     filterKeys : function(e){
3701         var k = e.getKey();
3702         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
3703             return;
3704         }
3705         var c = e.getCharCode(), cc = String.fromCharCode(c);
3706         if(Roo.isIE && (e.isSpecialKey() || !cc)){
3707             return;
3708         }
3709         if(!this.maskRe.test(cc)){
3710             e.stopEvent();
3711         }
3712     },
3713      /**
3714      * Clear any invalid styles/messages for this field
3715      */
3716     clearInvalid : function(){
3717         
3718         if(!this.el || this.preventMark){ // not rendered
3719             return;
3720         }
3721         this.el.removeClass(this.invalidClass);
3722         /*
3723         switch(this.msgTarget){
3724             case 'qtip':
3725                 this.el.dom.qtip = '';
3726                 break;
3727             case 'title':
3728                 this.el.dom.title = '';
3729                 break;
3730             case 'under':
3731                 if(this.errorEl){
3732                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
3733                 }
3734                 break;
3735             case 'side':
3736                 if(this.errorIcon){
3737                     this.errorIcon.dom.qtip = '';
3738                     this.errorIcon.hide();
3739                     this.un('resize', this.alignErrorIcon, this);
3740                 }
3741                 break;
3742             default:
3743                 var t = Roo.getDom(this.msgTarget);
3744                 t.innerHTML = '';
3745                 t.style.display = 'none';
3746                 break;
3747         }
3748         */
3749         this.fireEvent('valid', this);
3750     },
3751      /**
3752      * Mark this field as invalid
3753      * @param {String} msg The validation message
3754      */
3755     markInvalid : function(msg){
3756         if(!this.el  || this.preventMark){ // not rendered
3757             return;
3758         }
3759         this.el.addClass(this.invalidClass);
3760         /*
3761         msg = msg || this.invalidText;
3762         switch(this.msgTarget){
3763             case 'qtip':
3764                 this.el.dom.qtip = msg;
3765                 this.el.dom.qclass = 'x-form-invalid-tip';
3766                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
3767                     Roo.QuickTips.enable();
3768                 }
3769                 break;
3770             case 'title':
3771                 this.el.dom.title = msg;
3772                 break;
3773             case 'under':
3774                 if(!this.errorEl){
3775                     var elp = this.el.findParent('.x-form-element', 5, true);
3776                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
3777                     this.errorEl.setWidth(elp.getWidth(true)-20);
3778                 }
3779                 this.errorEl.update(msg);
3780                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
3781                 break;
3782             case 'side':
3783                 if(!this.errorIcon){
3784                     var elp = this.el.findParent('.x-form-element', 5, true);
3785                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
3786                 }
3787                 this.alignErrorIcon();
3788                 this.errorIcon.dom.qtip = msg;
3789                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
3790                 this.errorIcon.show();
3791                 this.on('resize', this.alignErrorIcon, this);
3792                 break;
3793             default:
3794                 var t = Roo.getDom(this.msgTarget);
3795                 t.innerHTML = msg;
3796                 t.style.display = this.msgDisplay;
3797                 break;
3798         }
3799         */
3800         this.fireEvent('invalid', this, msg);
3801     },
3802     // private
3803     SafariOnKeyDown : function(event)
3804     {
3805         // this is a workaround for a password hang bug on chrome/ webkit.
3806         
3807         var isSelectAll = false;
3808         
3809         if(this.inputEl().dom.selectionEnd > 0){
3810             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
3811         }
3812         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
3813             event.preventDefault();
3814             this.setValue('');
3815             return;
3816         }
3817         
3818         if(isSelectAll){ // backspace and delete key
3819             
3820             event.preventDefault();
3821             // this is very hacky as keydown always get's upper case.
3822             //
3823             var cc = String.fromCharCode(event.getCharCode());
3824             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
3825             
3826         }
3827         
3828         
3829     }
3830 });
3831
3832  
3833 /*
3834  * - LGPL
3835  *
3836  * trigger field - base class for combo..
3837  * 
3838  */
3839  
3840 /**
3841  * @class Roo.bootstrap.TriggerField
3842  * @extends Roo.bootstrap.Input
3843  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
3844  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
3845  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
3846  * for which you can provide a custom implementation.  For example:
3847  * <pre><code>
3848 var trigger = new Roo.bootstrap.TriggerField();
3849 trigger.onTriggerClick = myTriggerFn;
3850 trigger.applyTo('my-field');
3851 </code></pre>
3852  *
3853  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
3854  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
3855  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
3856  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
3857  * @constructor
3858  * Create a new TriggerField.
3859  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
3860  * to the base TextField)
3861  */
3862 Roo.bootstrap.TriggerField = function(config){
3863     this.mimicing = false;
3864     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
3865 };
3866
3867 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
3868     /**
3869      * @cfg {String} triggerClass A CSS class to apply to the trigger
3870      */
3871      /**
3872      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
3873      */
3874     hideTrigger:false,
3875
3876     /** @cfg {Boolean} grow @hide */
3877     /** @cfg {Number} growMin @hide */
3878     /** @cfg {Number} growMax @hide */
3879
3880     /**
3881      * @hide 
3882      * @method
3883      */
3884     autoSize: Roo.emptyFn,
3885     // private
3886     monitorTab : true,
3887     // private
3888     deferHeight : true,
3889
3890     
3891     actionMode : 'wrap',
3892     
3893     
3894     
3895     getAutoCreate : function(){
3896        
3897         var parent = this.parent();
3898         
3899         var align = parent.labelAlign;
3900         
3901         var id = Roo.id();
3902         
3903         var cfg = {
3904             cls: 'form-group' //input-group
3905         };
3906         
3907         
3908         var input =  {
3909             tag: 'input',
3910             id : id,
3911             type : this.inputType,
3912             cls : 'form-control',
3913             autocomplete: 'off',
3914             placeholder : this.placeholder || '' 
3915             
3916         };
3917         if (this.name) {
3918             input.name = this.name;
3919         }
3920         if (this.size) {
3921             input.cls += ' input-' + this.size;
3922         }
3923         var inputblock = {
3924             cls: 'combobox-container input-group',
3925             cn: [
3926                 {
3927                     tag: 'input',
3928                     type : 'hidden',
3929                     cls: 'form-hidden-field'
3930                 },
3931                 input,
3932                 {
3933                     tag: 'ul',
3934                     cls : 'typeahead typeahead-long dropdown-menu',
3935                     style : 'display:none'
3936                 },
3937                 {
3938                     tag :'span',
3939                     cls : 'input-group-addon btn dropdown-toggle',
3940                     cn : [
3941                         {
3942                             tag: 'span',
3943                             cls: 'caret'
3944                         },
3945                         {
3946                             tag: 'span',
3947                             cls: 'combobox-clear',
3948                             cn  : [
3949                                 {
3950                                     tag : 'i',
3951                                     cls: 'icon-remove'
3952                                 }
3953                             ]
3954                         }
3955                     ]
3956                         
3957                 }
3958             ]
3959         };
3960         
3961         
3962         
3963         
3964         if (align ==='left' && this.fieldLabel.length) {
3965                 
3966             
3967             
3968                 Roo.log("left and has label");
3969                 cfg.cn = [
3970                     
3971                     {
3972                         tag: 'label',
3973                         'for' :  id,
3974                         cls : 'col-sm-2 control-label',
3975                         html : this.fieldLabel
3976                         
3977                     },
3978                     {
3979                         cls : "col-sm-10", 
3980                         cn: [
3981                             inputblock
3982                         ]
3983                     }
3984                     
3985                 ];
3986         } else if ( this.fieldLabel.length) {
3987                 Roo.log(" label");
3988                  cfg.cn = [
3989                    
3990                     {
3991                         tag: 'label',
3992                         //cls : 'input-group-addon',
3993                         html : this.fieldLabel
3994                         
3995                     },
3996                     
3997                     inputblock
3998                     
3999                 ];
4000
4001         } else {
4002             
4003                 Roo.log(" no label && no align");
4004                 cfg = inputblock
4005                      
4006                 
4007         }
4008          
4009         var settings=this;
4010         ['xs','sm','md','lg'].map(function(size){
4011             if (settings[size]) {
4012                 cfg.cls += ' col-' + size + '-' + settings[size];
4013             }
4014         });
4015         
4016         
4017         
4018         if (this.disabled) {
4019             input.disabled=true;
4020         }
4021         return cfg;
4022         
4023     },
4024     
4025     
4026     
4027     // private
4028     onResize : function(w, h){
4029         Roo.boostrap.TriggerField.superclass.onResize.apply(this, arguments);
4030         if(typeof w == 'number'){
4031             var x = w - this.trigger.getWidth();
4032             this.inputEl().setWidth(this.adjustWidth('input', x));
4033             this.trigger.setStyle('left', x+'px');
4034         }
4035     },
4036
4037     // private
4038     adjustSize : Roo.BoxComponent.prototype.adjustSize,
4039
4040     // private
4041     getResizeEl : function(){
4042         return this.inputEl();
4043     },
4044
4045     // private
4046     getPositionEl : function(){
4047         return this.inputEl();
4048     },
4049
4050     // private
4051     alignErrorIcon : function(){
4052         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
4053     },
4054
4055     // private
4056     initEvents : function(){
4057         
4058         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
4059         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
4060         
4061         this.trigger = this.el.select('span.dropdown-toggle',true).first();
4062         if(this.hideTrigger){
4063             this.trigger.setDisplayed(false);
4064         }
4065         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
4066         //this.trigger.addClassOnOver('x-form-trigger-over');
4067         //this.trigger.addClassOnClick('x-form-trigger-click');
4068         
4069         //if(!this.width){
4070         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
4071         //}
4072     },
4073
4074     // private
4075     initTrigger : function(){
4076        
4077     },
4078
4079     // private
4080     onDestroy : function(){
4081         if(this.trigger){
4082             this.trigger.removeAllListeners();
4083           //  this.trigger.remove();
4084         }
4085         //if(this.wrap){
4086         //    this.wrap.remove();
4087         //}
4088         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
4089     },
4090
4091     // private
4092     onFocus : function(){
4093         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
4094         /*
4095         if(!this.mimicing){
4096             this.wrap.addClass('x-trigger-wrap-focus');
4097             this.mimicing = true;
4098             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
4099             if(this.monitorTab){
4100                 this.el.on("keydown", this.checkTab, this);
4101             }
4102         }
4103         */
4104     },
4105
4106     // private
4107     checkTab : function(e){
4108         if(e.getKey() == e.TAB){
4109             this.triggerBlur();
4110         }
4111     },
4112
4113     // private
4114     onBlur : function(){
4115         // do nothing
4116     },
4117
4118     // private
4119     mimicBlur : function(e, t){
4120         /*
4121         if(!this.wrap.contains(t) && this.validateBlur()){
4122             this.triggerBlur();
4123         }
4124         */
4125     },
4126
4127     // private
4128     triggerBlur : function(){
4129         this.mimicing = false;
4130         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
4131         if(this.monitorTab){
4132             this.el.un("keydown", this.checkTab, this);
4133         }
4134         //this.wrap.removeClass('x-trigger-wrap-focus');
4135         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
4136     },
4137
4138     // private
4139     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
4140     validateBlur : function(e, t){
4141         return true;
4142     },
4143
4144     // private
4145     onDisable : function(){
4146         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
4147         //if(this.wrap){
4148         //    this.wrap.addClass('x-item-disabled');
4149         //}
4150     },
4151
4152     // private
4153     onEnable : function(){
4154         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
4155         //if(this.wrap){
4156         //    this.el.removeClass('x-item-disabled');
4157         //}
4158     },
4159
4160     // private
4161     onShow : function(){
4162         var ae = this.getActionEl();
4163         
4164         if(ae){
4165             ae.dom.style.display = '';
4166             ae.dom.style.visibility = 'visible';
4167         }
4168     },
4169
4170     // private
4171     
4172     onHide : function(){
4173         var ae = this.getActionEl();
4174         ae.dom.style.display = 'none';
4175     },
4176
4177     /**
4178      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
4179      * by an implementing function.
4180      * @method
4181      * @param {EventObject} e
4182      */
4183     onTriggerClick : Roo.emptyFn
4184 });
4185  /*
4186  * Based on:
4187  * Ext JS Library 1.1.1
4188  * Copyright(c) 2006-2007, Ext JS, LLC.
4189  *
4190  * Originally Released Under LGPL - original licence link has changed is not relivant.
4191  *
4192  * Fork - LGPL
4193  * <script type="text/javascript">
4194  */
4195
4196
4197 /**
4198  * @class Roo.data.SortTypes
4199  * @singleton
4200  * Defines the default sorting (casting?) comparison functions used when sorting data.
4201  */
4202 Roo.data.SortTypes = {
4203     /**
4204      * Default sort that does nothing
4205      * @param {Mixed} s The value being converted
4206      * @return {Mixed} The comparison value
4207      */
4208     none : function(s){
4209         return s;
4210     },
4211     
4212     /**
4213      * The regular expression used to strip tags
4214      * @type {RegExp}
4215      * @property
4216      */
4217     stripTagsRE : /<\/?[^>]+>/gi,
4218     
4219     /**
4220      * Strips all HTML tags to sort on text only
4221      * @param {Mixed} s The value being converted
4222      * @return {String} The comparison value
4223      */
4224     asText : function(s){
4225         return String(s).replace(this.stripTagsRE, "");
4226     },
4227     
4228     /**
4229      * Strips all HTML tags to sort on text only - Case insensitive
4230      * @param {Mixed} s The value being converted
4231      * @return {String} The comparison value
4232      */
4233     asUCText : function(s){
4234         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4235     },
4236     
4237     /**
4238      * Case insensitive string
4239      * @param {Mixed} s The value being converted
4240      * @return {String} The comparison value
4241      */
4242     asUCString : function(s) {
4243         return String(s).toUpperCase();
4244     },
4245     
4246     /**
4247      * Date sorting
4248      * @param {Mixed} s The value being converted
4249      * @return {Number} The comparison value
4250      */
4251     asDate : function(s) {
4252         if(!s){
4253             return 0;
4254         }
4255         if(s instanceof Date){
4256             return s.getTime();
4257         }
4258         return Date.parse(String(s));
4259     },
4260     
4261     /**
4262      * Float sorting
4263      * @param {Mixed} s The value being converted
4264      * @return {Float} The comparison value
4265      */
4266     asFloat : function(s) {
4267         var val = parseFloat(String(s).replace(/,/g, ""));
4268         if(isNaN(val)) val = 0;
4269         return val;
4270     },
4271     
4272     /**
4273      * Integer sorting
4274      * @param {Mixed} s The value being converted
4275      * @return {Number} The comparison value
4276      */
4277     asInt : function(s) {
4278         var val = parseInt(String(s).replace(/,/g, ""));
4279         if(isNaN(val)) val = 0;
4280         return val;
4281     }
4282 };/*
4283  * Based on:
4284  * Ext JS Library 1.1.1
4285  * Copyright(c) 2006-2007, Ext JS, LLC.
4286  *
4287  * Originally Released Under LGPL - original licence link has changed is not relivant.
4288  *
4289  * Fork - LGPL
4290  * <script type="text/javascript">
4291  */
4292
4293 /**
4294 * @class Roo.data.Record
4295  * Instances of this class encapsulate both record <em>definition</em> information, and record
4296  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4297  * to access Records cached in an {@link Roo.data.Store} object.<br>
4298  * <p>
4299  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4300  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4301  * objects.<br>
4302  * <p>
4303  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4304  * @constructor
4305  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4306  * {@link #create}. The parameters are the same.
4307  * @param {Array} data An associative Array of data values keyed by the field name.
4308  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4309  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4310  * not specified an integer id is generated.
4311  */
4312 Roo.data.Record = function(data, id){
4313     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4314     this.data = data;
4315 };
4316
4317 /**
4318  * Generate a constructor for a specific record layout.
4319  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4320  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4321  * Each field definition object may contain the following properties: <ul>
4322  * <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,
4323  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4324  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4325  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4326  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4327  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4328  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4329  * this may be omitted.</p></li>
4330  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4331  * <ul><li>auto (Default, implies no conversion)</li>
4332  * <li>string</li>
4333  * <li>int</li>
4334  * <li>float</li>
4335  * <li>boolean</li>
4336  * <li>date</li></ul></p></li>
4337  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4338  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4339  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4340  * by the Reader into an object that will be stored in the Record. It is passed the
4341  * following parameters:<ul>
4342  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4343  * </ul></p></li>
4344  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4345  * </ul>
4346  * <br>usage:<br><pre><code>
4347 var TopicRecord = Roo.data.Record.create(
4348     {name: 'title', mapping: 'topic_title'},
4349     {name: 'author', mapping: 'username'},
4350     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4351     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4352     {name: 'lastPoster', mapping: 'user2'},
4353     {name: 'excerpt', mapping: 'post_text'}
4354 );
4355
4356 var myNewRecord = new TopicRecord({
4357     title: 'Do my job please',
4358     author: 'noobie',
4359     totalPosts: 1,
4360     lastPost: new Date(),
4361     lastPoster: 'Animal',
4362     excerpt: 'No way dude!'
4363 });
4364 myStore.add(myNewRecord);
4365 </code></pre>
4366  * @method create
4367  * @static
4368  */
4369 Roo.data.Record.create = function(o){
4370     var f = function(){
4371         f.superclass.constructor.apply(this, arguments);
4372     };
4373     Roo.extend(f, Roo.data.Record);
4374     var p = f.prototype;
4375     p.fields = new Roo.util.MixedCollection(false, function(field){
4376         return field.name;
4377     });
4378     for(var i = 0, len = o.length; i < len; i++){
4379         p.fields.add(new Roo.data.Field(o[i]));
4380     }
4381     f.getField = function(name){
4382         return p.fields.get(name);  
4383     };
4384     return f;
4385 };
4386
4387 Roo.data.Record.AUTO_ID = 1000;
4388 Roo.data.Record.EDIT = 'edit';
4389 Roo.data.Record.REJECT = 'reject';
4390 Roo.data.Record.COMMIT = 'commit';
4391
4392 Roo.data.Record.prototype = {
4393     /**
4394      * Readonly flag - true if this record has been modified.
4395      * @type Boolean
4396      */
4397     dirty : false,
4398     editing : false,
4399     error: null,
4400     modified: null,
4401
4402     // private
4403     join : function(store){
4404         this.store = store;
4405     },
4406
4407     /**
4408      * Set the named field to the specified value.
4409      * @param {String} name The name of the field to set.
4410      * @param {Object} value The value to set the field to.
4411      */
4412     set : function(name, value){
4413         if(this.data[name] == value){
4414             return;
4415         }
4416         this.dirty = true;
4417         if(!this.modified){
4418             this.modified = {};
4419         }
4420         if(typeof this.modified[name] == 'undefined'){
4421             this.modified[name] = this.data[name];
4422         }
4423         this.data[name] = value;
4424         if(!this.editing && this.store){
4425             this.store.afterEdit(this);
4426         }       
4427     },
4428
4429     /**
4430      * Get the value of the named field.
4431      * @param {String} name The name of the field to get the value of.
4432      * @return {Object} The value of the field.
4433      */
4434     get : function(name){
4435         return this.data[name]; 
4436     },
4437
4438     // private
4439     beginEdit : function(){
4440         this.editing = true;
4441         this.modified = {}; 
4442     },
4443
4444     // private
4445     cancelEdit : function(){
4446         this.editing = false;
4447         delete this.modified;
4448     },
4449
4450     // private
4451     endEdit : function(){
4452         this.editing = false;
4453         if(this.dirty && this.store){
4454             this.store.afterEdit(this);
4455         }
4456     },
4457
4458     /**
4459      * Usually called by the {@link Roo.data.Store} which owns the Record.
4460      * Rejects all changes made to the Record since either creation, or the last commit operation.
4461      * Modified fields are reverted to their original values.
4462      * <p>
4463      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4464      * of reject operations.
4465      */
4466     reject : function(){
4467         var m = this.modified;
4468         for(var n in m){
4469             if(typeof m[n] != "function"){
4470                 this.data[n] = m[n];
4471             }
4472         }
4473         this.dirty = false;
4474         delete this.modified;
4475         this.editing = false;
4476         if(this.store){
4477             this.store.afterReject(this);
4478         }
4479     },
4480
4481     /**
4482      * Usually called by the {@link Roo.data.Store} which owns the Record.
4483      * Commits all changes made to the Record since either creation, or the last commit operation.
4484      * <p>
4485      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4486      * of commit operations.
4487      */
4488     commit : function(){
4489         this.dirty = false;
4490         delete this.modified;
4491         this.editing = false;
4492         if(this.store){
4493             this.store.afterCommit(this);
4494         }
4495     },
4496
4497     // private
4498     hasError : function(){
4499         return this.error != null;
4500     },
4501
4502     // private
4503     clearError : function(){
4504         this.error = null;
4505     },
4506
4507     /**
4508      * Creates a copy of this record.
4509      * @param {String} id (optional) A new record id if you don't want to use this record's id
4510      * @return {Record}
4511      */
4512     copy : function(newId) {
4513         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4514     }
4515 };/*
4516  * Based on:
4517  * Ext JS Library 1.1.1
4518  * Copyright(c) 2006-2007, Ext JS, LLC.
4519  *
4520  * Originally Released Under LGPL - original licence link has changed is not relivant.
4521  *
4522  * Fork - LGPL
4523  * <script type="text/javascript">
4524  */
4525
4526
4527
4528 /**
4529  * @class Roo.data.Store
4530  * @extends Roo.util.Observable
4531  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4532  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4533  * <p>
4534  * 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
4535  * has no knowledge of the format of the data returned by the Proxy.<br>
4536  * <p>
4537  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4538  * instances from the data object. These records are cached and made available through accessor functions.
4539  * @constructor
4540  * Creates a new Store.
4541  * @param {Object} config A config object containing the objects needed for the Store to access data,
4542  * and read the data into Records.
4543  */
4544 Roo.data.Store = function(config){
4545     this.data = new Roo.util.MixedCollection(false);
4546     this.data.getKey = function(o){
4547         return o.id;
4548     };
4549     this.baseParams = {};
4550     // private
4551     this.paramNames = {
4552         "start" : "start",
4553         "limit" : "limit",
4554         "sort" : "sort",
4555         "dir" : "dir",
4556         "multisort" : "_multisort"
4557     };
4558
4559     if(config && config.data){
4560         this.inlineData = config.data;
4561         delete config.data;
4562     }
4563
4564     Roo.apply(this, config);
4565     
4566     if(this.reader){ // reader passed
4567         this.reader = Roo.factory(this.reader, Roo.data);
4568         this.reader.xmodule = this.xmodule || false;
4569         if(!this.recordType){
4570             this.recordType = this.reader.recordType;
4571         }
4572         if(this.reader.onMetaChange){
4573             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4574         }
4575     }
4576
4577     if(this.recordType){
4578         this.fields = this.recordType.prototype.fields;
4579     }
4580     this.modified = [];
4581
4582     this.addEvents({
4583         /**
4584          * @event datachanged
4585          * Fires when the data cache has changed, and a widget which is using this Store
4586          * as a Record cache should refresh its view.
4587          * @param {Store} this
4588          */
4589         datachanged : true,
4590         /**
4591          * @event metachange
4592          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4593          * @param {Store} this
4594          * @param {Object} meta The JSON metadata
4595          */
4596         metachange : true,
4597         /**
4598          * @event add
4599          * Fires when Records have been added to the Store
4600          * @param {Store} this
4601          * @param {Roo.data.Record[]} records The array of Records added
4602          * @param {Number} index The index at which the record(s) were added
4603          */
4604         add : true,
4605         /**
4606          * @event remove
4607          * Fires when a Record has been removed from the Store
4608          * @param {Store} this
4609          * @param {Roo.data.Record} record The Record that was removed
4610          * @param {Number} index The index at which the record was removed
4611          */
4612         remove : true,
4613         /**
4614          * @event update
4615          * Fires when a Record has been updated
4616          * @param {Store} this
4617          * @param {Roo.data.Record} record The Record that was updated
4618          * @param {String} operation The update operation being performed.  Value may be one of:
4619          * <pre><code>
4620  Roo.data.Record.EDIT
4621  Roo.data.Record.REJECT
4622  Roo.data.Record.COMMIT
4623          * </code></pre>
4624          */
4625         update : true,
4626         /**
4627          * @event clear
4628          * Fires when the data cache has been cleared.
4629          * @param {Store} this
4630          */
4631         clear : true,
4632         /**
4633          * @event beforeload
4634          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4635          * the load action will be canceled.
4636          * @param {Store} this
4637          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4638          */
4639         beforeload : true,
4640         /**
4641          * @event beforeloadadd
4642          * Fires after a new set of Records has been loaded.
4643          * @param {Store} this
4644          * @param {Roo.data.Record[]} records The Records that were loaded
4645          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4646          */
4647         beforeloadadd : true,
4648         /**
4649          * @event load
4650          * Fires after a new set of Records has been loaded, before they are added to the store.
4651          * @param {Store} this
4652          * @param {Roo.data.Record[]} records The Records that were loaded
4653          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4654          * @params {Object} return from reader
4655          */
4656         load : true,
4657         /**
4658          * @event loadexception
4659          * Fires if an exception occurs in the Proxy during loading.
4660          * Called with the signature of the Proxy's "loadexception" event.
4661          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4662          * 
4663          * @param {Proxy} 
4664          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4665          * @param {Object} load options 
4666          * @param {Object} jsonData from your request (normally this contains the Exception)
4667          */
4668         loadexception : true
4669     });
4670     
4671     if(this.proxy){
4672         this.proxy = Roo.factory(this.proxy, Roo.data);
4673         this.proxy.xmodule = this.xmodule || false;
4674         this.relayEvents(this.proxy,  ["loadexception"]);
4675     }
4676     this.sortToggle = {};
4677     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4678
4679     Roo.data.Store.superclass.constructor.call(this);
4680
4681     if(this.inlineData){
4682         this.loadData(this.inlineData);
4683         delete this.inlineData;
4684     }
4685 };
4686
4687 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4688      /**
4689     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4690     * without a remote query - used by combo/forms at present.
4691     */
4692     
4693     /**
4694     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4695     */
4696     /**
4697     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4698     */
4699     /**
4700     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4701     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4702     */
4703     /**
4704     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4705     * on any HTTP request
4706     */
4707     /**
4708     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4709     */
4710     /**
4711     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4712     */
4713     multiSort: false,
4714     /**
4715     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4716     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4717     */
4718     remoteSort : false,
4719
4720     /**
4721     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4722      * loaded or when a record is removed. (defaults to false).
4723     */
4724     pruneModifiedRecords : false,
4725
4726     // private
4727     lastOptions : null,
4728
4729     /**
4730      * Add Records to the Store and fires the add event.
4731      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4732      */
4733     add : function(records){
4734         records = [].concat(records);
4735         for(var i = 0, len = records.length; i < len; i++){
4736             records[i].join(this);
4737         }
4738         var index = this.data.length;
4739         this.data.addAll(records);
4740         this.fireEvent("add", this, records, index);
4741     },
4742
4743     /**
4744      * Remove a Record from the Store and fires the remove event.
4745      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4746      */
4747     remove : function(record){
4748         var index = this.data.indexOf(record);
4749         this.data.removeAt(index);
4750         if(this.pruneModifiedRecords){
4751             this.modified.remove(record);
4752         }
4753         this.fireEvent("remove", this, record, index);
4754     },
4755
4756     /**
4757      * Remove all Records from the Store and fires the clear event.
4758      */
4759     removeAll : function(){
4760         this.data.clear();
4761         if(this.pruneModifiedRecords){
4762             this.modified = [];
4763         }
4764         this.fireEvent("clear", this);
4765     },
4766
4767     /**
4768      * Inserts Records to the Store at the given index and fires the add event.
4769      * @param {Number} index The start index at which to insert the passed Records.
4770      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4771      */
4772     insert : function(index, records){
4773         records = [].concat(records);
4774         for(var i = 0, len = records.length; i < len; i++){
4775             this.data.insert(index, records[i]);
4776             records[i].join(this);
4777         }
4778         this.fireEvent("add", this, records, index);
4779     },
4780
4781     /**
4782      * Get the index within the cache of the passed Record.
4783      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4784      * @return {Number} The index of the passed Record. Returns -1 if not found.
4785      */
4786     indexOf : function(record){
4787         return this.data.indexOf(record);
4788     },
4789
4790     /**
4791      * Get the index within the cache of the Record with the passed id.
4792      * @param {String} id The id of the Record to find.
4793      * @return {Number} The index of the Record. Returns -1 if not found.
4794      */
4795     indexOfId : function(id){
4796         return this.data.indexOfKey(id);
4797     },
4798
4799     /**
4800      * Get the Record with the specified id.
4801      * @param {String} id The id of the Record to find.
4802      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4803      */
4804     getById : function(id){
4805         return this.data.key(id);
4806     },
4807
4808     /**
4809      * Get the Record at the specified index.
4810      * @param {Number} index The index of the Record to find.
4811      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4812      */
4813     getAt : function(index){
4814         return this.data.itemAt(index);
4815     },
4816
4817     /**
4818      * Returns a range of Records between specified indices.
4819      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4820      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4821      * @return {Roo.data.Record[]} An array of Records
4822      */
4823     getRange : function(start, end){
4824         return this.data.getRange(start, end);
4825     },
4826
4827     // private
4828     storeOptions : function(o){
4829         o = Roo.apply({}, o);
4830         delete o.callback;
4831         delete o.scope;
4832         this.lastOptions = o;
4833     },
4834
4835     /**
4836      * Loads the Record cache from the configured Proxy using the configured Reader.
4837      * <p>
4838      * If using remote paging, then the first load call must specify the <em>start</em>
4839      * and <em>limit</em> properties in the options.params property to establish the initial
4840      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4841      * <p>
4842      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4843      * and this call will return before the new data has been loaded. Perform any post-processing
4844      * in a callback function, or in a "load" event handler.</strong>
4845      * <p>
4846      * @param {Object} options An object containing properties which control loading options:<ul>
4847      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4848      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4849      * passed the following arguments:<ul>
4850      * <li>r : Roo.data.Record[]</li>
4851      * <li>options: Options object from the load call</li>
4852      * <li>success: Boolean success indicator</li></ul></li>
4853      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4854      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4855      * </ul>
4856      */
4857     load : function(options){
4858         options = options || {};
4859         if(this.fireEvent("beforeload", this, options) !== false){
4860             this.storeOptions(options);
4861             var p = Roo.apply(options.params || {}, this.baseParams);
4862             // if meta was not loaded from remote source.. try requesting it.
4863             if (!this.reader.metaFromRemote) {
4864                 p._requestMeta = 1;
4865             }
4866             if(this.sortInfo && this.remoteSort){
4867                 var pn = this.paramNames;
4868                 p[pn["sort"]] = this.sortInfo.field;
4869                 p[pn["dir"]] = this.sortInfo.direction;
4870             }
4871             if (this.multiSort) {
4872                 var pn = this.paramNames;
4873                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
4874             }
4875             
4876             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4877         }
4878     },
4879
4880     /**
4881      * Reloads the Record cache from the configured Proxy using the configured Reader and
4882      * the options from the last load operation performed.
4883      * @param {Object} options (optional) An object containing properties which may override the options
4884      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
4885      * the most recently used options are reused).
4886      */
4887     reload : function(options){
4888         this.load(Roo.applyIf(options||{}, this.lastOptions));
4889     },
4890
4891     // private
4892     // Called as a callback by the Reader during a load operation.
4893     loadRecords : function(o, options, success){
4894         if(!o || success === false){
4895             if(success !== false){
4896                 this.fireEvent("load", this, [], options, o);
4897             }
4898             if(options.callback){
4899                 options.callback.call(options.scope || this, [], options, false);
4900             }
4901             return;
4902         }
4903         // if data returned failure - throw an exception.
4904         if (o.success === false) {
4905             // show a message if no listener is registered.
4906             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
4907                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
4908             }
4909             // loadmask wil be hooked into this..
4910             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
4911             return;
4912         }
4913         var r = o.records, t = o.totalRecords || r.length;
4914         
4915         this.fireEvent("beforeloadadd", this, r, options, o);
4916         
4917         if(!options || options.add !== true){
4918             if(this.pruneModifiedRecords){
4919                 this.modified = [];
4920             }
4921             for(var i = 0, len = r.length; i < len; i++){
4922                 r[i].join(this);
4923             }
4924             if(this.snapshot){
4925                 this.data = this.snapshot;
4926                 delete this.snapshot;
4927             }
4928             this.data.clear();
4929             this.data.addAll(r);
4930             this.totalLength = t;
4931             this.applySort();
4932             this.fireEvent("datachanged", this);
4933         }else{
4934             this.totalLength = Math.max(t, this.data.length+r.length);
4935             this.add(r);
4936         }
4937         this.fireEvent("load", this, r, options, o);
4938         if(options.callback){
4939             options.callback.call(options.scope || this, r, options, true);
4940         }
4941     },
4942
4943
4944     /**
4945      * Loads data from a passed data block. A Reader which understands the format of the data
4946      * must have been configured in the constructor.
4947      * @param {Object} data The data block from which to read the Records.  The format of the data expected
4948      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
4949      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
4950      */
4951     loadData : function(o, append){
4952         var r = this.reader.readRecords(o);
4953         this.loadRecords(r, {add: append}, true);
4954     },
4955
4956     /**
4957      * Gets the number of cached records.
4958      * <p>
4959      * <em>If using paging, this may not be the total size of the dataset. If the data object
4960      * used by the Reader contains the dataset size, then the getTotalCount() function returns
4961      * the data set size</em>
4962      */
4963     getCount : function(){
4964         return this.data.length || 0;
4965     },
4966
4967     /**
4968      * Gets the total number of records in the dataset as returned by the server.
4969      * <p>
4970      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
4971      * the dataset size</em>
4972      */
4973     getTotalCount : function(){
4974         return this.totalLength || 0;
4975     },
4976
4977     /**
4978      * Returns the sort state of the Store as an object with two properties:
4979      * <pre><code>
4980  field {String} The name of the field by which the Records are sorted
4981  direction {String} The sort order, "ASC" or "DESC"
4982      * </code></pre>
4983      */
4984     getSortState : function(){
4985         return this.sortInfo;
4986     },
4987
4988     // private
4989     applySort : function(){
4990         if(this.sortInfo && !this.remoteSort){
4991             var s = this.sortInfo, f = s.field;
4992             var st = this.fields.get(f).sortType;
4993             var fn = function(r1, r2){
4994                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
4995                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
4996             };
4997             this.data.sort(s.direction, fn);
4998             if(this.snapshot && this.snapshot != this.data){
4999                 this.snapshot.sort(s.direction, fn);
5000             }
5001         }
5002     },
5003
5004     /**
5005      * Sets the default sort column and order to be used by the next load operation.
5006      * @param {String} fieldName The name of the field to sort by.
5007      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5008      */
5009     setDefaultSort : function(field, dir){
5010         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5011     },
5012
5013     /**
5014      * Sort the Records.
5015      * If remote sorting is used, the sort is performed on the server, and the cache is
5016      * reloaded. If local sorting is used, the cache is sorted internally.
5017      * @param {String} fieldName The name of the field to sort by.
5018      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5019      */
5020     sort : function(fieldName, dir){
5021         var f = this.fields.get(fieldName);
5022         if(!dir){
5023             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5024             
5025             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5026                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5027             }else{
5028                 dir = f.sortDir;
5029             }
5030         }
5031         this.sortToggle[f.name] = dir;
5032         this.sortInfo = {field: f.name, direction: dir};
5033         if(!this.remoteSort){
5034             this.applySort();
5035             this.fireEvent("datachanged", this);
5036         }else{
5037             this.load(this.lastOptions);
5038         }
5039     },
5040
5041     /**
5042      * Calls the specified function for each of the Records in the cache.
5043      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5044      * Returning <em>false</em> aborts and exits the iteration.
5045      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5046      */
5047     each : function(fn, scope){
5048         this.data.each(fn, scope);
5049     },
5050
5051     /**
5052      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5053      * (e.g., during paging).
5054      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5055      */
5056     getModifiedRecords : function(){
5057         return this.modified;
5058     },
5059
5060     // private
5061     createFilterFn : function(property, value, anyMatch){
5062         if(!value.exec){ // not a regex
5063             value = String(value);
5064             if(value.length == 0){
5065                 return false;
5066             }
5067             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5068         }
5069         return function(r){
5070             return value.test(r.data[property]);
5071         };
5072     },
5073
5074     /**
5075      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5076      * @param {String} property A field on your records
5077      * @param {Number} start The record index to start at (defaults to 0)
5078      * @param {Number} end The last record index to include (defaults to length - 1)
5079      * @return {Number} The sum
5080      */
5081     sum : function(property, start, end){
5082         var rs = this.data.items, v = 0;
5083         start = start || 0;
5084         end = (end || end === 0) ? end : rs.length-1;
5085
5086         for(var i = start; i <= end; i++){
5087             v += (rs[i].data[property] || 0);
5088         }
5089         return v;
5090     },
5091
5092     /**
5093      * Filter the records by a specified property.
5094      * @param {String} field A field on your records
5095      * @param {String/RegExp} value Either a string that the field
5096      * should start with or a RegExp to test against the field
5097      * @param {Boolean} anyMatch True to match any part not just the beginning
5098      */
5099     filter : function(property, value, anyMatch){
5100         var fn = this.createFilterFn(property, value, anyMatch);
5101         return fn ? this.filterBy(fn) : this.clearFilter();
5102     },
5103
5104     /**
5105      * Filter by a function. The specified function will be called with each
5106      * record in this data source. If the function returns true the record is included,
5107      * otherwise it is filtered.
5108      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5109      * @param {Object} scope (optional) The scope of the function (defaults to this)
5110      */
5111     filterBy : function(fn, scope){
5112         this.snapshot = this.snapshot || this.data;
5113         this.data = this.queryBy(fn, scope||this);
5114         this.fireEvent("datachanged", this);
5115     },
5116
5117     /**
5118      * Query the records by a specified property.
5119      * @param {String} field A field on your records
5120      * @param {String/RegExp} value Either a string that the field
5121      * should start with or a RegExp to test against the field
5122      * @param {Boolean} anyMatch True to match any part not just the beginning
5123      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5124      */
5125     query : function(property, value, anyMatch){
5126         var fn = this.createFilterFn(property, value, anyMatch);
5127         return fn ? this.queryBy(fn) : this.data.clone();
5128     },
5129
5130     /**
5131      * Query by a function. The specified function will be called with each
5132      * record in this data source. If the function returns true the record is included
5133      * in the results.
5134      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5135      * @param {Object} scope (optional) The scope of the function (defaults to this)
5136       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5137      **/
5138     queryBy : function(fn, scope){
5139         var data = this.snapshot || this.data;
5140         return data.filterBy(fn, scope||this);
5141     },
5142
5143     /**
5144      * Collects unique values for a particular dataIndex from this store.
5145      * @param {String} dataIndex The property to collect
5146      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5147      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5148      * @return {Array} An array of the unique values
5149      **/
5150     collect : function(dataIndex, allowNull, bypassFilter){
5151         var d = (bypassFilter === true && this.snapshot) ?
5152                 this.snapshot.items : this.data.items;
5153         var v, sv, r = [], l = {};
5154         for(var i = 0, len = d.length; i < len; i++){
5155             v = d[i].data[dataIndex];
5156             sv = String(v);
5157             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5158                 l[sv] = true;
5159                 r[r.length] = v;
5160             }
5161         }
5162         return r;
5163     },
5164
5165     /**
5166      * Revert to a view of the Record cache with no filtering applied.
5167      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5168      */
5169     clearFilter : function(suppressEvent){
5170         if(this.snapshot && this.snapshot != this.data){
5171             this.data = this.snapshot;
5172             delete this.snapshot;
5173             if(suppressEvent !== true){
5174                 this.fireEvent("datachanged", this);
5175             }
5176         }
5177     },
5178
5179     // private
5180     afterEdit : function(record){
5181         if(this.modified.indexOf(record) == -1){
5182             this.modified.push(record);
5183         }
5184         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5185     },
5186     
5187     // private
5188     afterReject : function(record){
5189         this.modified.remove(record);
5190         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5191     },
5192
5193     // private
5194     afterCommit : function(record){
5195         this.modified.remove(record);
5196         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5197     },
5198
5199     /**
5200      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5201      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5202      */
5203     commitChanges : function(){
5204         var m = this.modified.slice(0);
5205         this.modified = [];
5206         for(var i = 0, len = m.length; i < len; i++){
5207             m[i].commit();
5208         }
5209     },
5210
5211     /**
5212      * Cancel outstanding changes on all changed records.
5213      */
5214     rejectChanges : function(){
5215         var m = this.modified.slice(0);
5216         this.modified = [];
5217         for(var i = 0, len = m.length; i < len; i++){
5218             m[i].reject();
5219         }
5220     },
5221
5222     onMetaChange : function(meta, rtype, o){
5223         this.recordType = rtype;
5224         this.fields = rtype.prototype.fields;
5225         delete this.snapshot;
5226         this.sortInfo = meta.sortInfo || this.sortInfo;
5227         this.modified = [];
5228         this.fireEvent('metachange', this, this.reader.meta);
5229     }
5230 });/*
5231  * Based on:
5232  * Ext JS Library 1.1.1
5233  * Copyright(c) 2006-2007, Ext JS, LLC.
5234  *
5235  * Originally Released Under LGPL - original licence link has changed is not relivant.
5236  *
5237  * Fork - LGPL
5238  * <script type="text/javascript">
5239  */
5240
5241 /**
5242  * @class Roo.data.SimpleStore
5243  * @extends Roo.data.Store
5244  * Small helper class to make creating Stores from Array data easier.
5245  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5246  * @cfg {Array} fields An array of field definition objects, or field name strings.
5247  * @cfg {Array} data The multi-dimensional array of data
5248  * @constructor
5249  * @param {Object} config
5250  */
5251 Roo.data.SimpleStore = function(config){
5252     Roo.data.SimpleStore.superclass.constructor.call(this, {
5253         isLocal : true,
5254         reader: new Roo.data.ArrayReader({
5255                 id: config.id
5256             },
5257             Roo.data.Record.create(config.fields)
5258         ),
5259         proxy : new Roo.data.MemoryProxy(config.data)
5260     });
5261     this.load();
5262 };
5263 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5264  * Based on:
5265  * Ext JS Library 1.1.1
5266  * Copyright(c) 2006-2007, Ext JS, LLC.
5267  *
5268  * Originally Released Under LGPL - original licence link has changed is not relivant.
5269  *
5270  * Fork - LGPL
5271  * <script type="text/javascript">
5272  */
5273
5274 /**
5275 /**
5276  * @extends Roo.data.Store
5277  * @class Roo.data.JsonStore
5278  * Small helper class to make creating Stores for JSON data easier. <br/>
5279 <pre><code>
5280 var store = new Roo.data.JsonStore({
5281     url: 'get-images.php',
5282     root: 'images',
5283     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5284 });
5285 </code></pre>
5286  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5287  * JsonReader and HttpProxy (unless inline data is provided).</b>
5288  * @cfg {Array} fields An array of field definition objects, or field name strings.
5289  * @constructor
5290  * @param {Object} config
5291  */
5292 Roo.data.JsonStore = function(c){
5293     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5294         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5295         reader: new Roo.data.JsonReader(c, c.fields)
5296     }));
5297 };
5298 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5299  * Based on:
5300  * Ext JS Library 1.1.1
5301  * Copyright(c) 2006-2007, Ext JS, LLC.
5302  *
5303  * Originally Released Under LGPL - original licence link has changed is not relivant.
5304  *
5305  * Fork - LGPL
5306  * <script type="text/javascript">
5307  */
5308
5309  
5310 Roo.data.Field = function(config){
5311     if(typeof config == "string"){
5312         config = {name: config};
5313     }
5314     Roo.apply(this, config);
5315     
5316     if(!this.type){
5317         this.type = "auto";
5318     }
5319     
5320     var st = Roo.data.SortTypes;
5321     // named sortTypes are supported, here we look them up
5322     if(typeof this.sortType == "string"){
5323         this.sortType = st[this.sortType];
5324     }
5325     
5326     // set default sortType for strings and dates
5327     if(!this.sortType){
5328         switch(this.type){
5329             case "string":
5330                 this.sortType = st.asUCString;
5331                 break;
5332             case "date":
5333                 this.sortType = st.asDate;
5334                 break;
5335             default:
5336                 this.sortType = st.none;
5337         }
5338     }
5339
5340     // define once
5341     var stripRe = /[\$,%]/g;
5342
5343     // prebuilt conversion function for this field, instead of
5344     // switching every time we're reading a value
5345     if(!this.convert){
5346         var cv, dateFormat = this.dateFormat;
5347         switch(this.type){
5348             case "":
5349             case "auto":
5350             case undefined:
5351                 cv = function(v){ return v; };
5352                 break;
5353             case "string":
5354                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5355                 break;
5356             case "int":
5357                 cv = function(v){
5358                     return v !== undefined && v !== null && v !== '' ?
5359                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5360                     };
5361                 break;
5362             case "float":
5363                 cv = function(v){
5364                     return v !== undefined && v !== null && v !== '' ?
5365                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5366                     };
5367                 break;
5368             case "bool":
5369             case "boolean":
5370                 cv = function(v){ return v === true || v === "true" || v == 1; };
5371                 break;
5372             case "date":
5373                 cv = function(v){
5374                     if(!v){
5375                         return '';
5376                     }
5377                     if(v instanceof Date){
5378                         return v;
5379                     }
5380                     if(dateFormat){
5381                         if(dateFormat == "timestamp"){
5382                             return new Date(v*1000);
5383                         }
5384                         return Date.parseDate(v, dateFormat);
5385                     }
5386                     var parsed = Date.parse(v);
5387                     return parsed ? new Date(parsed) : null;
5388                 };
5389              break;
5390             
5391         }
5392         this.convert = cv;
5393     }
5394 };
5395
5396 Roo.data.Field.prototype = {
5397     dateFormat: null,
5398     defaultValue: "",
5399     mapping: null,
5400     sortType : null,
5401     sortDir : "ASC"
5402 };/*
5403  * Based on:
5404  * Ext JS Library 1.1.1
5405  * Copyright(c) 2006-2007, Ext JS, LLC.
5406  *
5407  * Originally Released Under LGPL - original licence link has changed is not relivant.
5408  *
5409  * Fork - LGPL
5410  * <script type="text/javascript">
5411  */
5412  
5413 // Base class for reading structured data from a data source.  This class is intended to be
5414 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5415
5416 /**
5417  * @class Roo.data.DataReader
5418  * Base class for reading structured data from a data source.  This class is intended to be
5419  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5420  */
5421
5422 Roo.data.DataReader = function(meta, recordType){
5423     
5424     this.meta = meta;
5425     
5426     this.recordType = recordType instanceof Array ? 
5427         Roo.data.Record.create(recordType) : recordType;
5428 };
5429
5430 Roo.data.DataReader.prototype = {
5431      /**
5432      * Create an empty record
5433      * @param {Object} data (optional) - overlay some values
5434      * @return {Roo.data.Record} record created.
5435      */
5436     newRow :  function(d) {
5437         var da =  {};
5438         this.recordType.prototype.fields.each(function(c) {
5439             switch( c.type) {
5440                 case 'int' : da[c.name] = 0; break;
5441                 case 'date' : da[c.name] = new Date(); break;
5442                 case 'float' : da[c.name] = 0.0; break;
5443                 case 'boolean' : da[c.name] = false; break;
5444                 default : da[c.name] = ""; break;
5445             }
5446             
5447         });
5448         return new this.recordType(Roo.apply(da, d));
5449     }
5450     
5451 };/*
5452  * Based on:
5453  * Ext JS Library 1.1.1
5454  * Copyright(c) 2006-2007, Ext JS, LLC.
5455  *
5456  * Originally Released Under LGPL - original licence link has changed is not relivant.
5457  *
5458  * Fork - LGPL
5459  * <script type="text/javascript">
5460  */
5461
5462 /**
5463  * @class Roo.data.DataProxy
5464  * @extends Roo.data.Observable
5465  * This class is an abstract base class for implementations which provide retrieval of
5466  * unformatted data objects.<br>
5467  * <p>
5468  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5469  * (of the appropriate type which knows how to parse the data object) to provide a block of
5470  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5471  * <p>
5472  * Custom implementations must implement the load method as described in
5473  * {@link Roo.data.HttpProxy#load}.
5474  */
5475 Roo.data.DataProxy = function(){
5476     this.addEvents({
5477         /**
5478          * @event beforeload
5479          * Fires before a network request is made to retrieve a data object.
5480          * @param {Object} This DataProxy object.
5481          * @param {Object} params The params parameter to the load function.
5482          */
5483         beforeload : true,
5484         /**
5485          * @event load
5486          * Fires before the load method's callback is called.
5487          * @param {Object} This DataProxy object.
5488          * @param {Object} o The data object.
5489          * @param {Object} arg The callback argument object passed to the load function.
5490          */
5491         load : true,
5492         /**
5493          * @event loadexception
5494          * Fires if an Exception occurs during data retrieval.
5495          * @param {Object} This DataProxy object.
5496          * @param {Object} o The data object.
5497          * @param {Object} arg The callback argument object passed to the load function.
5498          * @param {Object} e The Exception.
5499          */
5500         loadexception : true
5501     });
5502     Roo.data.DataProxy.superclass.constructor.call(this);
5503 };
5504
5505 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5506
5507     /**
5508      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5509      */
5510 /*
5511  * Based on:
5512  * Ext JS Library 1.1.1
5513  * Copyright(c) 2006-2007, Ext JS, LLC.
5514  *
5515  * Originally Released Under LGPL - original licence link has changed is not relivant.
5516  *
5517  * Fork - LGPL
5518  * <script type="text/javascript">
5519  */
5520 /**
5521  * @class Roo.data.MemoryProxy
5522  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5523  * to the Reader when its load method is called.
5524  * @constructor
5525  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5526  */
5527 Roo.data.MemoryProxy = function(data){
5528     if (data.data) {
5529         data = data.data;
5530     }
5531     Roo.data.MemoryProxy.superclass.constructor.call(this);
5532     this.data = data;
5533 };
5534
5535 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5536     /**
5537      * Load data from the requested source (in this case an in-memory
5538      * data object passed to the constructor), read the data object into
5539      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5540      * process that block using the passed callback.
5541      * @param {Object} params This parameter is not used by the MemoryProxy class.
5542      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5543      * object into a block of Roo.data.Records.
5544      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5545      * The function must be passed <ul>
5546      * <li>The Record block object</li>
5547      * <li>The "arg" argument from the load function</li>
5548      * <li>A boolean success indicator</li>
5549      * </ul>
5550      * @param {Object} scope The scope in which to call the callback
5551      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5552      */
5553     load : function(params, reader, callback, scope, arg){
5554         params = params || {};
5555         var result;
5556         try {
5557             result = reader.readRecords(this.data);
5558         }catch(e){
5559             this.fireEvent("loadexception", this, arg, null, e);
5560             callback.call(scope, null, arg, false);
5561             return;
5562         }
5563         callback.call(scope, result, arg, true);
5564     },
5565     
5566     // private
5567     update : function(params, records){
5568         
5569     }
5570 });/*
5571  * Based on:
5572  * Ext JS Library 1.1.1
5573  * Copyright(c) 2006-2007, Ext JS, LLC.
5574  *
5575  * Originally Released Under LGPL - original licence link has changed is not relivant.
5576  *
5577  * Fork - LGPL
5578  * <script type="text/javascript">
5579  */
5580 /**
5581  * @class Roo.data.HttpProxy
5582  * @extends Roo.data.DataProxy
5583  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5584  * configured to reference a certain URL.<br><br>
5585  * <p>
5586  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5587  * from which the running page was served.<br><br>
5588  * <p>
5589  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5590  * <p>
5591  * Be aware that to enable the browser to parse an XML document, the server must set
5592  * the Content-Type header in the HTTP response to "text/xml".
5593  * @constructor
5594  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5595  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5596  * will be used to make the request.
5597  */
5598 Roo.data.HttpProxy = function(conn){
5599     Roo.data.HttpProxy.superclass.constructor.call(this);
5600     // is conn a conn config or a real conn?
5601     this.conn = conn;
5602     this.useAjax = !conn || !conn.events;
5603   
5604 };
5605
5606 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5607     // thse are take from connection...
5608     
5609     /**
5610      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5611      */
5612     /**
5613      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5614      * extra parameters to each request made by this object. (defaults to undefined)
5615      */
5616     /**
5617      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5618      *  to each request made by this object. (defaults to undefined)
5619      */
5620     /**
5621      * @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)
5622      */
5623     /**
5624      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5625      */
5626      /**
5627      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5628      * @type Boolean
5629      */
5630   
5631
5632     /**
5633      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5634      * @type Boolean
5635      */
5636     /**
5637      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5638      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5639      * a finer-grained basis than the DataProxy events.
5640      */
5641     getConnection : function(){
5642         return this.useAjax ? Roo.Ajax : this.conn;
5643     },
5644
5645     /**
5646      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5647      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5648      * process that block using the passed callback.
5649      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5650      * for the request to the remote server.
5651      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5652      * object into a block of Roo.data.Records.
5653      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5654      * The function must be passed <ul>
5655      * <li>The Record block object</li>
5656      * <li>The "arg" argument from the load function</li>
5657      * <li>A boolean success indicator</li>
5658      * </ul>
5659      * @param {Object} scope The scope in which to call the callback
5660      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5661      */
5662     load : function(params, reader, callback, scope, arg){
5663         if(this.fireEvent("beforeload", this, params) !== false){
5664             var  o = {
5665                 params : params || {},
5666                 request: {
5667                     callback : callback,
5668                     scope : scope,
5669                     arg : arg
5670                 },
5671                 reader: reader,
5672                 callback : this.loadResponse,
5673                 scope: this
5674             };
5675             if(this.useAjax){
5676                 Roo.applyIf(o, this.conn);
5677                 if(this.activeRequest){
5678                     Roo.Ajax.abort(this.activeRequest);
5679                 }
5680                 this.activeRequest = Roo.Ajax.request(o);
5681             }else{
5682                 this.conn.request(o);
5683             }
5684         }else{
5685             callback.call(scope||this, null, arg, false);
5686         }
5687     },
5688
5689     // private
5690     loadResponse : function(o, success, response){
5691         delete this.activeRequest;
5692         if(!success){
5693             this.fireEvent("loadexception", this, o, response);
5694             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5695             return;
5696         }
5697         var result;
5698         try {
5699             result = o.reader.read(response);
5700         }catch(e){
5701             this.fireEvent("loadexception", this, o, response, e);
5702             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5703             return;
5704         }
5705         
5706         this.fireEvent("load", this, o, o.request.arg);
5707         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5708     },
5709
5710     // private
5711     update : function(dataSet){
5712
5713     },
5714
5715     // private
5716     updateResponse : function(dataSet){
5717
5718     }
5719 });/*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731  * @class Roo.data.ScriptTagProxy
5732  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5733  * other than the originating domain of the running page.<br><br>
5734  * <p>
5735  * <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
5736  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5737  * <p>
5738  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5739  * source code that is used as the source inside a &lt;script> tag.<br><br>
5740  * <p>
5741  * In order for the browser to process the returned data, the server must wrap the data object
5742  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5743  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5744  * depending on whether the callback name was passed:
5745  * <p>
5746  * <pre><code>
5747 boolean scriptTag = false;
5748 String cb = request.getParameter("callback");
5749 if (cb != null) {
5750     scriptTag = true;
5751     response.setContentType("text/javascript");
5752 } else {
5753     response.setContentType("application/x-json");
5754 }
5755 Writer out = response.getWriter();
5756 if (scriptTag) {
5757     out.write(cb + "(");
5758 }
5759 out.print(dataBlock.toJsonString());
5760 if (scriptTag) {
5761     out.write(");");
5762 }
5763 </pre></code>
5764  *
5765  * @constructor
5766  * @param {Object} config A configuration object.
5767  */
5768 Roo.data.ScriptTagProxy = function(config){
5769     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5770     Roo.apply(this, config);
5771     this.head = document.getElementsByTagName("head")[0];
5772 };
5773
5774 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5775
5776 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5777     /**
5778      * @cfg {String} url The URL from which to request the data object.
5779      */
5780     /**
5781      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5782      */
5783     timeout : 30000,
5784     /**
5785      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5786      * the server the name of the callback function set up by the load call to process the returned data object.
5787      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5788      * javascript output which calls this named function passing the data object as its only parameter.
5789      */
5790     callbackParam : "callback",
5791     /**
5792      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5793      * name to the request.
5794      */
5795     nocache : true,
5796
5797     /**
5798      * Load data from the configured URL, read the data object into
5799      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5800      * process that block using the passed callback.
5801      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5802      * for the request to the remote server.
5803      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5804      * object into a block of Roo.data.Records.
5805      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5806      * The function must be passed <ul>
5807      * <li>The Record block object</li>
5808      * <li>The "arg" argument from the load function</li>
5809      * <li>A boolean success indicator</li>
5810      * </ul>
5811      * @param {Object} scope The scope in which to call the callback
5812      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5813      */
5814     load : function(params, reader, callback, scope, arg){
5815         if(this.fireEvent("beforeload", this, params) !== false){
5816
5817             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5818
5819             var url = this.url;
5820             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5821             if(this.nocache){
5822                 url += "&_dc=" + (new Date().getTime());
5823             }
5824             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5825             var trans = {
5826                 id : transId,
5827                 cb : "stcCallback"+transId,
5828                 scriptId : "stcScript"+transId,
5829                 params : params,
5830                 arg : arg,
5831                 url : url,
5832                 callback : callback,
5833                 scope : scope,
5834                 reader : reader
5835             };
5836             var conn = this;
5837
5838             window[trans.cb] = function(o){
5839                 conn.handleResponse(o, trans);
5840             };
5841
5842             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5843
5844             if(this.autoAbort !== false){
5845                 this.abort();
5846             }
5847
5848             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5849
5850             var script = document.createElement("script");
5851             script.setAttribute("src", url);
5852             script.setAttribute("type", "text/javascript");
5853             script.setAttribute("id", trans.scriptId);
5854             this.head.appendChild(script);
5855
5856             this.trans = trans;
5857         }else{
5858             callback.call(scope||this, null, arg, false);
5859         }
5860     },
5861
5862     // private
5863     isLoading : function(){
5864         return this.trans ? true : false;
5865     },
5866
5867     /**
5868      * Abort the current server request.
5869      */
5870     abort : function(){
5871         if(this.isLoading()){
5872             this.destroyTrans(this.trans);
5873         }
5874     },
5875
5876     // private
5877     destroyTrans : function(trans, isLoaded){
5878         this.head.removeChild(document.getElementById(trans.scriptId));
5879         clearTimeout(trans.timeoutId);
5880         if(isLoaded){
5881             window[trans.cb] = undefined;
5882             try{
5883                 delete window[trans.cb];
5884             }catch(e){}
5885         }else{
5886             // if hasn't been loaded, wait for load to remove it to prevent script error
5887             window[trans.cb] = function(){
5888                 window[trans.cb] = undefined;
5889                 try{
5890                     delete window[trans.cb];
5891                 }catch(e){}
5892             };
5893         }
5894     },
5895
5896     // private
5897     handleResponse : function(o, trans){
5898         this.trans = false;
5899         this.destroyTrans(trans, true);
5900         var result;
5901         try {
5902             result = trans.reader.readRecords(o);
5903         }catch(e){
5904             this.fireEvent("loadexception", this, o, trans.arg, e);
5905             trans.callback.call(trans.scope||window, null, trans.arg, false);
5906             return;
5907         }
5908         this.fireEvent("load", this, o, trans.arg);
5909         trans.callback.call(trans.scope||window, result, trans.arg, true);
5910     },
5911
5912     // private
5913     handleFailure : function(trans){
5914         this.trans = false;
5915         this.destroyTrans(trans, false);
5916         this.fireEvent("loadexception", this, null, trans.arg);
5917         trans.callback.call(trans.scope||window, null, trans.arg, false);
5918     }
5919 });/*
5920  * Based on:
5921  * Ext JS Library 1.1.1
5922  * Copyright(c) 2006-2007, Ext JS, LLC.
5923  *
5924  * Originally Released Under LGPL - original licence link has changed is not relivant.
5925  *
5926  * Fork - LGPL
5927  * <script type="text/javascript">
5928  */
5929
5930 /**
5931  * @class Roo.data.JsonReader
5932  * @extends Roo.data.DataReader
5933  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
5934  * based on mappings in a provided Roo.data.Record constructor.
5935  * 
5936  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
5937  * in the reply previously. 
5938  * 
5939  * <p>
5940  * Example code:
5941  * <pre><code>
5942 var RecordDef = Roo.data.Record.create([
5943     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
5944     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
5945 ]);
5946 var myReader = new Roo.data.JsonReader({
5947     totalProperty: "results",    // The property which contains the total dataset size (optional)
5948     root: "rows",                // The property which contains an Array of row objects
5949     id: "id"                     // The property within each row object that provides an ID for the record (optional)
5950 }, RecordDef);
5951 </code></pre>
5952  * <p>
5953  * This would consume a JSON file like this:
5954  * <pre><code>
5955 { 'results': 2, 'rows': [
5956     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
5957     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
5958 }
5959 </code></pre>
5960  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
5961  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
5962  * paged from the remote server.
5963  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
5964  * @cfg {String} root name of the property which contains the Array of row objects.
5965  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
5966  * @constructor
5967  * Create a new JsonReader
5968  * @param {Object} meta Metadata configuration options
5969  * @param {Object} recordType Either an Array of field definition objects,
5970  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
5971  */
5972 Roo.data.JsonReader = function(meta, recordType){
5973     
5974     meta = meta || {};
5975     // set some defaults:
5976     Roo.applyIf(meta, {
5977         totalProperty: 'total',
5978         successProperty : 'success',
5979         root : 'data',
5980         id : 'id'
5981     });
5982     
5983     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
5984 };
5985 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
5986     
5987     /**
5988      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
5989      * Used by Store query builder to append _requestMeta to params.
5990      * 
5991      */
5992     metaFromRemote : false,
5993     /**
5994      * This method is only used by a DataProxy which has retrieved data from a remote server.
5995      * @param {Object} response The XHR object which contains the JSON data in its responseText.
5996      * @return {Object} data A data block which is used by an Roo.data.Store object as
5997      * a cache of Roo.data.Records.
5998      */
5999     read : function(response){
6000         var json = response.responseText;
6001        
6002         var o = /* eval:var:o */ eval("("+json+")");
6003         if(!o) {
6004             throw {message: "JsonReader.read: Json object not found"};
6005         }
6006         
6007         if(o.metaData){
6008             
6009             delete this.ef;
6010             this.metaFromRemote = true;
6011             this.meta = o.metaData;
6012             this.recordType = Roo.data.Record.create(o.metaData.fields);
6013             this.onMetaChange(this.meta, this.recordType, o);
6014         }
6015         return this.readRecords(o);
6016     },
6017
6018     // private function a store will implement
6019     onMetaChange : function(meta, recordType, o){
6020
6021     },
6022
6023     /**
6024          * @ignore
6025          */
6026     simpleAccess: function(obj, subsc) {
6027         return obj[subsc];
6028     },
6029
6030         /**
6031          * @ignore
6032          */
6033     getJsonAccessor: function(){
6034         var re = /[\[\.]/;
6035         return function(expr) {
6036             try {
6037                 return(re.test(expr))
6038                     ? new Function("obj", "return obj." + expr)
6039                     : function(obj){
6040                         return obj[expr];
6041                     };
6042             } catch(e){}
6043             return Roo.emptyFn;
6044         };
6045     }(),
6046
6047     /**
6048      * Create a data block containing Roo.data.Records from an XML document.
6049      * @param {Object} o An object which contains an Array of row objects in the property specified
6050      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6051      * which contains the total size of the dataset.
6052      * @return {Object} data A data block which is used by an Roo.data.Store object as
6053      * a cache of Roo.data.Records.
6054      */
6055     readRecords : function(o){
6056         /**
6057          * After any data loads, the raw JSON data is available for further custom processing.
6058          * @type Object
6059          */
6060         this.o = o;
6061         var s = this.meta, Record = this.recordType,
6062             f = Record.prototype.fields, fi = f.items, fl = f.length;
6063
6064 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6065         if (!this.ef) {
6066             if(s.totalProperty) {
6067                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6068                 }
6069                 if(s.successProperty) {
6070                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6071                 }
6072                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6073                 if (s.id) {
6074                         var g = this.getJsonAccessor(s.id);
6075                         this.getId = function(rec) {
6076                                 var r = g(rec);
6077                                 return (r === undefined || r === "") ? null : r;
6078                         };
6079                 } else {
6080                         this.getId = function(){return null;};
6081                 }
6082             this.ef = [];
6083             for(var jj = 0; jj < fl; jj++){
6084                 f = fi[jj];
6085                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6086                 this.ef[jj] = this.getJsonAccessor(map);
6087             }
6088         }
6089
6090         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6091         if(s.totalProperty){
6092             var vt = parseInt(this.getTotal(o), 10);
6093             if(!isNaN(vt)){
6094                 totalRecords = vt;
6095             }
6096         }
6097         if(s.successProperty){
6098             var vs = this.getSuccess(o);
6099             if(vs === false || vs === 'false'){
6100                 success = false;
6101             }
6102         }
6103         var records = [];
6104             for(var i = 0; i < c; i++){
6105                     var n = root[i];
6106                 var values = {};
6107                 var id = this.getId(n);
6108                 for(var j = 0; j < fl; j++){
6109                     f = fi[j];
6110                 var v = this.ef[j](n);
6111                 if (!f.convert) {
6112                     Roo.log('missing convert for ' + f.name);
6113                     Roo.log(f);
6114                     continue;
6115                 }
6116                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6117                 }
6118                 var record = new Record(values, id);
6119                 record.json = n;
6120                 records[i] = record;
6121             }
6122             return {
6123             raw : o,
6124                 success : success,
6125                 records : records,
6126                 totalRecords : totalRecords
6127             };
6128     }
6129 });/*
6130  * Based on:
6131  * Ext JS Library 1.1.1
6132  * Copyright(c) 2006-2007, Ext JS, LLC.
6133  *
6134  * Originally Released Under LGPL - original licence link has changed is not relivant.
6135  *
6136  * Fork - LGPL
6137  * <script type="text/javascript">
6138  */
6139
6140 /**
6141  * @class Roo.data.ArrayReader
6142  * @extends Roo.data.DataReader
6143  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6144  * Each element of that Array represents a row of data fields. The
6145  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6146  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6147  * <p>
6148  * Example code:.
6149  * <pre><code>
6150 var RecordDef = Roo.data.Record.create([
6151     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6152     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6153 ]);
6154 var myReader = new Roo.data.ArrayReader({
6155     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6156 }, RecordDef);
6157 </code></pre>
6158  * <p>
6159  * This would consume an Array like this:
6160  * <pre><code>
6161 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6162   </code></pre>
6163  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6164  * @constructor
6165  * Create a new JsonReader
6166  * @param {Object} meta Metadata configuration options.
6167  * @param {Object} recordType Either an Array of field definition objects
6168  * as specified to {@link Roo.data.Record#create},
6169  * or an {@link Roo.data.Record} object
6170  * created using {@link Roo.data.Record#create}.
6171  */
6172 Roo.data.ArrayReader = function(meta, recordType){
6173     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6174 };
6175
6176 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6177     /**
6178      * Create a data block containing Roo.data.Records from an XML document.
6179      * @param {Object} o An Array of row objects which represents the dataset.
6180      * @return {Object} data A data block which is used by an Roo.data.Store object as
6181      * a cache of Roo.data.Records.
6182      */
6183     readRecords : function(o){
6184         var sid = this.meta ? this.meta.id : null;
6185         var recordType = this.recordType, fields = recordType.prototype.fields;
6186         var records = [];
6187         var root = o;
6188             for(var i = 0; i < root.length; i++){
6189                     var n = root[i];
6190                 var values = {};
6191                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6192                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6193                 var f = fields.items[j];
6194                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6195                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6196                 v = f.convert(v);
6197                 values[f.name] = v;
6198             }
6199                 var record = new recordType(values, id);
6200                 record.json = n;
6201                 records[records.length] = record;
6202             }
6203             return {
6204                 records : records,
6205                 totalRecords : records.length
6206             };
6207     }
6208 });/*
6209  * - LGPL
6210  * * 
6211  */
6212
6213 /**
6214  * @class Roo.bootstrap.ComboBox
6215  * @extends Roo.bootstrap.TriggerField
6216  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
6217  * @constructor
6218  * Create a new ComboBox.
6219  * @param {Object} config Configuration options
6220  */
6221 Roo.bootstrap.ComboBox = function(config){
6222     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
6223     this.addEvents({
6224         /**
6225          * @event expand
6226          * Fires when the dropdown list is expanded
6227              * @param {Roo.bootstrap.ComboBox} combo This combo box
6228              */
6229         'expand' : true,
6230         /**
6231          * @event collapse
6232          * Fires when the dropdown list is collapsed
6233              * @param {Roo.bootstrap.ComboBox} combo This combo box
6234              */
6235         'collapse' : true,
6236         /**
6237          * @event beforeselect
6238          * Fires before a list item is selected. Return false to cancel the selection.
6239              * @param {Roo.bootstrap.ComboBox} combo This combo box
6240              * @param {Roo.data.Record} record The data record returned from the underlying store
6241              * @param {Number} index The index of the selected item in the dropdown list
6242              */
6243         'beforeselect' : true,
6244         /**
6245          * @event select
6246          * Fires when a list item is selected
6247              * @param {Roo.bootstrap.ComboBox} combo This combo box
6248              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
6249              * @param {Number} index The index of the selected item in the dropdown list
6250              */
6251         'select' : true,
6252         /**
6253          * @event beforequery
6254          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
6255          * The event object passed has these properties:
6256              * @param {Roo.bootstrap.ComboBox} combo This combo box
6257              * @param {String} query The query
6258              * @param {Boolean} forceAll true to force "all" query
6259              * @param {Boolean} cancel true to cancel the query
6260              * @param {Object} e The query event object
6261              */
6262         'beforequery': true,
6263          /**
6264          * @event add
6265          * Fires when the 'add' icon is pressed (add a listener to enable add button)
6266              * @param {Roo.bootstrap.ComboBox} combo This combo box
6267              */
6268         'add' : true,
6269         /**
6270          * @event edit
6271          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
6272              * @param {Roo.bootstrap.ComboBox} combo This combo box
6273              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
6274              */
6275         'edit' : true
6276         
6277         
6278     });
6279     
6280     
6281     this.selectedIndex = -1;
6282     if(this.mode == 'local'){
6283         if(config.queryDelay === undefined){
6284             this.queryDelay = 10;
6285         }
6286         if(config.minChars === undefined){
6287             this.minChars = 0;
6288         }
6289     }
6290 };
6291
6292 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
6293      
6294     /**
6295      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
6296      * rendering into an Roo.Editor, defaults to false)
6297      */
6298     /**
6299      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
6300      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
6301      */
6302     /**
6303      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
6304      */
6305     /**
6306      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
6307      * the dropdown list (defaults to undefined, with no header element)
6308      */
6309
6310      /**
6311      * @cfg {String/Roo.Template} tpl The template to use to render the output
6312      */
6313      
6314      /**
6315      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
6316      */
6317     listWidth: undefined,
6318     /**
6319      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
6320      * mode = 'remote' or 'text' if mode = 'local')
6321      */
6322     displayField: undefined,
6323     /**
6324      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
6325      * mode = 'remote' or 'value' if mode = 'local'). 
6326      * Note: use of a valueField requires the user make a selection
6327      * in order for a value to be mapped.
6328      */
6329     valueField: undefined,
6330     
6331     
6332     /**
6333      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
6334      * field's data value (defaults to the underlying DOM element's name)
6335      */
6336     hiddenName: undefined,
6337     /**
6338      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
6339      */
6340     listClass: '',
6341     /**
6342      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
6343      */
6344     selectedClass: 'active',
6345     
6346     /**
6347      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
6348      */
6349     shadow:'sides',
6350     /**
6351      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
6352      * anchor positions (defaults to 'tl-bl')
6353      */
6354     listAlign: 'tl-bl?',
6355     /**
6356      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
6357      */
6358     maxHeight: 300,
6359     /**
6360      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
6361      * query specified by the allQuery config option (defaults to 'query')
6362      */
6363     triggerAction: 'query',
6364     /**
6365      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
6366      * (defaults to 4, does not apply if editable = false)
6367      */
6368     minChars : 4,
6369     /**
6370      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
6371      * delay (typeAheadDelay) if it matches a known value (defaults to false)
6372      */
6373     typeAhead: false,
6374     /**
6375      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
6376      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
6377      */
6378     queryDelay: 500,
6379     /**
6380      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
6381      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
6382      */
6383     pageSize: 0,
6384     /**
6385      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
6386      * when editable = true (defaults to false)
6387      */
6388     selectOnFocus:false,
6389     /**
6390      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
6391      */
6392     queryParam: 'query',
6393     /**
6394      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
6395      * when mode = 'remote' (defaults to 'Loading...')
6396      */
6397     loadingText: 'Loading...',
6398     /**
6399      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
6400      */
6401     resizable: false,
6402     /**
6403      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
6404      */
6405     handleHeight : 8,
6406     /**
6407      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
6408      * traditional select (defaults to true)
6409      */
6410     editable: true,
6411     /**
6412      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
6413      */
6414     allQuery: '',
6415     /**
6416      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
6417      */
6418     mode: 'remote',
6419     /**
6420      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
6421      * listWidth has a higher value)
6422      */
6423     minListWidth : 70,
6424     /**
6425      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
6426      * allow the user to set arbitrary text into the field (defaults to false)
6427      */
6428     forceSelection:false,
6429     /**
6430      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
6431      * if typeAhead = true (defaults to 250)
6432      */
6433     typeAheadDelay : 250,
6434     /**
6435      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
6436      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
6437      */
6438     valueNotFoundText : undefined,
6439     /**
6440      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
6441      */
6442     blockFocus : false,
6443     
6444     /**
6445      * @cfg {Boolean} disableClear Disable showing of clear button.
6446      */
6447     disableClear : false,
6448     /**
6449      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
6450      */
6451     alwaysQuery : false,
6452     
6453     //private
6454     addicon : false,
6455     editicon: false,
6456     
6457     // element that contains real text value.. (when hidden is used..)
6458      
6459     // private
6460     initEvents: function(){
6461         
6462         if (!this.store) {
6463             throw "can not find store for combo";
6464         }
6465         this.store = Roo.factory(this.store, Roo.data);
6466         
6467         
6468         
6469         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
6470         
6471         
6472         if(this.hiddenName){
6473             
6474             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
6475             
6476             this.hiddenField.dom.value =
6477                 this.hiddenValue !== undefined ? this.hiddenValue :
6478                 this.value !== undefined ? this.value : '';
6479
6480             // prevent input submission
6481             this.el.dom.removeAttribute('name');
6482             this.hiddenField.dom.setAttribute('name', this.hiddenName);
6483              
6484              
6485         }
6486         //if(Roo.isGecko){
6487         //    this.el.dom.setAttribute('autocomplete', 'off');
6488         //}
6489
6490         var cls = 'x-combo-list';
6491         this.list = this.el.select('ul',true).first();
6492
6493         //this.list = new Roo.Layer({
6494         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
6495         //});
6496         
6497         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
6498         this.list.setWidth(lw);
6499         
6500         this.list.on('mouseover', this.onViewOver, this);
6501         this.list.on('mousemove', this.onViewMove, this);
6502         
6503         /*
6504         this.list.swallowEvent('mousewheel');
6505         this.assetHeight = 0;
6506
6507         if(this.title){
6508             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
6509             this.assetHeight += this.header.getHeight();
6510         }
6511
6512         this.innerList = this.list.createChild({cls:cls+'-inner'});
6513         this.innerList.on('mouseover', this.onViewOver, this);
6514         this.innerList.on('mousemove', this.onViewMove, this);
6515         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
6516         
6517         if(this.allowBlank && !this.pageSize && !this.disableClear){
6518             this.footer = this.list.createChild({cls:cls+'-ft'});
6519             this.pageTb = new Roo.Toolbar(this.footer);
6520            
6521         }
6522         if(this.pageSize){
6523             this.footer = this.list.createChild({cls:cls+'-ft'});
6524             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
6525                     {pageSize: this.pageSize});
6526             
6527         }
6528         
6529         if (this.pageTb && this.allowBlank && !this.disableClear) {
6530             var _this = this;
6531             this.pageTb.add(new Roo.Toolbar.Fill(), {
6532                 cls: 'x-btn-icon x-btn-clear',
6533                 text: '&#160;',
6534                 handler: function()
6535                 {
6536                     _this.collapse();
6537                     _this.clearValue();
6538                     _this.onSelect(false, -1);
6539                 }
6540             });
6541         }
6542         if (this.footer) {
6543             this.assetHeight += this.footer.getHeight();
6544         }
6545         */
6546             
6547         if(!this.tpl){
6548             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
6549         }
6550
6551         this.view = new Roo.View(this.el.select('ul',true).first(), this.tpl, {
6552             singleSelect:true, store: this.store, selectedClass: this.selectedClass
6553         });
6554         //this.view.wrapEl.setDisplayed(false);
6555         this.view.on('click', this.onViewClick, this);
6556         
6557         
6558         
6559         this.store.on('beforeload', this.onBeforeLoad, this);
6560         this.store.on('load', this.onLoad, this);
6561         this.store.on('loadexception', this.onLoadException, this);
6562         /*
6563         if(this.resizable){
6564             this.resizer = new Roo.Resizable(this.list,  {
6565                pinned:true, handles:'se'
6566             });
6567             this.resizer.on('resize', function(r, w, h){
6568                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
6569                 this.listWidth = w;
6570                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
6571                 this.restrictHeight();
6572             }, this);
6573             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
6574         }
6575         */
6576         if(!this.editable){
6577             this.editable = true;
6578             this.setEditable(false);
6579         }
6580         
6581         /*
6582         
6583         if (typeof(this.events.add.listeners) != 'undefined') {
6584             
6585             this.addicon = this.wrap.createChild(
6586                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
6587        
6588             this.addicon.on('click', function(e) {
6589                 this.fireEvent('add', this);
6590             }, this);
6591         }
6592         if (typeof(this.events.edit.listeners) != 'undefined') {
6593             
6594             this.editicon = this.wrap.createChild(
6595                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
6596             if (this.addicon) {
6597                 this.editicon.setStyle('margin-left', '40px');
6598             }
6599             this.editicon.on('click', function(e) {
6600                 
6601                 // we fire even  if inothing is selected..
6602                 this.fireEvent('edit', this, this.lastData );
6603                 
6604             }, this);
6605         }
6606         */
6607         
6608  
6609         this.keyNav = new Roo.KeyNav(this.inputEl(), {
6610             "up" : function(e){
6611                 this.inKeyMode = true;
6612                 this.selectPrev();
6613             },
6614
6615             "down" : function(e){
6616                 if(!this.isExpanded()){
6617                     this.onTriggerClick();
6618                 }else{
6619                     this.inKeyMode = true;
6620                     this.selectNext();
6621                 }
6622             },
6623
6624             "enter" : function(e){
6625                 this.onViewClick();
6626                 //return true;
6627             },
6628
6629             "esc" : function(e){
6630                 this.collapse();
6631             },
6632
6633             "tab" : function(e){
6634                 this.onViewClick(false);
6635                 this.fireEvent("specialkey", this, e);
6636                 return true;
6637             },
6638
6639             scope : this,
6640
6641             doRelay : function(foo, bar, hname){
6642                 if(hname == 'down' || this.scope.isExpanded()){
6643                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
6644                 }
6645                 return true;
6646             },
6647
6648             forceKeyDown: true
6649         });
6650         
6651         
6652         this.queryDelay = Math.max(this.queryDelay || 10,
6653                 this.mode == 'local' ? 10 : 250);
6654         
6655         
6656         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
6657         
6658         if(this.typeAhead){
6659             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
6660         }
6661         if(this.editable !== false){
6662             this.inputEl().on("keyup", this.onKeyUp, this);
6663         }
6664         if(this.forceSelection){
6665             this.on('blur', this.doForce, this);
6666         }
6667     },
6668
6669     onDestroy : function(){
6670         if(this.view){
6671             this.view.setStore(null);
6672             this.view.el.removeAllListeners();
6673             this.view.el.remove();
6674             this.view.purgeListeners();
6675         }
6676         if(this.list){
6677             this.list.dom.innerHTML  = '';
6678         }
6679         if(this.store){
6680             this.store.un('beforeload', this.onBeforeLoad, this);
6681             this.store.un('load', this.onLoad, this);
6682             this.store.un('loadexception', this.onLoadException, this);
6683         }
6684         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
6685     },
6686
6687     // private
6688     fireKey : function(e){
6689         if(e.isNavKeyPress() && !this.list.isVisible()){
6690             this.fireEvent("specialkey", this, e);
6691         }
6692     },
6693
6694     // private
6695     onResize: function(w, h){
6696         Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
6697         
6698         if(typeof w != 'number'){
6699             // we do not handle it!?!?
6700             return;
6701         }
6702         var tw = this.trigger.getWidth();
6703        // tw += this.addicon ? this.addicon.getWidth() : 0;
6704        // tw += this.editicon ? this.editicon.getWidth() : 0;
6705         var x = w - tw;
6706         this.inputEl().setWidth( this.adjustWidth('input', x));
6707             
6708         //this.trigger.setStyle('left', x+'px');
6709         
6710         if(this.list && this.listWidth === undefined){
6711             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
6712             this.list.setWidth(lw);
6713             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
6714         }
6715         
6716     
6717         
6718     },
6719
6720     /**
6721      * Allow or prevent the user from directly editing the field text.  If false is passed,
6722      * the user will only be able to select from the items defined in the dropdown list.  This method
6723      * is the runtime equivalent of setting the 'editable' config option at config time.
6724      * @param {Boolean} value True to allow the user to directly edit the field text
6725      */
6726     setEditable : function(value){
6727         if(value == this.editable){
6728             return;
6729         }
6730         this.editable = value;
6731         if(!value){
6732             this.inputEl().dom.setAttribute('readOnly', true);
6733             this.inputEl().on('mousedown', this.onTriggerClick,  this);
6734             this.inputEl().addClass('x-combo-noedit');
6735         }else{
6736             this.inputEl().dom.setAttribute('readOnly', false);
6737             this.inputEl().un('mousedown', this.onTriggerClick,  this);
6738             this.inputEl().removeClass('x-combo-noedit');
6739         }
6740     },
6741
6742     // private
6743     onBeforeLoad : function(){
6744         if(!this.hasFocus){
6745             return;
6746         }
6747         //this.innerList.update(this.loadingText ?
6748         //       '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
6749         this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
6750         
6751         this.restrictHeight();
6752         this.selectedIndex = -1;
6753     },
6754
6755     // private
6756     onLoad : function(){
6757         if(!this.hasFocus){
6758             return;
6759         }
6760         if(this.store.getCount() > 0){
6761             this.expand();
6762             this.restrictHeight();
6763             if(this.lastQuery == this.allQuery){
6764                 if(this.editable){
6765                     this.inputEl().dom.select();
6766                 }
6767                 if(!this.selectByValue(this.value, true)){
6768                     this.select(0, true);
6769                 }
6770             }else{
6771                 this.selectNext();
6772                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
6773                     this.taTask.delay(this.typeAheadDelay);
6774                 }
6775             }
6776         }else{
6777             this.onEmptyResults();
6778         }
6779         //this.el.focus();
6780     },
6781     // private
6782     onLoadException : function()
6783     {
6784         this.collapse();
6785         Roo.log(this.store.reader.jsonData);
6786         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6787             // fixme
6788             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6789         }
6790         
6791         
6792     },
6793     // private
6794     onTypeAhead : function(){
6795         if(this.store.getCount() > 0){
6796             var r = this.store.getAt(0);
6797             var newValue = r.data[this.displayField];
6798             var len = newValue.length;
6799             var selStart = this.getRawValue().length;
6800             
6801             if(selStart != len){
6802                 this.setRawValue(newValue);
6803                 this.selectText(selStart, newValue.length);
6804             }
6805         }
6806     },
6807
6808     // private
6809     onSelect : function(record, index){
6810         if(this.fireEvent('beforeselect', this, record, index) !== false){
6811             this.setFromData(index > -1 ? record.data : false);
6812             this.collapse();
6813             this.fireEvent('select', this, record, index);
6814         }
6815     },
6816
6817     /**
6818      * Returns the currently selected field value or empty string if no value is set.
6819      * @return {String} value The selected value
6820      */
6821     getValue : function(){
6822         if(this.valueField){
6823             return typeof this.value != 'undefined' ? this.value : '';
6824         }else{
6825             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
6826         }
6827     },
6828
6829     /**
6830      * Clears any text/value currently set in the field
6831      */
6832     clearValue : function(){
6833         if(this.hiddenField){
6834             this.hiddenField.dom.value = '';
6835         }
6836         this.value = '';
6837         this.setRawValue('');
6838         this.lastSelectionText = '';
6839         
6840     },
6841
6842     /**
6843      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
6844      * will be displayed in the field.  If the value does not match the data value of an existing item,
6845      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
6846      * Otherwise the field will be blank (although the value will still be set).
6847      * @param {String} value The value to match
6848      */
6849     setValue : function(v){
6850         var text = v;
6851         if(this.valueField){
6852             var r = this.findRecord(this.valueField, v);
6853             if(r){
6854                 text = r.data[this.displayField];
6855             }else if(this.valueNotFoundText !== undefined){
6856                 text = this.valueNotFoundText;
6857             }
6858         }
6859         this.lastSelectionText = text;
6860         if(this.hiddenField){
6861             this.hiddenField.dom.value = v;
6862         }
6863         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
6864         this.value = v;
6865     },
6866     /**
6867      * @property {Object} the last set data for the element
6868      */
6869     
6870     lastData : false,
6871     /**
6872      * Sets the value of the field based on a object which is related to the record format for the store.
6873      * @param {Object} value the value to set as. or false on reset?
6874      */
6875     setFromData : function(o){
6876         var dv = ''; // display value
6877         var vv = ''; // value value..
6878         this.lastData = o;
6879         if (this.displayField) {
6880             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
6881         } else {
6882             // this is an error condition!!!
6883             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
6884         }
6885         
6886         if(this.valueField){
6887             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
6888         }
6889         if(this.hiddenField){
6890             this.hiddenField.dom.value = vv;
6891             
6892             this.lastSelectionText = dv;
6893             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
6894             this.value = vv;
6895             return;
6896         }
6897         // no hidden field.. - we store the value in 'value', but still display
6898         // display field!!!!
6899         this.lastSelectionText = dv;
6900         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
6901         this.value = vv;
6902         
6903         
6904     },
6905     // private
6906     reset : function(){
6907         // overridden so that last data is reset..
6908         this.setValue(this.originalValue);
6909         this.clearInvalid();
6910         this.lastData = false;
6911         if (this.view) {
6912             this.view.clearSelections();
6913         }
6914     },
6915     // private
6916     findRecord : function(prop, value){
6917         var record;
6918         if(this.store.getCount() > 0){
6919             this.store.each(function(r){
6920                 if(r.data[prop] == value){
6921                     record = r;
6922                     return false;
6923                 }
6924                 return true;
6925             });
6926         }
6927         return record;
6928     },
6929     
6930     getName: function()
6931     {
6932         // returns hidden if it's set..
6933         if (!this.rendered) {return ''};
6934         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
6935         
6936     },
6937     // private
6938     onViewMove : function(e, t){
6939         this.inKeyMode = false;
6940     },
6941
6942     // private
6943     onViewOver : function(e, t){
6944         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
6945             return;
6946         }
6947         var item = this.view.findItemFromChild(t);
6948         if(item){
6949             var index = this.view.indexOf(item);
6950             this.select(index, false);
6951         }
6952     },
6953
6954     // private
6955     onViewClick : function(doFocus)
6956     {
6957         var index = this.view.getSelectedIndexes()[0];
6958         var r = this.store.getAt(index);
6959         if(r){
6960             this.onSelect(r, index);
6961         }
6962         if(doFocus !== false && !this.blockFocus){
6963             this.inputEl().focus();
6964         }
6965     },
6966
6967     // private
6968     restrictHeight : function(){
6969         //this.innerList.dom.style.height = '';
6970         //var inner = this.innerList.dom;
6971         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
6972         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
6973         //this.list.beginUpdate();
6974         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
6975         this.list.alignTo(this.inputEl(), this.listAlign);
6976         //this.list.endUpdate();
6977     },
6978
6979     // private
6980     onEmptyResults : function(){
6981         this.collapse();
6982     },
6983
6984     /**
6985      * Returns true if the dropdown list is expanded, else false.
6986      */
6987     isExpanded : function(){
6988         return this.list.isVisible();
6989     },
6990
6991     /**
6992      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
6993      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
6994      * @param {String} value The data value of the item to select
6995      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
6996      * selected item if it is not currently in view (defaults to true)
6997      * @return {Boolean} True if the value matched an item in the list, else false
6998      */
6999     selectByValue : function(v, scrollIntoView){
7000         if(v !== undefined && v !== null){
7001             var r = this.findRecord(this.valueField || this.displayField, v);
7002             if(r){
7003                 this.select(this.store.indexOf(r), scrollIntoView);
7004                 return true;
7005             }
7006         }
7007         return false;
7008     },
7009
7010     /**
7011      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
7012      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
7013      * @param {Number} index The zero-based index of the list item to select
7014      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
7015      * selected item if it is not currently in view (defaults to true)
7016      */
7017     select : function(index, scrollIntoView){
7018         this.selectedIndex = index;
7019         this.view.select(index);
7020         if(scrollIntoView !== false){
7021             var el = this.view.getNode(index);
7022             if(el){
7023                 //this.innerList.scrollChildIntoView(el, false);
7024                 
7025             }
7026         }
7027     },
7028
7029     // private
7030     selectNext : function(){
7031         var ct = this.store.getCount();
7032         if(ct > 0){
7033             if(this.selectedIndex == -1){
7034                 this.select(0);
7035             }else if(this.selectedIndex < ct-1){
7036                 this.select(this.selectedIndex+1);
7037             }
7038         }
7039     },
7040
7041     // private
7042     selectPrev : function(){
7043         var ct = this.store.getCount();
7044         if(ct > 0){
7045             if(this.selectedIndex == -1){
7046                 this.select(0);
7047             }else if(this.selectedIndex != 0){
7048                 this.select(this.selectedIndex-1);
7049             }
7050         }
7051     },
7052
7053     // private
7054     onKeyUp : function(e){
7055         if(this.editable !== false && !e.isSpecialKey()){
7056             this.lastKey = e.getKey();
7057             this.dqTask.delay(this.queryDelay);
7058         }
7059     },
7060
7061     // private
7062     validateBlur : function(){
7063         return !this.list || !this.list.isVisible();   
7064     },
7065
7066     // private
7067     initQuery : function(){
7068         this.doQuery(this.getRawValue());
7069     },
7070
7071     // private
7072     doForce : function(){
7073         if(this.el.dom.value.length > 0){
7074             this.el.dom.value =
7075                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
7076              
7077         }
7078     },
7079
7080     /**
7081      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
7082      * query allowing the query action to be canceled if needed.
7083      * @param {String} query The SQL query to execute
7084      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
7085      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
7086      * saved in the current store (defaults to false)
7087      */
7088     doQuery : function(q, forceAll){
7089         if(q === undefined || q === null){
7090             q = '';
7091         }
7092         var qe = {
7093             query: q,
7094             forceAll: forceAll,
7095             combo: this,
7096             cancel:false
7097         };
7098         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
7099             return false;
7100         }
7101         q = qe.query;
7102         forceAll = qe.forceAll;
7103         if(forceAll === true || (q.length >= this.minChars)){
7104             if(this.lastQuery != q || this.alwaysQuery){
7105                 this.lastQuery = q;
7106                 if(this.mode == 'local'){
7107                     this.selectedIndex = -1;
7108                     if(forceAll){
7109                         this.store.clearFilter();
7110                     }else{
7111                         this.store.filter(this.displayField, q);
7112                     }
7113                     this.onLoad();
7114                 }else{
7115                     this.store.baseParams[this.queryParam] = q;
7116                     this.store.load({
7117                         params: this.getParams(q)
7118                     });
7119                     this.expand();
7120                 }
7121             }else{
7122                 this.selectedIndex = -1;
7123                 this.onLoad();   
7124             }
7125         }
7126     },
7127
7128     // private
7129     getParams : function(q){
7130         var p = {};
7131         //p[this.queryParam] = q;
7132         if(this.pageSize){
7133             p.start = 0;
7134             p.limit = this.pageSize;
7135         }
7136         return p;
7137     },
7138
7139     /**
7140      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
7141      */
7142     collapse : function(){
7143         if(!this.isExpanded()){
7144             return;
7145         }
7146         this.list.hide();
7147         Roo.get(document).un('mousedown', this.collapseIf, this);
7148         Roo.get(document).un('mousewheel', this.collapseIf, this);
7149         if (!this.editable) {
7150             Roo.get(document).un('keydown', this.listKeyPress, this);
7151         }
7152         this.fireEvent('collapse', this);
7153     },
7154
7155     // private
7156     collapseIf : function(e){
7157         if(!e.within(this.el) && !e.within(this.el)){
7158             this.collapse();
7159         }
7160     },
7161
7162     /**
7163      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
7164      */
7165     expand : function(){
7166         Roo.log('expand');
7167         if(this.isExpanded() || !this.hasFocus){
7168             return;
7169         }
7170         this.list.alignTo(this.inputEl(), this.listAlign);
7171         this.list.show();
7172         Roo.get(document).on('mousedown', this.collapseIf, this);
7173         Roo.get(document).on('mousewheel', this.collapseIf, this);
7174         if (!this.editable) {
7175             Roo.get(document).on('keydown', this.listKeyPress, this);
7176         }
7177         
7178         this.fireEvent('expand', this);
7179     },
7180
7181     // private
7182     // Implements the default empty TriggerField.onTriggerClick function
7183     onTriggerClick : function()
7184     {
7185         Roo.log('trigger click');
7186         
7187         if(this.disabled){
7188             return;
7189         }
7190         if(this.isExpanded()){
7191             this.collapse();
7192             if (!this.blockFocus) {
7193                 this.inputEl().focus();
7194             }
7195             
7196         }else {
7197             this.hasFocus = true;
7198             if(this.triggerAction == 'all') {
7199                 this.doQuery(this.allQuery, true);
7200             } else {
7201                 this.doQuery(this.getRawValue());
7202             }
7203             if (!this.blockFocus) {
7204                 this.inputEl().focus();
7205             }
7206         }
7207     },
7208     listKeyPress : function(e)
7209     {
7210         //Roo.log('listkeypress');
7211         // scroll to first matching element based on key pres..
7212         if (e.isSpecialKey()) {
7213             return false;
7214         }
7215         var k = String.fromCharCode(e.getKey()).toUpperCase();
7216         //Roo.log(k);
7217         var match  = false;
7218         var csel = this.view.getSelectedNodes();
7219         var cselitem = false;
7220         if (csel.length) {
7221             var ix = this.view.indexOf(csel[0]);
7222             cselitem  = this.store.getAt(ix);
7223             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
7224                 cselitem = false;
7225             }
7226             
7227         }
7228         
7229         this.store.each(function(v) { 
7230             if (cselitem) {
7231                 // start at existing selection.
7232                 if (cselitem.id == v.id) {
7233                     cselitem = false;
7234                 }
7235                 return true;
7236             }
7237                 
7238             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
7239                 match = this.store.indexOf(v);
7240                 return false;
7241             }
7242             return true;
7243         }, this);
7244         
7245         if (match === false) {
7246             return true; // no more action?
7247         }
7248         // scroll to?
7249         this.view.select(match);
7250         var sn = Roo.get(this.view.getSelectedNodes()[0])
7251         //sn.scrollIntoView(sn.dom.parentNode, false);
7252     }
7253
7254     /** 
7255     * @cfg {Boolean} grow 
7256     * @hide 
7257     */
7258     /** 
7259     * @cfg {Number} growMin 
7260     * @hide 
7261     */
7262     /** 
7263     * @cfg {Number} growMax 
7264     * @hide 
7265     */
7266     /**
7267      * @hide
7268      * @method autoSize
7269      */
7270 });/*
7271  * Based on:
7272  * Ext JS Library 1.1.1
7273  * Copyright(c) 2006-2007, Ext JS, LLC.
7274  *
7275  * Originally Released Under LGPL - original licence link has changed is not relivant.
7276  *
7277  * Fork - LGPL
7278  * <script type="text/javascript">
7279  */
7280
7281 /**
7282  * @class Roo.View
7283  * @extends Roo.util.Observable
7284  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
7285  * This class also supports single and multi selection modes. <br>
7286  * Create a data model bound view:
7287  <pre><code>
7288  var store = new Roo.data.Store(...);
7289
7290  var view = new Roo.View({
7291     el : "my-element",
7292     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
7293  
7294     singleSelect: true,
7295     selectedClass: "ydataview-selected",
7296     store: store
7297  });
7298
7299  // listen for node click?
7300  view.on("click", function(vw, index, node, e){
7301  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
7302  });
7303
7304  // load XML data
7305  dataModel.load("foobar.xml");
7306  </code></pre>
7307  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
7308  * <br><br>
7309  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
7310  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
7311  * 
7312  * Note: old style constructor is still suported (container, template, config)
7313  * 
7314  * @constructor
7315  * Create a new View
7316  * @param {Object} config The config object
7317  * 
7318  */
7319 Roo.View = function(config, depreciated_tpl, depreciated_config){
7320     
7321     if (typeof(depreciated_tpl) == 'undefined') {
7322         // new way.. - universal constructor.
7323         Roo.apply(this, config);
7324         this.el  = Roo.get(this.el);
7325     } else {
7326         // old format..
7327         this.el  = Roo.get(config);
7328         this.tpl = depreciated_tpl;
7329         Roo.apply(this, depreciated_config);
7330     }
7331     this.wrapEl  = this.el.wrap().wrap();
7332     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
7333     
7334     
7335     if(typeof(this.tpl) == "string"){
7336         this.tpl = new Roo.Template(this.tpl);
7337     } else {
7338         // support xtype ctors..
7339         this.tpl = new Roo.factory(this.tpl, Roo);
7340     }
7341     
7342     
7343     this.tpl.compile();
7344    
7345   
7346     
7347      
7348     /** @private */
7349     this.addEvents({
7350         /**
7351          * @event beforeclick
7352          * Fires before a click is processed. Returns false to cancel the default action.
7353          * @param {Roo.View} this
7354          * @param {Number} index The index of the target node
7355          * @param {HTMLElement} node The target node
7356          * @param {Roo.EventObject} e The raw event object
7357          */
7358             "beforeclick" : true,
7359         /**
7360          * @event click
7361          * Fires when a template node is clicked.
7362          * @param {Roo.View} this
7363          * @param {Number} index The index of the target node
7364          * @param {HTMLElement} node The target node
7365          * @param {Roo.EventObject} e The raw event object
7366          */
7367             "click" : true,
7368         /**
7369          * @event dblclick
7370          * Fires when a template node is double clicked.
7371          * @param {Roo.View} this
7372          * @param {Number} index The index of the target node
7373          * @param {HTMLElement} node The target node
7374          * @param {Roo.EventObject} e The raw event object
7375          */
7376             "dblclick" : true,
7377         /**
7378          * @event contextmenu
7379          * Fires when a template node is right clicked.
7380          * @param {Roo.View} this
7381          * @param {Number} index The index of the target node
7382          * @param {HTMLElement} node The target node
7383          * @param {Roo.EventObject} e The raw event object
7384          */
7385             "contextmenu" : true,
7386         /**
7387          * @event selectionchange
7388          * Fires when the selected nodes change.
7389          * @param {Roo.View} this
7390          * @param {Array} selections Array of the selected nodes
7391          */
7392             "selectionchange" : true,
7393     
7394         /**
7395          * @event beforeselect
7396          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
7397          * @param {Roo.View} this
7398          * @param {HTMLElement} node The node to be selected
7399          * @param {Array} selections Array of currently selected nodes
7400          */
7401             "beforeselect" : true,
7402         /**
7403          * @event preparedata
7404          * Fires on every row to render, to allow you to change the data.
7405          * @param {Roo.View} this
7406          * @param {Object} data to be rendered (change this)
7407          */
7408           "preparedata" : true
7409           
7410           
7411         });
7412
7413
7414
7415     this.el.on({
7416         "click": this.onClick,
7417         "dblclick": this.onDblClick,
7418         "contextmenu": this.onContextMenu,
7419         scope:this
7420     });
7421
7422     this.selections = [];
7423     this.nodes = [];
7424     this.cmp = new Roo.CompositeElementLite([]);
7425     if(this.store){
7426         this.store = Roo.factory(this.store, Roo.data);
7427         this.setStore(this.store, true);
7428     }
7429     
7430     if ( this.footer && this.footer.xtype) {
7431            
7432          var fctr = this.wrapEl.appendChild(document.createElement("div"));
7433         
7434         this.footer.dataSource = this.store
7435         this.footer.container = fctr;
7436         this.footer = Roo.factory(this.footer, Roo);
7437         fctr.insertFirst(this.el);
7438         
7439         // this is a bit insane - as the paging toolbar seems to detach the el..
7440 //        dom.parentNode.parentNode.parentNode
7441          // they get detached?
7442     }
7443     
7444     
7445     Roo.View.superclass.constructor.call(this);
7446     
7447     
7448 };
7449
7450 Roo.extend(Roo.View, Roo.util.Observable, {
7451     
7452      /**
7453      * @cfg {Roo.data.Store} store Data store to load data from.
7454      */
7455     store : false,
7456     
7457     /**
7458      * @cfg {String|Roo.Element} el The container element.
7459      */
7460     el : '',
7461     
7462     /**
7463      * @cfg {String|Roo.Template} tpl The template used by this View 
7464      */
7465     tpl : false,
7466     /**
7467      * @cfg {String} dataName the named area of the template to use as the data area
7468      *                          Works with domtemplates roo-name="name"
7469      */
7470     dataName: false,
7471     /**
7472      * @cfg {String} selectedClass The css class to add to selected nodes
7473      */
7474     selectedClass : "x-view-selected",
7475      /**
7476      * @cfg {String} emptyText The empty text to show when nothing is loaded.
7477      */
7478     emptyText : "",
7479     
7480     /**
7481      * @cfg {String} text to display on mask (default Loading)
7482      */
7483     mask : false,
7484     /**
7485      * @cfg {Boolean} multiSelect Allow multiple selection
7486      */
7487     multiSelect : false,
7488     /**
7489      * @cfg {Boolean} singleSelect Allow single selection
7490      */
7491     singleSelect:  false,
7492     
7493     /**
7494      * @cfg {Boolean} toggleSelect - selecting 
7495      */
7496     toggleSelect : false,
7497     
7498     /**
7499      * Returns the element this view is bound to.
7500      * @return {Roo.Element}
7501      */
7502     getEl : function(){
7503         return this.wrapEl;
7504     },
7505     
7506     
7507
7508     /**
7509      * Refreshes the view. - called by datachanged on the store. - do not call directly.
7510      */
7511     refresh : function(){
7512         var t = this.tpl;
7513         
7514         // if we are using something like 'domtemplate', then
7515         // the what gets used is:
7516         // t.applySubtemplate(NAME, data, wrapping data..)
7517         // the outer template then get' applied with
7518         //     the store 'extra data'
7519         // and the body get's added to the
7520         //      roo-name="data" node?
7521         //      <span class='roo-tpl-{name}'></span> ?????
7522         
7523         
7524         
7525         this.clearSelections();
7526         this.el.update("");
7527         var html = [];
7528         var records = this.store.getRange();
7529         if(records.length < 1) {
7530             
7531             // is this valid??  = should it render a template??
7532             
7533             this.el.update(this.emptyText);
7534             return;
7535         }
7536         var el = this.el;
7537         if (this.dataName) {
7538             this.el.update(t.apply(this.store.meta)); //????
7539             el = this.el.child('.roo-tpl-' + this.dataName);
7540         }
7541         
7542         for(var i = 0, len = records.length; i < len; i++){
7543             var data = this.prepareData(records[i].data, i, records[i]);
7544             this.fireEvent("preparedata", this, data, i, records[i]);
7545             html[html.length] = Roo.util.Format.trim(
7546                 this.dataName ?
7547                     t.applySubtemplate(this.dataName, data, this.store.meta) :
7548                     t.apply(data)
7549             );
7550         }
7551         
7552         
7553         
7554         el.update(html.join(""));
7555         this.nodes = el.dom.childNodes;
7556         this.updateIndexes(0);
7557     },
7558
7559     /**
7560      * Function to override to reformat the data that is sent to
7561      * the template for each node.
7562      * DEPRICATED - use the preparedata event handler.
7563      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
7564      * a JSON object for an UpdateManager bound view).
7565      */
7566     prepareData : function(data, index, record)
7567     {
7568         this.fireEvent("preparedata", this, data, index, record);
7569         return data;
7570     },
7571
7572     onUpdate : function(ds, record){
7573         this.clearSelections();
7574         var index = this.store.indexOf(record);
7575         var n = this.nodes[index];
7576         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
7577         n.parentNode.removeChild(n);
7578         this.updateIndexes(index, index);
7579     },
7580
7581     
7582     
7583 // --------- FIXME     
7584     onAdd : function(ds, records, index)
7585     {
7586         this.clearSelections();
7587         if(this.nodes.length == 0){
7588             this.refresh();
7589             return;
7590         }
7591         var n = this.nodes[index];
7592         for(var i = 0, len = records.length; i < len; i++){
7593             var d = this.prepareData(records[i].data, i, records[i]);
7594             if(n){
7595                 this.tpl.insertBefore(n, d);
7596             }else{
7597                 
7598                 this.tpl.append(this.el, d);
7599             }
7600         }
7601         this.updateIndexes(index);
7602     },
7603
7604     onRemove : function(ds, record, index){
7605         this.clearSelections();
7606         var el = this.dataName  ?
7607             this.el.child('.roo-tpl-' + this.dataName) :
7608             this.el; 
7609         el.dom.removeChild(this.nodes[index]);
7610         this.updateIndexes(index);
7611     },
7612
7613     /**
7614      * Refresh an individual node.
7615      * @param {Number} index
7616      */
7617     refreshNode : function(index){
7618         this.onUpdate(this.store, this.store.getAt(index));
7619     },
7620
7621     updateIndexes : function(startIndex, endIndex){
7622         var ns = this.nodes;
7623         startIndex = startIndex || 0;
7624         endIndex = endIndex || ns.length - 1;
7625         for(var i = startIndex; i <= endIndex; i++){
7626             ns[i].nodeIndex = i;
7627         }
7628     },
7629
7630     /**
7631      * Changes the data store this view uses and refresh the view.
7632      * @param {Store} store
7633      */
7634     setStore : function(store, initial){
7635         if(!initial && this.store){
7636             this.store.un("datachanged", this.refresh);
7637             this.store.un("add", this.onAdd);
7638             this.store.un("remove", this.onRemove);
7639             this.store.un("update", this.onUpdate);
7640             this.store.un("clear", this.refresh);
7641             this.store.un("beforeload", this.onBeforeLoad);
7642             this.store.un("load", this.onLoad);
7643             this.store.un("loadexception", this.onLoad);
7644         }
7645         if(store){
7646           
7647             store.on("datachanged", this.refresh, this);
7648             store.on("add", this.onAdd, this);
7649             store.on("remove", this.onRemove, this);
7650             store.on("update", this.onUpdate, this);
7651             store.on("clear", this.refresh, this);
7652             store.on("beforeload", this.onBeforeLoad, this);
7653             store.on("load", this.onLoad, this);
7654             store.on("loadexception", this.onLoad, this);
7655         }
7656         
7657         if(store){
7658             this.refresh();
7659         }
7660     },
7661     /**
7662      * onbeforeLoad - masks the loading area.
7663      *
7664      */
7665     onBeforeLoad : function()
7666     {
7667         this.el.update("");
7668         this.el.mask(this.mask ? this.mask : "Loading" ); 
7669     },
7670     onLoad : function ()
7671     {
7672         this.el.unmask();
7673     },
7674     
7675
7676     /**
7677      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
7678      * @param {HTMLElement} node
7679      * @return {HTMLElement} The template node
7680      */
7681     findItemFromChild : function(node){
7682         var el = this.dataName  ?
7683             this.el.child('.roo-tpl-' + this.dataName,true) :
7684             this.el.dom; 
7685         
7686         if(!node || node.parentNode == el){
7687                     return node;
7688             }
7689             var p = node.parentNode;
7690             while(p && p != el){
7691             if(p.parentNode == el){
7692                 return p;
7693             }
7694             p = p.parentNode;
7695         }
7696             return null;
7697     },
7698
7699     /** @ignore */
7700     onClick : function(e){
7701         var item = this.findItemFromChild(e.getTarget());
7702         if(item){
7703             var index = this.indexOf(item);
7704             if(this.onItemClick(item, index, e) !== false){
7705                 this.fireEvent("click", this, index, item, e);
7706             }
7707         }else{
7708             this.clearSelections();
7709         }
7710     },
7711
7712     /** @ignore */
7713     onContextMenu : function(e){
7714         var item = this.findItemFromChild(e.getTarget());
7715         if(item){
7716             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
7717         }
7718     },
7719
7720     /** @ignore */
7721     onDblClick : function(e){
7722         var item = this.findItemFromChild(e.getTarget());
7723         if(item){
7724             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
7725         }
7726     },
7727
7728     onItemClick : function(item, index, e)
7729     {
7730         if(this.fireEvent("beforeclick", this, index, item, e) === false){
7731             return false;
7732         }
7733         if (this.toggleSelect) {
7734             var m = this.isSelected(item) ? 'unselect' : 'select';
7735             Roo.log(m);
7736             var _t = this;
7737             _t[m](item, true, false);
7738             return true;
7739         }
7740         if(this.multiSelect || this.singleSelect){
7741             if(this.multiSelect && e.shiftKey && this.lastSelection){
7742                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
7743             }else{
7744                 this.select(item, this.multiSelect && e.ctrlKey);
7745                 this.lastSelection = item;
7746             }
7747             e.preventDefault();
7748         }
7749         return true;
7750     },
7751
7752     /**
7753      * Get the number of selected nodes.
7754      * @return {Number}
7755      */
7756     getSelectionCount : function(){
7757         return this.selections.length;
7758     },
7759
7760     /**
7761      * Get the currently selected nodes.
7762      * @return {Array} An array of HTMLElements
7763      */
7764     getSelectedNodes : function(){
7765         return this.selections;
7766     },
7767
7768     /**
7769      * Get the indexes of the selected nodes.
7770      * @return {Array}
7771      */
7772     getSelectedIndexes : function(){
7773         var indexes = [], s = this.selections;
7774         for(var i = 0, len = s.length; i < len; i++){
7775             indexes.push(s[i].nodeIndex);
7776         }
7777         return indexes;
7778     },
7779
7780     /**
7781      * Clear all selections
7782      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
7783      */
7784     clearSelections : function(suppressEvent){
7785         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
7786             this.cmp.elements = this.selections;
7787             this.cmp.removeClass(this.selectedClass);
7788             this.selections = [];
7789             if(!suppressEvent){
7790                 this.fireEvent("selectionchange", this, this.selections);
7791             }
7792         }
7793     },
7794
7795     /**
7796      * Returns true if the passed node is selected
7797      * @param {HTMLElement/Number} node The node or node index
7798      * @return {Boolean}
7799      */
7800     isSelected : function(node){
7801         var s = this.selections;
7802         if(s.length < 1){
7803             return false;
7804         }
7805         node = this.getNode(node);
7806         return s.indexOf(node) !== -1;
7807     },
7808
7809     /**
7810      * Selects nodes.
7811      * @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
7812      * @param {Boolean} keepExisting (optional) true to keep existing selections
7813      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
7814      */
7815     select : function(nodeInfo, keepExisting, suppressEvent){
7816         if(nodeInfo instanceof Array){
7817             if(!keepExisting){
7818                 this.clearSelections(true);
7819             }
7820             for(var i = 0, len = nodeInfo.length; i < len; i++){
7821                 this.select(nodeInfo[i], true, true);
7822             }
7823             return;
7824         } 
7825         var node = this.getNode(nodeInfo);
7826         if(!node || this.isSelected(node)){
7827             return; // already selected.
7828         }
7829         if(!keepExisting){
7830             this.clearSelections(true);
7831         }
7832         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
7833             Roo.fly(node).addClass(this.selectedClass);
7834             this.selections.push(node);
7835             if(!suppressEvent){
7836                 this.fireEvent("selectionchange", this, this.selections);
7837             }
7838         }
7839         
7840         
7841     },
7842       /**
7843      * Unselects nodes.
7844      * @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
7845      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
7846      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
7847      */
7848     unselect : function(nodeInfo, keepExisting, suppressEvent)
7849     {
7850         if(nodeInfo instanceof Array){
7851             Roo.each(this.selections, function(s) {
7852                 this.unselect(s, nodeInfo);
7853             }, this);
7854             return;
7855         }
7856         var node = this.getNode(nodeInfo);
7857         if(!node || !this.isSelected(node)){
7858             Roo.log("not selected");
7859             return; // not selected.
7860         }
7861         // fireevent???
7862         var ns = [];
7863         Roo.each(this.selections, function(s) {
7864             if (s == node ) {
7865                 Roo.fly(node).removeClass(this.selectedClass);
7866
7867                 return;
7868             }
7869             ns.push(s);
7870         },this);
7871         
7872         this.selections= ns;
7873         this.fireEvent("selectionchange", this, this.selections);
7874     },
7875
7876     /**
7877      * Gets a template node.
7878      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
7879      * @return {HTMLElement} The node or null if it wasn't found
7880      */
7881     getNode : function(nodeInfo){
7882         if(typeof nodeInfo == "string"){
7883             return document.getElementById(nodeInfo);
7884         }else if(typeof nodeInfo == "number"){
7885             return this.nodes[nodeInfo];
7886         }
7887         return nodeInfo;
7888     },
7889
7890     /**
7891      * Gets a range template nodes.
7892      * @param {Number} startIndex
7893      * @param {Number} endIndex
7894      * @return {Array} An array of nodes
7895      */
7896     getNodes : function(start, end){
7897         var ns = this.nodes;
7898         start = start || 0;
7899         end = typeof end == "undefined" ? ns.length - 1 : end;
7900         var nodes = [];
7901         if(start <= end){
7902             for(var i = start; i <= end; i++){
7903                 nodes.push(ns[i]);
7904             }
7905         } else{
7906             for(var i = start; i >= end; i--){
7907                 nodes.push(ns[i]);
7908             }
7909         }
7910         return nodes;
7911     },
7912
7913     /**
7914      * Finds the index of the passed node
7915      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
7916      * @return {Number} The index of the node or -1
7917      */
7918     indexOf : function(node){
7919         node = this.getNode(node);
7920         if(typeof node.nodeIndex == "number"){
7921             return node.nodeIndex;
7922         }
7923         var ns = this.nodes;
7924         for(var i = 0, len = ns.length; i < len; i++){
7925             if(ns[i] == node){
7926                 return i;
7927             }
7928         }
7929         return -1;
7930     }
7931 });
7932 /*
7933  * - LGPL
7934  *
7935  * based on jquery fullcalendar
7936  * 
7937  */
7938
7939
7940 /**
7941  * @class Roo.bootstrap.Calendar
7942  * @extends Roo.bootstrap.Component
7943  * Bootstrap Calendar class
7944     
7945  * @constructor
7946  * Create a new Container
7947  * @param {Object} config The config object
7948  */
7949
7950 Roo.bootstrap.Calendar = function(config){
7951     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
7952      this.addEvents({
7953         /**
7954              * @event select
7955              * Fires when a date is selected
7956              * @param {DatePicker} this
7957              * @param {Date} date The selected date
7958              */
7959         'select': true,
7960         /**
7961              * @event monthchange
7962              * Fires when the displayed month changes 
7963              * @param {DatePicker} this
7964              * @param {Date} date The selected month
7965              */
7966         'monthchange': true,
7967         /**
7968              * @event evententer
7969              * Fires when mouse over an event
7970              * @param {Calendar} this
7971              * @param {event} Event
7972              */
7973         'evententer': true,
7974         /**
7975              * @event eventleave
7976              * Fires when the mouse leaves an
7977              * @param {Calendar} this
7978              * @param {event}
7979              */
7980         'eventleave': true,
7981         /**
7982              * @event eventclick
7983              * Fires when the mouse click an
7984              * @param {Calendar} this
7985              * @param {event}
7986              */
7987         'eventclick': true
7988         
7989     });
7990
7991 };
7992
7993 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
7994     
7995      /**
7996      * @cfg {Number} startDay
7997      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
7998      */
7999     startDay : 0,
8000       
8001     getAutoCreate : function(){
8002         
8003         
8004         fc_button = function(name, corner, style, content ) {
8005             return Roo.apply({},{
8006                 tag : 'span',
8007                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
8008                          (corner.length ?
8009                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
8010                             ''
8011                         ),
8012                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
8013                 unselectable: 'on'
8014             });
8015         };
8016         
8017         var header = {
8018             tag : 'table',
8019             cls : 'fc-header',
8020             style : 'width:100%',
8021             cn : [
8022                 {
8023                     tag: 'tr',
8024                     cn : [
8025                         {
8026                             tag : 'td',
8027                             cls : 'fc-header-left',
8028                             cn : [
8029                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
8030                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
8031                                 { tag: 'span', cls: 'fc-header-space' },
8032                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
8033                                 
8034                                 
8035                             ]
8036                         },
8037                         
8038                         {
8039                             tag : 'td',
8040                             cls : 'fc-header-center',
8041                             cn : [
8042                                 {
8043                                     tag: 'span',
8044                                     cls: 'fc-header-title',
8045                                     cn : {
8046                                         tag: 'H2',
8047                                         html : 'month / year'
8048                                     }
8049                                 }
8050                                 
8051                             ]
8052                         },
8053                         {
8054                             tag : 'td',
8055                             cls : 'fc-header-right',
8056                             cn : [
8057                           /*      fc_button('month', 'left', '', 'month' ),
8058                                 fc_button('week', '', '', 'week' ),
8059                                 fc_button('day', 'right', '', 'day' )
8060                             */    
8061                                 
8062                             ]
8063                         }
8064                         
8065                     ]
8066                 }
8067             ]
8068         };
8069         
8070        
8071         var cal_heads = function() {
8072             var ret = [];
8073             // fixme - handle this.
8074             
8075             for (var i =0; i < Date.dayNames.length; i++) {
8076                 var d = Date.dayNames[i];
8077                 ret.push({
8078                     tag: 'th',
8079                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
8080                     html : d.substring(0,3)
8081                 });
8082                 
8083             }
8084             ret[0].cls += ' fc-first';
8085             ret[6].cls += ' fc-last';
8086             return ret;
8087         };
8088         var cal_cell = function(n) {
8089             return  {
8090                 tag: 'td',
8091                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
8092                 cn : [
8093                     {
8094                         cn : [
8095                             {
8096                                 cls: 'fc-day-number',
8097                                 html: 'D'
8098                             },
8099                             {
8100                                 cls: 'fc-day-content',
8101                              
8102                                 cn : [
8103                                      {
8104                                         style: 'position: relative;' // height: 17px;
8105                                     }
8106                                 ]
8107                             }
8108                             
8109                             
8110                         ]
8111                     }
8112                 ]
8113                 
8114             }
8115         };
8116         var cal_rows = function() {
8117             
8118             var ret = []
8119             for (var r = 0; r < 6; r++) {
8120                 var row= {
8121                     tag : 'tr',
8122                     cls : 'fc-week',
8123                     cn : []
8124                 };
8125                 
8126                 for (var i =0; i < Date.dayNames.length; i++) {
8127                     var d = Date.dayNames[i];
8128                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
8129
8130                 }
8131                 row.cn[0].cls+=' fc-first';
8132                 row.cn[0].cn[0].style = 'min-height:90px';
8133                 row.cn[6].cls+=' fc-last';
8134                 ret.push(row);
8135                 
8136             }
8137             ret[0].cls += ' fc-first';
8138             ret[4].cls += ' fc-prev-last';
8139             ret[5].cls += ' fc-last';
8140             return ret;
8141             
8142         };
8143         
8144         var cal_table = {
8145             tag: 'table',
8146             cls: 'fc-border-separate',
8147             style : 'width:100%',
8148             cellspacing  : 0,
8149             cn : [
8150                 { 
8151                     tag: 'thead',
8152                     cn : [
8153                         { 
8154                             tag: 'tr',
8155                             cls : 'fc-first fc-last',
8156                             cn : cal_heads()
8157                         }
8158                     ]
8159                 },
8160                 { 
8161                     tag: 'tbody',
8162                     cn : cal_rows()
8163                 }
8164                   
8165             ]
8166         };
8167          
8168          var cfg = {
8169             cls : 'fc fc-ltr',
8170             cn : [
8171                 header,
8172                 {
8173                     cls : 'fc-content',
8174                     style : "position: relative;",
8175                     cn : [
8176                         {
8177                             cls : 'fc-view fc-view-month fc-grid',
8178                             style : 'position: relative',
8179                             unselectable : 'on',
8180                             cn : [
8181                                 {
8182                                     cls : 'fc-event-container',
8183                                     style : 'position:absolute;z-index:8;top:0;left:0;'
8184                                 },
8185                                 cal_table
8186                             ]
8187                         }
8188                     ]
8189     
8190                 }
8191            ] 
8192             
8193         };
8194         
8195          
8196         
8197         return cfg;
8198     },
8199     
8200     
8201     initEvents : function()
8202     {
8203         if(!this.store){
8204             throw "can not find store for combo";
8205         }
8206         
8207         this.store = Roo.factory(this.store, Roo.data);
8208         this.store.on('load', this.onLoad, this);
8209         
8210         this.resize();
8211         this.cells = this.el.select('.fc-day',true);
8212         
8213         this.textNodes = this.el.query('.fc-day-number');
8214         this.cells.addClassOnOver('fc-state-hover');
8215         
8216         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
8217         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
8218         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
8219         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
8220         
8221         this.on('monthchange', this.onMonthChange, this);
8222         
8223         this.update(new Date().clearTime());
8224     },
8225     
8226     resize : function() {
8227         var sz  = this.el.getSize();
8228         
8229         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
8230         this.el.select('.fc-day-content div',true).setHeight(34);
8231     },
8232     
8233     
8234     // private
8235     showPrevMonth : function(e){
8236         this.update(this.activeDate.add("mo", -1));
8237     },
8238     showToday : function(e){
8239         this.update(new Date().clearTime());
8240     },
8241     // private
8242     showNextMonth : function(e){
8243         this.update(this.activeDate.add("mo", 1));
8244     },
8245
8246     // private
8247     showPrevYear : function(){
8248         this.update(this.activeDate.add("y", -1));
8249     },
8250
8251     // private
8252     showNextYear : function(){
8253         this.update(this.activeDate.add("y", 1));
8254     },
8255
8256     
8257    // private
8258     update : function(date)
8259     {
8260         var vd = this.activeDate;
8261         this.activeDate = date;
8262 //        if(vd && this.el){
8263 //            var t = date.getTime();
8264 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
8265 //                Roo.log('using add remove');
8266 //                
8267 //                this.fireEvent('monthchange', this, date);
8268 //                
8269 //                this.cells.removeClass("fc-state-highlight");
8270 //                this.cells.each(function(c){
8271 //                   if(c.dateValue == t){
8272 //                       c.addClass("fc-state-highlight");
8273 //                       setTimeout(function(){
8274 //                            try{c.dom.firstChild.focus();}catch(e){}
8275 //                       }, 50);
8276 //                       return false;
8277 //                   }
8278 //                   return true;
8279 //                });
8280 //                return;
8281 //            }
8282 //        }
8283         
8284         var days = date.getDaysInMonth();
8285         
8286         var firstOfMonth = date.getFirstDateOfMonth();
8287         var startingPos = firstOfMonth.getDay()-this.startDay;
8288         
8289         if(startingPos < this.startDay){
8290             startingPos += 7;
8291         }
8292         
8293         var pm = date.add("mo", -1);
8294         var prevStart = pm.getDaysInMonth()-startingPos;
8295 //        
8296         this.cells = this.el.select('.fc-day',true);
8297         this.textNodes = this.el.query('.fc-day-number');
8298         this.cells.addClassOnOver('fc-state-hover');
8299         
8300         var cells = this.cells.elements;
8301         var textEls = this.textNodes;
8302         
8303         Roo.each(cells, function(cell){
8304             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
8305         });
8306         
8307         days += startingPos;
8308
8309         // convert everything to numbers so it's fast
8310         var day = 86400000;
8311         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
8312         var today = new Date().clearTime().getTime();
8313         var sel = date.clearTime().getTime();
8314         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
8315         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
8316         var ddMatch = this.disabledDatesRE;
8317         var ddText = this.disabledDatesText;
8318         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
8319         var ddaysText = this.disabledDaysText;
8320         var format = this.format;
8321         
8322         var setCellClass = function(cal, cell){
8323             cell.title = "";
8324             var t = d.getTime();
8325             
8326             cell.dateValue = t;
8327             if(t == today){
8328                 cell.className += " fc-today";
8329                 cell.className += " fc-state-highlight";
8330                 cell.title = cal.todayText;
8331             }
8332             if(t == sel){
8333                 // disable highlight in other month..
8334                 //cell.className += " fc-state-highlight";
8335                 
8336             }
8337             // disabling
8338             if(t < min) {
8339                 cell.className = " fc-state-disabled";
8340                 cell.title = cal.minText;
8341                 return;
8342             }
8343             if(t > max) {
8344                 cell.className = " fc-state-disabled";
8345                 cell.title = cal.maxText;
8346                 return;
8347             }
8348             if(ddays){
8349                 if(ddays.indexOf(d.getDay()) != -1){
8350                     cell.title = ddaysText;
8351                     cell.className = " fc-state-disabled";
8352                 }
8353             }
8354             if(ddMatch && format){
8355                 var fvalue = d.dateFormat(format);
8356                 if(ddMatch.test(fvalue)){
8357                     cell.title = ddText.replace("%0", fvalue);
8358                     cell.className = " fc-state-disabled";
8359                 }
8360             }
8361             
8362             if (!cell.initialClassName) {
8363                 cell.initialClassName = cell.dom.className;
8364             }
8365             
8366             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
8367         };
8368
8369         var i = 0;
8370         
8371         for(; i < startingPos; i++) {
8372             textEls[i].innerHTML = (++prevStart);
8373             d.setDate(d.getDate()+1);
8374             
8375             cells[i].className = "fc-past fc-other-month";
8376             setCellClass(this, cells[i]);
8377         }
8378         
8379         var intDay = 0;
8380         
8381         for(; i < days; i++){
8382             intDay = i - startingPos + 1;
8383             textEls[i].innerHTML = (intDay);
8384             d.setDate(d.getDate()+1);
8385             
8386             cells[i].className = ''; // "x-date-active";
8387             setCellClass(this, cells[i]);
8388         }
8389         var extraDays = 0;
8390         
8391         for(; i < 42; i++) {
8392             textEls[i].innerHTML = (++extraDays);
8393             d.setDate(d.getDate()+1);
8394             
8395             cells[i].className = "fc-future fc-other-month";
8396             setCellClass(this, cells[i]);
8397         }
8398         
8399         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
8400         
8401         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
8402         
8403         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
8404         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
8405         
8406         if(totalRows != 6){
8407             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
8408             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
8409         }
8410         
8411         this.fireEvent('monthchange', this, date);
8412         
8413         
8414         /*
8415         if(!this.internalRender){
8416             var main = this.el.dom.firstChild;
8417             var w = main.offsetWidth;
8418             this.el.setWidth(w + this.el.getBorderWidth("lr"));
8419             Roo.fly(main).setWidth(w);
8420             this.internalRender = true;
8421             // opera does not respect the auto grow header center column
8422             // then, after it gets a width opera refuses to recalculate
8423             // without a second pass
8424             if(Roo.isOpera && !this.secondPass){
8425                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
8426                 this.secondPass = true;
8427                 this.update.defer(10, this, [date]);
8428             }
8429         }
8430         */
8431         
8432     },
8433     
8434     findCell : function(dt) {
8435         dt = dt.clearTime().getTime();
8436         var ret = false;
8437         this.cells.each(function(c){
8438             //Roo.log("check " +c.dateValue + '?=' + dt);
8439             if(c.dateValue == dt){
8440                 ret = c;
8441                 return false;
8442             }
8443             return true;
8444         });
8445         
8446         return ret;
8447     },
8448     
8449     findCells : function(ev) {
8450         var s = ev.start.clone().clearTime().getTime();
8451         var e= ev.end.clone().clearTime().getTime();
8452         var ret = [];
8453         this.cells.each(function(c){
8454             //Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
8455             
8456             if(c.dateValue > e){
8457                 return ;
8458             }
8459             if(c.dateValue < s){
8460                 return ;
8461             }
8462             ret.push(c);
8463         });
8464         
8465         return ret;    
8466     },
8467     
8468     findBestRow: function(cells)
8469     {
8470         var ret = 0;
8471         
8472         for (var i =0 ; i < cells.length;i++) {
8473             ret  = Math.max(cells[i].rows || 0,ret);
8474         }
8475         return ret;
8476         
8477     },
8478     
8479     
8480     addItem : function(ev)
8481     {
8482         // look for vertical location slot in
8483         var cells = this.findCells(ev);
8484         
8485         ev.row = this.findBestRow(cells);
8486         
8487         // work out the location.
8488         
8489         var crow = false;
8490         var rows = [];
8491         for(var i =0; i < cells.length; i++) {
8492             if (!crow) {
8493                 crow = {
8494                     start : cells[i],
8495                     end :  cells[i]
8496                 };
8497                 continue;
8498             }
8499             if (crow.start.getY() == cells[i].getY()) {
8500                 // on same row.
8501                 crow.end = cells[i];
8502                 continue;
8503             }
8504             // different row.
8505             rows.push(crow);
8506             crow = {
8507                 start: cells[i],
8508                 end : cells[i]
8509             };
8510             
8511         }
8512         
8513         rows.push(crow);
8514         ev.els = [];
8515         ev.rows = rows;
8516         ev.cells = cells;
8517         for (var i = 0; i < cells.length;i++) {
8518             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
8519             
8520         }
8521         
8522         this.calevents.push(ev);
8523     },
8524     
8525     clearEvents: function() {
8526         
8527         if(!this.calevents){
8528             return;
8529         }
8530         
8531         Roo.each(this.cells.elements, function(c){
8532             c.rows = 0;
8533         });
8534         
8535         Roo.each(this.calevents, function(e) {
8536             Roo.each(e.els, function(el) {
8537                 el.un('mouseenter' ,this.onEventEnter, this);
8538                 el.un('mouseleave' ,this.onEventLeave, this);
8539                 el.remove();
8540             },this);
8541         },this);
8542         
8543     },
8544     
8545     renderEvents: function()
8546     {   
8547         // first make sure there is enough space..
8548         
8549         this.cells.each(function(c) {
8550         
8551             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
8552         });
8553         
8554         for (var e = 0; e < this.calevents.length; e++) {
8555             var ev = this.calevents[e];
8556             var cells = ev.cells;
8557             var rows = ev.rows;
8558             
8559             for(var i =0; i < rows.length; i++) {
8560                 
8561                  
8562                 // how many rows should it span..
8563                 
8564                 var  cfg = {
8565                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
8566                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
8567                     
8568                     unselectable : "on",
8569                     cn : [
8570                         {
8571                             cls: 'fc-event-inner',
8572                             cn : [
8573                                 {
8574                                   tag:'span',
8575                                   cls: 'fc-event-time',
8576                                   html : cells.length > 1 ? '' : ev.time
8577                                 },
8578                                 {
8579                                   tag:'span',
8580                                   cls: 'fc-event-title',
8581                                   html : String.format('{0}', ev.title)
8582                                 }
8583                                 
8584                                 
8585                             ]
8586                         },
8587                         {
8588                             cls: 'ui-resizable-handle ui-resizable-e',
8589                             html : '&nbsp;&nbsp;&nbsp'
8590                         }
8591                         
8592                     ]
8593                 };
8594                 if (i == 0) {
8595                     cfg.cls += ' fc-event-start';
8596                 }
8597                 if ((i+1) == rows.length) {
8598                     cfg.cls += ' fc-event-end';
8599                 }
8600                 
8601                 var ctr = this.el.select('.fc-event-container',true).first();
8602                 var cg = ctr.createChild(cfg);
8603                 
8604                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
8605                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
8606                 cg.on('click', this.onEventClick, this, ev);
8607                 
8608                 ev.els.push(cg);
8609                 
8610                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
8611                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
8612                 //Roo.log(cg);
8613                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
8614                 cg.setWidth(ebox.right - sbox.x -2);
8615             }
8616             
8617             
8618         }
8619         
8620     },
8621     
8622     onEventEnter: function (e, el,event,d) {
8623         this.fireEvent('evententer', this, el, event);
8624     },
8625     
8626     onEventLeave: function (e, el,event,d) {
8627         this.fireEvent('eventleave', this, el, event);
8628     },
8629     
8630     onEventClick: function (e, el,event,d) {
8631         this.fireEvent('eventclick', this, el, event);
8632     },
8633     
8634     onMonthChange: function () {
8635         this.store.load();
8636     },
8637     
8638     onLoad: function () {
8639         
8640         this.clearEvents();
8641         Roo.log('calendar onload');
8642 //        
8643         this.calevents = [];
8644         var cal = this;
8645         if(this.store.getCount() > 0){
8646             this.store.data.each(function(d){
8647                cal.addItem({
8648                     id : d.data.id,
8649                     start: new Date(d.data.start_dt),
8650                     end : new Date(d.data.end_dt),
8651                     time : d.data.start_time,
8652                     title : d.data.title,
8653                     description : d.data.description,
8654                     venue : d.data.venue
8655                 });
8656             });
8657         }
8658         
8659         this.renderEvents();
8660     }
8661 });
8662
8663  
8664  /*
8665  * - LGPL
8666  *
8667  * element
8668  * 
8669  */
8670
8671 /**
8672  * @class Roo.bootstrap.Popover
8673  * @extends Roo.bootstrap.Component
8674  * Bootstrap Popover class
8675  * @cfg {String} html contents of the popover   (or false to use children..)
8676  * @cfg {String} title of popover (or false to hide)
8677  * @cfg {String} placement how it is placed
8678  * @cfg {String} trigger click || hover (or false to trigger manually)
8679  * @cfg {String} over what (parent or false to trigger manually.)
8680  * 
8681  * @constructor
8682  * Create a new Popover
8683  * @param {Object} config The config object
8684  */
8685
8686 Roo.bootstrap.Popover = function(config){
8687     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
8688 };
8689
8690 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
8691     
8692     title: 'Fill in a title',
8693     html: false,
8694     
8695     placement : 'right',
8696     trigger : 'hover', // hover
8697     
8698     over: 'parent',
8699     
8700     can_build_overlaid : false,
8701     
8702     getChildContainer : function()
8703     {
8704         return this.el.select('.popover-content',true).first();
8705     },
8706     
8707     getAutoCreate : function(){
8708          Roo.log('make popover?');
8709         var cfg = {
8710            cls : 'popover roo-dynamic',
8711            style: 'display:block',
8712            cn : [
8713                 {
8714                     cls : 'arrow'
8715                 },
8716                 {
8717                     cls : 'popover-inner',
8718                     cn : [
8719                         {
8720                             tag: 'h3',
8721                             cls: 'popover-title',
8722                             html : this.title
8723                         },
8724                         {
8725                             cls : 'popover-content',
8726                             html : this.html
8727                         }
8728                     ]
8729                     
8730                 }
8731            ]
8732         };
8733         
8734         return cfg;
8735     },
8736     setTitle: function(str)
8737     {
8738         this.el.select('.popover-title',true).first().dom.innerHTML = str;
8739     },
8740     setContent: function(str)
8741     {
8742         this.el.select('.popover-content',true).first().dom.innerHTML = str;
8743     },
8744     // as it get's added to the bottom of the page.
8745     onRender : function(ct, position)
8746     {
8747         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
8748         if(!this.el){
8749             var cfg = Roo.apply({},  this.getAutoCreate());
8750             cfg.id = Roo.id();
8751             
8752             if (this.cls) {
8753                 cfg.cls += ' ' + this.cls;
8754             }
8755             if (this.style) {
8756                 cfg.style = this.style;
8757             }
8758             Roo.log("adding to ")
8759             this.el = Roo.get(document.body).createChild(cfg, position);
8760             Roo.log(this.el);
8761         }
8762         this.initEvents();
8763     },
8764     
8765     initEvents : function()
8766     {
8767         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
8768         this.el.enableDisplayMode('block');
8769         this.el.hide();
8770         if (this.over === false) {
8771             return; 
8772         }
8773         if (this.triggers === false) {
8774             return;
8775         }
8776         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
8777         var triggers = this.trigger ? this.trigger.split(' ') : [];
8778         Roo.each(triggers, function(trigger) {
8779         
8780             if (trigger == 'click') {
8781                 on_el.on('click', this.toggle, this);
8782             } else if (trigger != 'manual') {
8783                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
8784                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
8785       
8786                 on_el.on(eventIn  ,this.enter, this);
8787                 on_el.on(eventOut, this.leave, this);
8788             }
8789         }, this);
8790         
8791     },
8792     
8793     
8794     // private
8795     timeout : null,
8796     hoverState : null,
8797     
8798     toggle : function () {
8799         this.hoverState == 'in' ? this.leave() : this.enter();
8800     },
8801     
8802     enter : function () {
8803        
8804     
8805         clearTimeout(this.timeout);
8806     
8807         this.hoverState = 'in'
8808     
8809         if (!this.delay || !this.delay.show) {
8810             this.show();
8811             return 
8812         }
8813         var _t = this;
8814         this.timeout = setTimeout(function () {
8815             if (_t.hoverState == 'in') {
8816                 _t.show();
8817             }
8818         }, this.delay.show)
8819     },
8820     leave : function() {
8821         clearTimeout(this.timeout);
8822     
8823         this.hoverState = 'out'
8824     
8825         if (!this.delay || !this.delay.hide) {
8826             this.hide();
8827             return 
8828         }
8829         var _t = this;
8830         this.timeout = setTimeout(function () {
8831             if (_t.hoverState == 'out') {
8832                 _t.hide();
8833             }
8834         }, this.delay.hide)
8835     },
8836     
8837     show : function (on_el)
8838     {
8839         if (!on_el) {
8840             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
8841         }
8842         // set content.
8843         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
8844         if (this.html !== false) {
8845             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
8846         }
8847         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
8848         if (!this.title.length) {
8849             this.el.select('.popover-title',true).hide();
8850         }
8851         
8852         var placement = typeof this.placement == 'function' ?
8853             this.placement.call(this, this.el, on_el) :
8854             this.placement;
8855             
8856         var autoToken = /\s?auto?\s?/i;
8857         var autoPlace = autoToken.test(placement);
8858         if (autoPlace) {
8859             placement = placement.replace(autoToken, '') || 'top';
8860         }
8861         
8862         //this.el.detach()
8863         //this.el.setXY([0,0]);
8864         this.el.show();
8865         this.el.dom.style.display='block';
8866         this.el.addClass(placement);
8867         
8868         //this.el.appendTo(on_el);
8869         
8870         var p = this.getPosition();
8871         var box = this.el.getBox();
8872         
8873         if (autoPlace) {
8874             // fixme..
8875         }
8876         var align = Roo.bootstrap.Popover.alignment[placement]
8877         this.el.alignTo(on_el, align[0],align[1]);
8878         //var arrow = this.el.select('.arrow',true).first();
8879         //arrow.set(align[2], 
8880         
8881         this.el.addClass('in');
8882         this.hoverState = null;
8883         
8884         if (this.el.hasClass('fade')) {
8885             // fade it?
8886         }
8887         
8888     },
8889     hide : function()
8890     {
8891         this.el.setXY([0,0]);
8892         this.el.removeClass('in');
8893         this.el.hide();
8894         
8895     }
8896     
8897 });
8898
8899 Roo.bootstrap.Popover.alignment = {
8900     'left' : ['r-l', [-10,0], 'right'],
8901     'right' : ['l-r', [10,0], 'left'],
8902     'bottom' : ['t-b', [0,10], 'top'],
8903     'top' : [ 'b-t', [0,-10], 'bottom']
8904 };
8905
8906