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