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