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