Roo/bootstrap/TimeField.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         if (this.disabled) {
658             return;
659         }
660         
661         Roo.log('button on click ');
662         if(this.preventDefault){
663             e.preventDefault();
664         }
665         if (this.pressed === true || this.pressed === false) {
666             this.pressed = !this.pressed;
667             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
668             this.fireEvent('toggle', this, e, this.pressed);
669         }
670         
671         
672         this.fireEvent('click', this, e);
673     },
674     
675     /**
676      * Enables this button
677      */
678     enable : function()
679     {
680         this.disabled = false;
681         this.el.removeClass('disabled');
682     },
683     
684     /**
685      * Disable this button
686      */
687     disable : function()
688     {
689         this.disabled = true;
690         this.el.addClass('disabled');
691     },
692      /**
693      * sets the active state on/off, 
694      * @param {Boolean} state (optional) Force a particular state
695      */
696     setActive : function(v) {
697         
698         this.el[v ? 'addClass' : 'removeClass']('active');
699     },
700      /**
701      * toggles the current active state 
702      */
703     toggleActive : function()
704     {
705        var active = this.el.hasClass('active');
706        this.setActive(!active);
707        
708         
709     }
710     
711     
712     
713 });
714
715  /*
716  * - LGPL
717  *
718  * column
719  * 
720  */
721
722 /**
723  * @class Roo.bootstrap.Column
724  * @extends Roo.bootstrap.Component
725  * Bootstrap Column class
726  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
727  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
728  * @cfg {Number} md colspan out of 12 for computer-sized screens
729  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
730  * @cfg {String} html content of column.
731  * 
732  * @constructor
733  * Create a new Column
734  * @param {Object} config The config object
735  */
736
737 Roo.bootstrap.Column = function(config){
738     Roo.bootstrap.Column.superclass.constructor.call(this, config);
739 };
740
741 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
742     
743     xs: null,
744     sm: null,
745     md: null,
746     lg: null,
747     html: '',
748     offset: 0,
749     
750     getAutoCreate : function(){
751         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
752         
753         cfg = {
754             tag: 'div',
755             cls: 'column'
756         };
757         
758         var settings=this;
759         ['xs','sm','md','lg'].map(function(size){
760             if (settings[size]) {
761                 cfg.cls += ' col-' + size + '-' + settings[size];
762             }
763         });
764         if (this.html.length) {
765             cfg.html = this.html;
766         }
767         
768         return cfg;
769     }
770    
771 });
772
773  
774
775  /*
776  * - LGPL
777  *
778  * page container.
779  * 
780  */
781
782
783 /**
784  * @class Roo.bootstrap.Container
785  * @extends Roo.bootstrap.Component
786  * Bootstrap Container class
787  * @cfg {Boolean} jumbotron is it a jumbotron element
788  * @cfg {String} html content of element
789  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
790  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
791  * @cfg {String} header content of header (for panel)
792  * @cfg {String} footer content of footer (for panel)
793  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
794  *     
795  * @constructor
796  * Create a new Container
797  * @param {Object} config The config object
798  */
799
800 Roo.bootstrap.Container = function(config){
801     Roo.bootstrap.Container.superclass.constructor.call(this, config);
802 };
803
804 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
805     
806     jumbotron : false,
807     well: '',
808     panel : '',
809     header: '',
810     footer : '',
811     sticky: '',
812   
813      
814     getChildContainer : function() {
815         
816         if(!this.el){
817             return false;
818         }
819         
820         if (this.panel.length) {
821             return this.el.select('.panel-body',true).first();
822         }
823         
824         return this.el;
825     },
826     
827     
828     getAutoCreate : function(){
829         
830         var cfg = {
831             html : '',
832             cls : ''
833         };
834         if (this.jumbotron) {
835             cfg.cls = 'jumbotron';
836         }
837         if (this.cls) {
838             cfg.cls = this.cls + '';
839         }
840         
841         if (this.sticky.length) {
842             
843             var bd = Roo.get(document.body);
844             if (!bd.hasClass('bootstrap-sticky')) {
845                 bd.addClass('bootstrap-sticky');
846                 Roo.select('html',true).setStyle('height', '100%');
847             }
848              
849             cfg.cls += 'bootstrap-sticky-' + this.sticky;
850         }
851         
852         
853         if (this.well.length) {
854             switch (this.well) {
855                 case 'lg':
856                 case 'sm':
857                     cfg.cls +=' well well-' +this.well;
858                     break;
859                 default:
860                     cfg.cls +=' well';
861                     break;
862             }
863         }
864         
865         var body = cfg;
866         
867         if (this.panel.length) {
868             cfg.cls += ' panel panel-' + this.panel;
869             cfg.cn = [];
870             if (this.header.length) {
871                 cfg.cn.push({
872                     
873                     cls : 'panel-heading',
874                     cn : [{
875                         tag: 'h3',
876                         cls : 'panel-title',
877                         html : this.header
878                     }]
879                     
880                 });
881             }
882             body = false;
883             cfg.cn.push({
884                 cls : 'panel-body',
885                 html : this.html
886             });
887             
888             
889             if (this.footer.length) {
890                 cfg.cn.push({
891                     cls : 'panel-footer',
892                     html : this.footer
893                     
894                 });
895             }
896             
897         }
898         if (body) {
899             body.html = this.html || cfg.html;
900         }
901         if (!cfg.cls.length) {
902             cfg.cls =  'container';
903         }
904         
905         return cfg;
906     }
907    
908 });
909
910  /*
911  * - LGPL
912  *
913  * image
914  * 
915  */
916
917
918 /**
919  * @class Roo.bootstrap.Img
920  * @extends Roo.bootstrap.Component
921  * Bootstrap Img class
922  * @cfg {Boolean} imgResponsive false | true
923  * @cfg {String} border rounded | circle | thumbnail
924  * @cfg {String} src image source
925  * @cfg {String} alt image alternative text
926  * @cfg {String} href a tag href
927  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
928  * 
929  * @constructor
930  * Create a new Input
931  * @param {Object} config The config object
932  */
933
934 Roo.bootstrap.Img = function(config){
935     Roo.bootstrap.Img.superclass.constructor.call(this, config);
936     
937     this.addEvents({
938         // img events
939         /**
940          * @event click
941          * The img click event for the img.
942          * @param {Roo.EventObject} e
943          */
944         "click" : true
945     });
946 };
947
948 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
949     
950     imgResponsive: true,
951     border: '',
952     src: '',
953     href: false,
954     target: false,
955
956     getAutoCreate : function(){
957         
958         var cfg = {
959             tag: 'img',
960             cls: 'img-responsive',
961             html : null
962         }
963         
964         cfg.html = this.html || cfg.html;
965         
966         cfg.src = this.src || cfg.src;
967         
968         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
969             cfg.cls += ' img-' + this.border;
970         }
971         
972         if(this.alt){
973             cfg.alt = this.alt;
974         }
975         
976         if(this.href){
977             var a = {
978                 tag: 'a',
979                 href: this.href,
980                 cn: [
981                     cfg
982                 ]
983             }
984             
985             if(this.target){
986                 a.target = this.target;
987             }
988             
989         }
990         
991         
992         return (this.href) ? a : cfg;
993     },
994     
995     initEvents: function() {
996         
997         if(!this.href){
998             this.el.on('click', this.onClick, this);
999         }
1000     },
1001     
1002     onClick : function(e)
1003     {
1004         Roo.log('img onclick');
1005         this.fireEvent('click', this, e);
1006     }
1007    
1008 });
1009
1010  /*
1011  * - LGPL
1012  *
1013  * header
1014  * 
1015  */
1016
1017 /**
1018  * @class Roo.bootstrap.Header
1019  * @extends Roo.bootstrap.Component
1020  * Bootstrap Header class
1021  * @cfg {String} html content of header
1022  * @cfg {Number} level (1|2|3|4|5|6) default 1
1023  * 
1024  * @constructor
1025  * Create a new Header
1026  * @param {Object} config The config object
1027  */
1028
1029
1030 Roo.bootstrap.Header  = function(config){
1031     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1032 };
1033
1034 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1035     
1036     //href : false,
1037     html : false,
1038     level : 1,
1039     
1040     
1041     
1042     getAutoCreate : function(){
1043         
1044         var cfg = {
1045             tag: 'h' + (1 *this.level),
1046             html: this.html || 'fill in html'
1047         } ;
1048         
1049         return cfg;
1050     }
1051    
1052 });
1053
1054  
1055
1056  /*
1057  * Based on:
1058  * Ext JS Library 1.1.1
1059  * Copyright(c) 2006-2007, Ext JS, LLC.
1060  *
1061  * Originally Released Under LGPL - original licence link has changed is not relivant.
1062  *
1063  * Fork - LGPL
1064  * <script type="text/javascript">
1065  */
1066  
1067 /**
1068  * @class Roo.bootstrap.MenuMgr
1069  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1070  * @singleton
1071  */
1072 Roo.bootstrap.MenuMgr = function(){
1073    var menus, active, groups = {}, attached = false, lastShow = new Date();
1074
1075    // private - called when first menu is created
1076    function init(){
1077        menus = {};
1078        active = new Roo.util.MixedCollection();
1079        Roo.get(document).addKeyListener(27, function(){
1080            if(active.length > 0){
1081                hideAll();
1082            }
1083        });
1084    }
1085
1086    // private
1087    function hideAll(){
1088        if(active && active.length > 0){
1089            var c = active.clone();
1090            c.each(function(m){
1091                m.hide();
1092            });
1093        }
1094    }
1095
1096    // private
1097    function onHide(m){
1098        active.remove(m);
1099        if(active.length < 1){
1100            Roo.get(document).un("mouseup", onMouseDown);
1101             
1102            attached = false;
1103        }
1104    }
1105
1106    // private
1107    function onShow(m){
1108        var last = active.last();
1109        lastShow = new Date();
1110        active.add(m);
1111        if(!attached){
1112           Roo.get(document).on("mouseup", onMouseDown);
1113            
1114            attached = true;
1115        }
1116        if(m.parentMenu){
1117           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1118           m.parentMenu.activeChild = m;
1119        }else if(last && last.isVisible()){
1120           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1121        }
1122    }
1123
1124    // private
1125    function onBeforeHide(m){
1126        if(m.activeChild){
1127            m.activeChild.hide();
1128        }
1129        if(m.autoHideTimer){
1130            clearTimeout(m.autoHideTimer);
1131            delete m.autoHideTimer;
1132        }
1133    }
1134
1135    // private
1136    function onBeforeShow(m){
1137        var pm = m.parentMenu;
1138        if(!pm && !m.allowOtherMenus){
1139            hideAll();
1140        }else if(pm && pm.activeChild && active != m){
1141            pm.activeChild.hide();
1142        }
1143    }
1144
1145    // private
1146    function onMouseDown(e){
1147         Roo.log("on MouseDown");
1148         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1149            hideAll();
1150         }
1151         
1152         
1153    }
1154
1155    // private
1156    function onBeforeCheck(mi, state){
1157        if(state){
1158            var g = groups[mi.group];
1159            for(var i = 0, l = g.length; i < l; i++){
1160                if(g[i] != mi){
1161                    g[i].setChecked(false);
1162                }
1163            }
1164        }
1165    }
1166
1167    return {
1168
1169        /**
1170         * Hides all menus that are currently visible
1171         */
1172        hideAll : function(){
1173             hideAll();  
1174        },
1175
1176        // private
1177        register : function(menu){
1178            if(!menus){
1179                init();
1180            }
1181            menus[menu.id] = menu;
1182            menu.on("beforehide", onBeforeHide);
1183            menu.on("hide", onHide);
1184            menu.on("beforeshow", onBeforeShow);
1185            menu.on("show", onShow);
1186            var g = menu.group;
1187            if(g && menu.events["checkchange"]){
1188                if(!groups[g]){
1189                    groups[g] = [];
1190                }
1191                groups[g].push(menu);
1192                menu.on("checkchange", onCheck);
1193            }
1194        },
1195
1196         /**
1197          * Returns a {@link Roo.menu.Menu} object
1198          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1199          * be used to generate and return a new Menu instance.
1200          */
1201        get : function(menu){
1202            if(typeof menu == "string"){ // menu id
1203                return menus[menu];
1204            }else if(menu.events){  // menu instance
1205                return menu;
1206            }
1207            /*else if(typeof menu.length == 'number'){ // array of menu items?
1208                return new Roo.bootstrap.Menu({items:menu});
1209            }else{ // otherwise, must be a config
1210                return new Roo.bootstrap.Menu(menu);
1211            }
1212            */
1213            return false;
1214        },
1215
1216        // private
1217        unregister : function(menu){
1218            delete menus[menu.id];
1219            menu.un("beforehide", onBeforeHide);
1220            menu.un("hide", onHide);
1221            menu.un("beforeshow", onBeforeShow);
1222            menu.un("show", onShow);
1223            var g = menu.group;
1224            if(g && menu.events["checkchange"]){
1225                groups[g].remove(menu);
1226                menu.un("checkchange", onCheck);
1227            }
1228        },
1229
1230        // private
1231        registerCheckable : function(menuItem){
1232            var g = menuItem.group;
1233            if(g){
1234                if(!groups[g]){
1235                    groups[g] = [];
1236                }
1237                groups[g].push(menuItem);
1238                menuItem.on("beforecheckchange", onBeforeCheck);
1239            }
1240        },
1241
1242        // private
1243        unregisterCheckable : function(menuItem){
1244            var g = menuItem.group;
1245            if(g){
1246                groups[g].remove(menuItem);
1247                menuItem.un("beforecheckchange", onBeforeCheck);
1248            }
1249        }
1250    };
1251 }();/*
1252  * - LGPL
1253  *
1254  * menu
1255  * 
1256  */
1257
1258 /**
1259  * @class Roo.bootstrap.Menu
1260  * @extends Roo.bootstrap.Component
1261  * Bootstrap Menu class - container for MenuItems
1262  * @cfg {String} type type of menu
1263  * 
1264  * @constructor
1265  * Create a new Menu
1266  * @param {Object} config The config object
1267  */
1268
1269
1270 Roo.bootstrap.Menu = function(config){
1271     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1272     if (this.registerMenu) {
1273         Roo.bootstrap.MenuMgr.register(this);
1274     }
1275     this.addEvents({
1276         /**
1277          * @event beforeshow
1278          * Fires before this menu is displayed
1279          * @param {Roo.menu.Menu} this
1280          */
1281         beforeshow : true,
1282         /**
1283          * @event beforehide
1284          * Fires before this menu is hidden
1285          * @param {Roo.menu.Menu} this
1286          */
1287         beforehide : true,
1288         /**
1289          * @event show
1290          * Fires after this menu is displayed
1291          * @param {Roo.menu.Menu} this
1292          */
1293         show : true,
1294         /**
1295          * @event hide
1296          * Fires after this menu is hidden
1297          * @param {Roo.menu.Menu} this
1298          */
1299         hide : true,
1300         /**
1301          * @event click
1302          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1303          * @param {Roo.menu.Menu} this
1304          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1305          * @param {Roo.EventObject} e
1306          */
1307         click : true,
1308         /**
1309          * @event mouseover
1310          * Fires when the mouse is hovering over this menu
1311          * @param {Roo.menu.Menu} this
1312          * @param {Roo.EventObject} e
1313          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1314          */
1315         mouseover : true,
1316         /**
1317          * @event mouseout
1318          * Fires when the mouse exits this menu
1319          * @param {Roo.menu.Menu} this
1320          * @param {Roo.EventObject} e
1321          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1322          */
1323         mouseout : true,
1324         /**
1325          * @event itemclick
1326          * Fires when a menu item contained in this menu is clicked
1327          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1328          * @param {Roo.EventObject} e
1329          */
1330         itemclick: true
1331     });
1332     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1333 };
1334
1335 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1336     
1337    /// html : false,
1338     //align : '',
1339     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1340     type: false,
1341     /**
1342      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1343      */
1344     registerMenu : true,
1345     
1346     menuItems :false, // stores the menu items..
1347     
1348     hidden:true,
1349     
1350     parentMenu : false,
1351     
1352     getChildContainer : function() {
1353         return this.el;  
1354     },
1355     
1356     getAutoCreate : function(){
1357          
1358         //if (['right'].indexOf(this.align)!==-1) {
1359         //    cfg.cn[1].cls += ' pull-right'
1360         //}
1361         var cfg = {
1362             tag : 'ul',
1363             cls : 'dropdown-menu' ,
1364             style : 'z-index:1000'
1365             
1366         }
1367         
1368         if (this.type === 'submenu') {
1369             cfg.cls = 'submenu active'
1370         }
1371         
1372         return cfg;
1373     },
1374     initEvents : function() {
1375         
1376        // Roo.log("ADD event");
1377        // Roo.log(this.triggerEl.dom);
1378         this.triggerEl.on('click', this.onTriggerPress, this);
1379         this.triggerEl.addClass('dropdown-toggle');
1380         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1381
1382         this.el.on("mouseover", this.onMouseOver, this);
1383         this.el.on("mouseout", this.onMouseOut, this);
1384         
1385         
1386     },
1387     findTargetItem : function(e){
1388         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1389         if(!t){
1390             return false;
1391         }
1392         //Roo.log(t);         Roo.log(t.id);
1393         if(t && t.id){
1394             //Roo.log(this.menuitems);
1395             return this.menuitems.get(t.id);
1396             
1397             //return this.items.get(t.menuItemId);
1398         }
1399         
1400         return false;
1401     },
1402     onClick : function(e){
1403         Roo.log("menu.onClick");
1404         var t = this.findTargetItem(e);
1405         if(!t){
1406             return;
1407         }
1408         Roo.log(e);
1409         /*
1410         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1411             if(t == this.activeItem && t.shouldDeactivate(e)){
1412                 this.activeItem.deactivate();
1413                 delete this.activeItem;
1414                 return;
1415             }
1416             if(t.canActivate){
1417                 this.setActiveItem(t, true);
1418             }
1419             return;
1420             
1421             
1422         }
1423         */
1424         Roo.log('pass click event');
1425         
1426         t.onClick(e);
1427         
1428         this.fireEvent("click", this, t, e);
1429         
1430         this.hide();
1431     },
1432      onMouseOver : function(e){
1433         var t  = this.findTargetItem(e);
1434         //Roo.log(t);
1435         //if(t){
1436         //    if(t.canActivate && !t.disabled){
1437         //        this.setActiveItem(t, true);
1438         //    }
1439         //}
1440         
1441         this.fireEvent("mouseover", this, e, t);
1442     },
1443     isVisible : function(){
1444         return !this.hidden;
1445     },
1446      onMouseOut : function(e){
1447         var t  = this.findTargetItem(e);
1448         
1449         //if(t ){
1450         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1451         //        this.activeItem.deactivate();
1452         //        delete this.activeItem;
1453         //    }
1454         //}
1455         this.fireEvent("mouseout", this, e, t);
1456     },
1457     
1458     
1459     /**
1460      * Displays this menu relative to another element
1461      * @param {String/HTMLElement/Roo.Element} element The element to align to
1462      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1463      * the element (defaults to this.defaultAlign)
1464      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1465      */
1466     show : function(el, pos, parentMenu){
1467         this.parentMenu = parentMenu;
1468         if(!this.el){
1469             this.render();
1470         }
1471         this.fireEvent("beforeshow", this);
1472         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1473     },
1474      /**
1475      * Displays this menu at a specific xy position
1476      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1477      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1478      */
1479     showAt : function(xy, parentMenu, /* private: */_e){
1480         this.parentMenu = parentMenu;
1481         if(!this.el){
1482             this.render();
1483         }
1484         if(_e !== false){
1485             this.fireEvent("beforeshow", this);
1486             
1487             //xy = this.el.adjustForConstraints(xy);
1488         }
1489         //this.el.setXY(xy);
1490         //this.el.show();
1491         this.hideMenuItems();
1492         this.hidden = false;
1493         this.triggerEl.addClass('open');
1494         this.focus();
1495         this.fireEvent("show", this);
1496     },
1497     
1498     focus : function(){
1499         return;
1500         if(!this.hidden){
1501             this.doFocus.defer(50, this);
1502         }
1503     },
1504
1505     doFocus : function(){
1506         if(!this.hidden){
1507             this.focusEl.focus();
1508         }
1509     },
1510
1511     /**
1512      * Hides this menu and optionally all parent menus
1513      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1514      */
1515     hide : function(deep){
1516         
1517         this.hideMenuItems();
1518         if(this.el && this.isVisible()){
1519             this.fireEvent("beforehide", this);
1520             if(this.activeItem){
1521                 this.activeItem.deactivate();
1522                 this.activeItem = null;
1523             }
1524             this.triggerEl.removeClass('open');;
1525             this.hidden = true;
1526             this.fireEvent("hide", this);
1527         }
1528         if(deep === true && this.parentMenu){
1529             this.parentMenu.hide(true);
1530         }
1531     },
1532     
1533     onTriggerPress  : function(e)
1534     {
1535         
1536         Roo.log('trigger press');
1537         //Roo.log(e.getTarget());
1538        // Roo.log(this.triggerEl.dom);
1539         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1540             return;
1541         }
1542         if (this.isVisible()) {
1543             Roo.log('hide');
1544             this.hide();
1545         } else {
1546             this.show(this.triggerEl, false, false);
1547         }
1548         
1549         
1550     },
1551     
1552          
1553        
1554     
1555     hideMenuItems : function()
1556     {
1557         //$(backdrop).remove()
1558         Roo.select('.open',true).each(function(aa) {
1559             
1560             aa.removeClass('open');
1561           //var parent = getParent($(this))
1562           //var relatedTarget = { relatedTarget: this }
1563           
1564            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1565           //if (e.isDefaultPrevented()) return
1566            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1567         })
1568     },
1569     addxtypeChild : function (tree, cntr) {
1570         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1571           
1572         this.menuitems.add(comp);
1573         return comp;
1574
1575     },
1576     getEl : function()
1577     {
1578         Roo.log(this.el);
1579         return this.el;
1580     }
1581 });
1582
1583  
1584  /*
1585  * - LGPL
1586  *
1587  * menu item
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.MenuItem
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap MenuItem class
1596  * @cfg {String} html the menu label
1597  * @cfg {String} href the link
1598  * @cfg {Boolean} preventDefault (true | false) default true
1599  * 
1600  * 
1601  * @constructor
1602  * Create a new MenuItem
1603  * @param {Object} config The config object
1604  */
1605
1606
1607 Roo.bootstrap.MenuItem = function(config){
1608     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1609     this.addEvents({
1610         // raw events
1611         /**
1612          * @event click
1613          * The raw click event for the entire grid.
1614          * @param {Roo.EventObject} e
1615          */
1616         "click" : true
1617     });
1618 };
1619
1620 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1621     
1622     href : false,
1623     html : false,
1624     preventDefault: true,
1625     
1626     getAutoCreate : function(){
1627         var cfg= {
1628             tag: 'li',
1629         cls: 'dropdown-menu-item',
1630             cn: [
1631             {
1632                 tag : 'a',
1633                 href : '#',
1634                 html : 'Link'
1635             }
1636             ]
1637     };
1638         
1639         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1640         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1641         return cfg;
1642     },
1643     
1644     initEvents: function() {
1645         
1646         //this.el.select('a').on('click', this.onClick, this);
1647         
1648     },
1649     onClick : function(e)
1650     {
1651         Roo.log('item on click ');
1652         //if(this.preventDefault){
1653         //    e.preventDefault();
1654         //}
1655         //this.parent().hideMenuItems();
1656         
1657         this.fireEvent('click', this, e);
1658     },
1659     getEl : function()
1660     {
1661         return this.el;
1662     }
1663 });
1664
1665  
1666
1667  /*
1668  * - LGPL
1669  *
1670  * menu separator
1671  * 
1672  */
1673
1674
1675 /**
1676  * @class Roo.bootstrap.MenuSeparator
1677  * @extends Roo.bootstrap.Component
1678  * Bootstrap MenuSeparator class
1679  * 
1680  * @constructor
1681  * Create a new MenuItem
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.MenuSeparator = function(config){
1687     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1691     
1692     getAutoCreate : function(){
1693         var cfg = {
1694             cls: 'divider',
1695             tag : 'li'
1696         };
1697         
1698         return cfg;
1699     }
1700    
1701 });
1702
1703  
1704
1705  
1706 /*
1707 <div class="modal fade">
1708   <div class="modal-dialog">
1709     <div class="modal-content">
1710       <div class="modal-header">
1711         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1712         <h4 class="modal-title">Modal title</h4>
1713       </div>
1714       <div class="modal-body">
1715         <p>One fine body&hellip;</p>
1716       </div>
1717       <div class="modal-footer">
1718         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1719         <button type="button" class="btn btn-primary">Save changes</button>
1720       </div>
1721     </div><!-- /.modal-content -->
1722   </div><!-- /.modal-dialog -->
1723 </div><!-- /.modal -->
1724 */
1725 /*
1726  * - LGPL
1727  *
1728  * page contgainer.
1729  * 
1730  */
1731
1732 /**
1733  * @class Roo.bootstrap.Modal
1734  * @extends Roo.bootstrap.Component
1735  * Bootstrap Modal class
1736  * @cfg {String} title Title of dialog
1737  * @cfg {Array} buttons Array of buttons or standard button set..
1738  * 
1739  * @constructor
1740  * Create a new Modal Dialog
1741  * @param {Object} config The config object
1742  */
1743
1744 Roo.bootstrap.Modal = function(config){
1745     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1746     this.addEvents({
1747         // raw events
1748         /**
1749          * @event btnclick
1750          * The raw btnclick event for the button
1751          * @param {Roo.EventObject} e
1752          */
1753         "btnclick" : true
1754     });
1755 };
1756
1757 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1758     
1759     title : 'test dialog',
1760    
1761     buttons : false,
1762     
1763     onRender : function(ct, position)
1764     {
1765         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1766      
1767         if(!this.el){
1768             var cfg = Roo.apply({},  this.getAutoCreate());
1769             cfg.id = Roo.id();
1770             //if(!cfg.name){
1771             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1772             //}
1773             //if (!cfg.name.length) {
1774             //    delete cfg.name;
1775            // }
1776             if (this.cls) {
1777                 cfg.cls += ' ' + this.cls;
1778             }
1779             if (this.style) {
1780                 cfg.style = this.style;
1781             }
1782             this.el = Roo.get(document.body).createChild(cfg, position);
1783         }
1784         //var type = this.el.dom.type;
1785         
1786         if(this.tabIndex !== undefined){
1787             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1788         }
1789         
1790         
1791         
1792         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1793         this.maskEl.enableDisplayMode("block");
1794         this.maskEl.hide();
1795         //this.el.addClass("x-dlg-modal");
1796     
1797         if (this.buttons) {
1798             Roo.each(this.buttons, function(bb) {
1799                 b = Roo.apply({}, bb);
1800                 b.xns = b.xns || Roo.bootstrap;
1801                 b.xtype = b.xtype || 'Button';
1802                 if (typeof(b.listeners) == 'undefined') {
1803                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1804                 }
1805                 
1806                 var btn = Roo.factory(b);
1807                 
1808                 btn.onRender(this.el.select('.modal-footer').first());
1809                 
1810             },this);
1811         }
1812         // render the children.
1813         var nitems = [];
1814         
1815         if(typeof(this.items) != 'undefined'){
1816             var items = this.items;
1817             delete this.items;
1818
1819             for(var i =0;i < items.length;i++) {
1820                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1821             }
1822         }
1823         
1824         this.items = nitems;
1825         this.initEvents();
1826         //this.el.addClass([this.fieldClass, this.cls]);
1827         
1828     },
1829     getAutoCreate : function(){
1830         
1831         
1832         var bdy = {
1833                 cls : 'modal-body',
1834                 html : this.html || ''
1835         };
1836         
1837          
1838         return modal = {
1839             cls: "modal fade",
1840             cn : [
1841                 {
1842                     cls: "modal-dialog",
1843                     cn : [
1844                         {
1845                             cls : "modal-content",
1846                             cn : [
1847                                 {
1848                                     cls : 'modal-header',
1849                                     cn : [
1850                                         {
1851                                             tag: 'button',
1852                                             cls : 'close',
1853                                             html : '&times'
1854                                         },
1855                                         {
1856                                             tag: 'h4',
1857                                             cls : 'modal-title',
1858                                             html : this.title
1859                                         }
1860                                     
1861                                     ]
1862                                 },
1863                                 bdy,
1864                                 {
1865                                     cls : 'modal-footer' 
1866                                 }
1867                                 
1868                                 
1869                             ]
1870                             
1871                         }
1872                     ]
1873                         
1874                 }
1875             ]
1876             
1877             
1878         };
1879           
1880     },
1881     getChildContainer : function() {
1882          
1883          return this.el.select('.modal-body',true).first();
1884         
1885     },
1886     getButtonContainer : function() {
1887          return this.el.select('.modal-footer',true).first();
1888         
1889     },
1890     initEvents : function()
1891     {
1892         this.el.select('.modal-header .close').on('click', this.hide, this);
1893 //        
1894 //        this.addxtype(this);
1895     },
1896     show : function() {
1897         
1898         if (!this.rendered) {
1899             this.render();
1900         }
1901        
1902         this.el.addClass('on');
1903         this.el.removeClass('fade');
1904         this.el.setStyle('display', 'block');
1905         Roo.get(document.body).addClass("x-body-masked");
1906         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1907         this.maskEl.show();
1908         this.el.setStyle('zIndex', '10001');
1909         this.fireEvent('show', this);
1910         
1911         
1912     },
1913     hide : function()
1914     {
1915         Roo.log('Modal hide?!');
1916         this.maskEl.hide();
1917         Roo.get(document.body).removeClass("x-body-masked");
1918         this.el.removeClass('on');
1919         this.el.addClass('fade');
1920         this.el.setStyle('display', 'none');
1921         this.fireEvent('hide', this);
1922     },
1923     onButtonClick: function(btn,e)
1924     {
1925         //Roo.log([a,b,c]);
1926         this.fireEvent('btnclick', btn.name, e);
1927     }
1928 });
1929
1930
1931 Roo.apply(Roo.bootstrap.Modal,  {
1932     /**
1933          * Button config that displays a single OK button
1934          * @type Object
1935          */
1936         OK :  [{
1937             name : 'ok',
1938             weight : 'primary',
1939             html : 'OK'
1940         }], 
1941         /**
1942          * Button config that displays Yes and No buttons
1943          * @type Object
1944          */
1945         YESNO : [
1946             {
1947                 name  : 'no',
1948                 html : 'No'
1949             },
1950             {
1951                 name  :'yes',
1952                 weight : 'primary',
1953                 html : 'Yes'
1954             }
1955         ],
1956         
1957         /**
1958          * Button config that displays OK and Cancel buttons
1959          * @type Object
1960          */
1961         OKCANCEL : [
1962             {
1963                name : 'cancel',
1964                 html : 'Cancel'
1965             },
1966             {
1967                 name : 'ok',
1968                 weight : 'primary',
1969                 html : 'OK'
1970             }
1971         ],
1972         /**
1973          * Button config that displays Yes, No and Cancel buttons
1974          * @type Object
1975          */
1976         YESNOCANCEL : [
1977             {
1978                 name : 'yes',
1979                 weight : 'primary',
1980                 html : 'Yes'
1981             },
1982             {
1983                 name : 'no',
1984                 html : 'No'
1985             },
1986             {
1987                 name : 'cancel',
1988                 html : 'Cancel'
1989             }
1990         ]
1991 });
1992  /*
1993  * - LGPL
1994  *
1995  * navbar
1996  * 
1997  */
1998
1999 /**
2000  * @class Roo.bootstrap.Navbar
2001  * @extends Roo.bootstrap.Component
2002  * Bootstrap Navbar class
2003  * @cfg {Boolean} sidebar has side bar
2004  * @cfg {Boolean} bar is a bar?
2005  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2006  * @cfg {String} brand what is brand
2007  * @cfg {Boolean} inverse is inverted color
2008  * @cfg {String} type (nav | pills | tabs)
2009  * @cfg {Boolean} arrangement stacked | justified
2010  * @cfg {String} align (left | right) alignment
2011  * @cfg {String} brand_href href of the brand
2012  * @cfg {Boolean} main (true|false) main nav bar? default false
2013  *
2014  * 
2015  * @constructor
2016  * Create a new Navbar
2017  * @param {Object} config The config object
2018  */
2019
2020
2021 Roo.bootstrap.Navbar = function(config){
2022     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2023 };
2024
2025 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2026     
2027     sidebar: false,
2028     
2029     bar: false,
2030     brand: '',
2031     inverse: false,
2032     position: '',
2033     align : false,
2034     type: 'nav',
2035     arrangement: '',
2036     brand_href: false,
2037     main : false,
2038     
2039     getAutoCreate : function(){
2040         var cfg = {
2041             cls : 'navbar'
2042         };
2043         
2044         if (this.sidebar === true) {
2045             cfg = {
2046                 tag: 'div',
2047                 cls: 'sidebar-nav'
2048             };
2049             return cfg;
2050         }
2051         
2052         if (this.bar === true) {
2053             cfg = {
2054                 tag: 'nav',
2055                 cls: 'navbar',
2056                 role: 'navigation',
2057                 cn: [
2058                     {
2059                         tag: 'div',
2060                         cls: 'navbar-header',
2061                         cn: [
2062                             {
2063                             tag: 'button',
2064                             type: 'button',
2065                             cls: 'navbar-toggle',
2066                             'data-toggle': 'collapse',
2067                             cn: [
2068                                 {
2069                                     tag: 'span',
2070                                     cls: 'sr-only',
2071                                     html: 'Toggle navigation'
2072                                 },
2073                                 {
2074                                     tag: 'span',
2075                                     cls: 'icon-bar'
2076                                 },
2077                                 {
2078                                     tag: 'span',
2079                                     cls: 'icon-bar'
2080                                 },
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'icon-bar'
2084                                 }
2085                             ]
2086                             }
2087                         ]
2088                     },
2089                     {
2090                     tag: 'div',
2091                     cls: 'collapse navbar-collapse'
2092                     }
2093                 ]
2094             };
2095             
2096             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2097             
2098             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2099                 cfg.cls += ' navbar-' + this.position;
2100                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2101             }
2102             
2103             if (this.brand !== '') {
2104                 cfg.cn[0].cn.push({
2105                     tag: 'a',
2106                     href: this.brand_href ? this.brand_href : '#',
2107                     cls: 'navbar-brand',
2108                     cn: [
2109                     this.brand
2110                     ]
2111                 });
2112             }
2113             
2114             if(this.main){
2115                 cfg.cls += ' main-nav';
2116             }
2117             
2118             
2119             return cfg;
2120         
2121         } else if (this.bar === false) {
2122             
2123         } else {
2124             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2125         }
2126         
2127         cfg.cn = [
2128             {
2129                 cls: 'nav',
2130                 tag : 'ul'
2131             }
2132         ];
2133         
2134         if (['tabs','pills'].indexOf(this.type)!==-1) {
2135             cfg.cn[0].cls += ' nav-' + this.type
2136         } else {
2137             if (this.type!=='nav') {
2138             Roo.log('nav type must be nav/tabs/pills')
2139             }
2140             cfg.cn[0].cls += ' navbar-nav'
2141         }
2142         
2143         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2144             cfg.cn[0].cls += ' nav-' + this.arrangement;
2145         }
2146         
2147         if (this.align === 'right') {
2148             cfg.cn[0].cls += ' navbar-right';
2149         }
2150         if (this.inverse) {
2151             cfg.cls += ' navbar-inverse';
2152             
2153         }
2154         
2155         
2156         return cfg;
2157     },
2158     
2159     initEvents :function ()
2160     {
2161         //Roo.log(this.el.select('.navbar-toggle',true));
2162         this.el.select('.navbar-toggle',true).on('click', function() {
2163            // Roo.log('click');
2164             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2165         }, this);
2166     },
2167     
2168     
2169     getChildContainer : function()
2170     {
2171         if (this.bar === true) {
2172             return this.el.select('.collapse',true).first();
2173         }
2174         console.log(this);
2175         return this.el;
2176     }
2177    
2178 });
2179
2180  
2181
2182  /*
2183  * - LGPL
2184  *
2185  * nav group
2186  * 
2187  */
2188
2189 /**
2190  * @class Roo.bootstrap.NavGroup
2191  * @extends Roo.bootstrap.Component
2192  * Bootstrap NavGroup class
2193  * @cfg {String} align left | right
2194  * @cfg {Boolean} inverse false | true
2195  * @cfg {String} type (nav|pills|tab) default nav
2196  * 
2197  * @constructor
2198  * Create a new nav group
2199  * @param {Object} config The config object
2200  */
2201
2202 Roo.bootstrap.NavGroup = function(config){
2203     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2204 };
2205
2206 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2207     
2208     align: '',
2209     inverse: false,
2210     form: false,
2211     type: 'nav',
2212     
2213     getAutoCreate : function(){
2214         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2215         
2216         cfg = {
2217             tag : 'ul',
2218             cls: 'nav' 
2219         }
2220         
2221         if (['tabs','pills'].indexOf(this.type)!==-1) {
2222             cfg.cls += ' nav-' + this.type
2223         } else {
2224             if (this.type!=='nav') {
2225                 Roo.log('nav type must be nav/tabs/pills')
2226             }
2227             cfg.cls += ' navbar-nav'
2228         }
2229         
2230         if (this.parent().sidebar === true) {
2231             cfg = {
2232                 tag: 'ul',
2233                 cls: 'dashboard-menu'
2234             }
2235             
2236             return cfg;
2237         }
2238         
2239         if (this.form === true) {
2240             cfg = {
2241                 tag: 'form',
2242                 cls: 'navbar-form'
2243             }
2244             
2245             if (this.align === 'right') {
2246                 cfg.cls += ' navbar-right';
2247             } else {
2248                 cfg.cls += ' navbar-left';
2249             }
2250         }
2251         
2252         if (this.align === 'right') {
2253             cfg.cls += ' navbar-right';
2254         }
2255         
2256         if (this.inverse) {
2257             cfg.cls += ' navbar-inverse';
2258             
2259         }
2260         
2261         
2262         return cfg;
2263     }
2264    
2265 });
2266
2267  
2268
2269  /*
2270  * - LGPL
2271  *
2272  * row
2273  * 
2274  */
2275
2276 /**
2277  * @class Roo.bootstrap.Navbar.Item
2278  * @extends Roo.bootstrap.Component
2279  * Bootstrap Navbar.Button class
2280  * @cfg {String} href  link to
2281  * @cfg {String} html content of button
2282  * @cfg {String} badge text inside badge
2283  * @cfg {String} glyphicon name of glyphicon
2284  * @cfg {String} icon name of font awesome icon
2285  * @cfg {Boolena} active Is item active
2286  * @cfg {Boolean} preventDefault (true | false) default false
2287   
2288  * @constructor
2289  * Create a new Navbar Button
2290  * @param {Object} config The config object
2291  */
2292 Roo.bootstrap.Navbar.Item = function(config){
2293     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2294     this.addEvents({
2295         // raw events
2296         /**
2297          * @event click
2298          * The raw click event for the entire grid.
2299          * @param {Roo.EventObject} e
2300          */
2301         "click" : true
2302     });
2303 };
2304
2305 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2306     
2307     href: false,
2308     html: '',
2309     badge: '',
2310     icon: false,
2311     glyphicon: false,
2312     icon: false,
2313     active: false,
2314     preventDefault : false,
2315     
2316     getAutoCreate : function(){
2317         
2318         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2319         
2320         if (this.parent().parent().sidebar === true) {
2321             cfg = {
2322                 tag: 'li',
2323                 cls: '',
2324                 cn: [
2325                     {
2326                         tag: 'p',
2327                         cls: ''
2328                     }
2329                 ]
2330             }
2331             
2332             if (this.html) {
2333                 cfg.cn[0].html = this.html;
2334             }
2335             
2336             if (this.active) {
2337                 this.cls += ' active';
2338             }
2339             
2340             if (this.menu) {
2341                 cfg.cn[0].cls += ' dropdown-toggle';
2342                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2343             }
2344             
2345             if (this.href) {
2346                 cfg.cn[0].tag = 'a',
2347                 cfg.cn[0].href = this.href;
2348             }
2349             
2350             if (this.glyphicon) {
2351                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2352             }
2353             
2354             if (this.icon) {
2355                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2356             }
2357             
2358             return cfg;
2359         }
2360         
2361         cfg = {
2362             tag: 'li',
2363             cls: 'nav-item'
2364         }
2365         
2366         if (this.active) {
2367             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2368         }
2369             
2370         cfg.cn = [
2371             {
2372                 tag: 'p',
2373                 html: 'Text'
2374             }
2375         ];
2376         
2377         if (this.glyphicon) {
2378             if(cfg.html){cfg.html = ' ' + this.html};
2379             cfg.cn=[
2380                 {
2381                     tag: 'span',
2382                     cls: 'glyphicon glyphicon-' + this.glyphicon
2383                 }
2384             ];
2385         }
2386         
2387         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2388         
2389         if (this.menu) {
2390             cfg.cn[0].tag='a';
2391             cfg.cn[0].href='#';
2392             cfg.cn[0].html += " <span class='caret'></span>";
2393         //}else if (!this.href) {
2394         //    cfg.cn[0].tag='p';
2395         //    cfg.cn[0].cls='navbar-text';
2396         } else {
2397             cfg.cn[0].tag='a';
2398             cfg.cn[0].href=this.href||'#';
2399             cfg.cn[0].html=this.html;
2400         }
2401         
2402         if (this.badge !== '') {
2403             
2404             cfg.cn[0].cn=[
2405                 cfg.cn[0].html + ' ',
2406                 {
2407                     tag: 'span',
2408                     cls: 'badge',
2409                     html: this.badge
2410                 }
2411             ];
2412             cfg.cn[0].html=''
2413         }
2414          
2415         if (this.icon) {
2416             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2417         }
2418         
2419         return cfg;
2420     },
2421     initEvents: function() {
2422        // Roo.log('init events?');
2423        // Roo.log(this.el.dom);
2424         this.el.select('a',true).on('click', this.onClick, this);
2425     },
2426     
2427     onClick : function(e)
2428     {
2429         if(this.preventDefault){
2430             e.preventDefault();
2431         }
2432         
2433         if(this.fireEvent('click', this, e) === false){
2434             return;
2435         };
2436         
2437         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2438             this.onTabsClick(e);
2439         } 
2440     },
2441     
2442     onTabsClick : function(e)
2443     {
2444         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2445             v.removeClass('active');
2446         })
2447
2448         this.el.addClass('active');
2449
2450         if(this.href && this.href.substring(0,1) == '#'){
2451             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2452
2453             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2454                 v.removeClass('active');
2455             });
2456
2457             tab.addClass('active');
2458         }
2459     }
2460    
2461 });
2462  
2463
2464  /*
2465  * - LGPL
2466  *
2467  * row
2468  * 
2469  */
2470
2471 /**
2472  * @class Roo.bootstrap.Row
2473  * @extends Roo.bootstrap.Component
2474  * Bootstrap Row class (contains columns...)
2475  * 
2476  * @constructor
2477  * Create a new Row
2478  * @param {Object} config The config object
2479  */
2480
2481 Roo.bootstrap.Row = function(config){
2482     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2483 };
2484
2485 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2486     
2487     getAutoCreate : function(){
2488        return {
2489             cls: 'row clearfix'
2490        };
2491     }
2492     
2493     
2494 });
2495
2496  
2497
2498  /*
2499  * - LGPL
2500  *
2501  * element
2502  * 
2503  */
2504
2505 /**
2506  * @class Roo.bootstrap.Element
2507  * @extends Roo.bootstrap.Component
2508  * Bootstrap Element class
2509  * @cfg {String} html contents of the element
2510  * @cfg {String} tag tag of the element
2511  * @cfg {String} cls class of the element
2512  * 
2513  * @constructor
2514  * Create a new Element
2515  * @param {Object} config The config object
2516  */
2517
2518 Roo.bootstrap.Element = function(config){
2519     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2520 };
2521
2522 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2523     
2524     tag: 'div',
2525     cls: '',
2526     html: '',
2527      
2528     
2529     getAutoCreate : function(){
2530         
2531         var cfg = {
2532             tag: this.tag,
2533             cls: this.cls,
2534             html: this.html
2535         }
2536         
2537         
2538         
2539         return cfg;
2540     }
2541    
2542 });
2543
2544  
2545
2546  /*
2547  * - LGPL
2548  *
2549  * pagination
2550  * 
2551  */
2552
2553 /**
2554  * @class Roo.bootstrap.Pagination
2555  * @extends Roo.bootstrap.Component
2556  * Bootstrap Pagination class
2557  * @cfg {String} size xs | sm | md | lg
2558  * @cfg {Boolean} inverse false | true
2559  * 
2560  * @constructor
2561  * Create a new Pagination
2562  * @param {Object} config The config object
2563  */
2564
2565 Roo.bootstrap.Pagination = function(config){
2566     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2567 };
2568
2569 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2570     
2571     cls: false,
2572     size: false,
2573     inverse: false,
2574     
2575     getAutoCreate : function(){
2576         var cfg = {
2577             tag: 'ul',
2578                 cls: 'pagination'
2579         };
2580         if (this.inverse) {
2581             cfg.cls += ' inverse';
2582         }
2583         if (this.html) {
2584             cfg.html=this.html;
2585         }
2586         if (this.cls) {
2587             cfg.cls += " " + this.cls;
2588         }
2589         return cfg;
2590     }
2591    
2592 });
2593
2594  
2595
2596  /*
2597  * - LGPL
2598  *
2599  * Pagination item
2600  * 
2601  */
2602
2603
2604 /**
2605  * @class Roo.bootstrap.PaginationItem
2606  * @extends Roo.bootstrap.Component
2607  * Bootstrap PaginationItem class
2608  * @cfg {String} html text
2609  * @cfg {String} href the link
2610  * @cfg {Boolean} preventDefault (true | false) default true
2611  * @cfg {Boolean} active (true | false) default false
2612  * 
2613  * 
2614  * @constructor
2615  * Create a new PaginationItem
2616  * @param {Object} config The config object
2617  */
2618
2619
2620 Roo.bootstrap.PaginationItem = function(config){
2621     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2622     this.addEvents({
2623         // raw events
2624         /**
2625          * @event click
2626          * The raw click event for the entire grid.
2627          * @param {Roo.EventObject} e
2628          */
2629         "click" : true
2630     });
2631 };
2632
2633 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2634     
2635     href : false,
2636     html : false,
2637     preventDefault: true,
2638     active : false,
2639     cls : false,
2640     
2641     getAutoCreate : function(){
2642         var cfg= {
2643             tag: 'li',
2644             cn: [
2645                 {
2646                     tag : 'a',
2647                     href : this.href ? this.href : '#',
2648                     html : this.html ? this.html : ''
2649                 }
2650             ]
2651         };
2652         
2653         if(this.cls){
2654             cfg.cls = this.cls;
2655         }
2656         
2657         if(this.active){
2658             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2659         }
2660         
2661         return cfg;
2662     },
2663     
2664     initEvents: function() {
2665         
2666         this.el.on('click', this.onClick, this);
2667         
2668     },
2669     onClick : function(e)
2670     {
2671         Roo.log('PaginationItem on click ');
2672         if(this.preventDefault){
2673             e.preventDefault();
2674         }
2675         
2676         this.fireEvent('click', this, e);
2677     }
2678    
2679 });
2680
2681  
2682
2683  /*
2684  * - LGPL
2685  *
2686  * slider
2687  * 
2688  */
2689
2690
2691 /**
2692  * @class Roo.bootstrap.Slider
2693  * @extends Roo.bootstrap.Component
2694  * Bootstrap Slider class
2695  *    
2696  * @constructor
2697  * Create a new Slider
2698  * @param {Object} config The config object
2699  */
2700
2701 Roo.bootstrap.Slider = function(config){
2702     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2703 };
2704
2705 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2706     
2707     getAutoCreate : function(){
2708         
2709         var cfg = {
2710             tag: 'div',
2711             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2712             cn: [
2713                 {
2714                     tag: 'a',
2715                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2716                 }
2717             ]
2718         }
2719         
2720         return cfg;
2721     }
2722    
2723 });
2724
2725  /*
2726  * - LGPL
2727  *
2728  * table
2729  * 
2730  */
2731
2732 /**
2733  * @class Roo.bootstrap.Table
2734  * @extends Roo.bootstrap.Component
2735  * Bootstrap Table class
2736  * @cfg {String} cls table class
2737  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2738  * @cfg {String} bgcolor Specifies the background color for a table
2739  * @cfg {Number} border Specifies whether the table cells should have borders or not
2740  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2741  * @cfg {Number} cellspacing Specifies the space between cells
2742  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2743  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2744  * @cfg {String} sortable Specifies that the table should be sortable
2745  * @cfg {String} summary Specifies a summary of the content of a table
2746  * @cfg {Number} width Specifies the width of a table
2747  * 
2748  * @cfg {boolean} striped Should the rows be alternative striped
2749  * @cfg {boolean} bordered Add borders to the table
2750  * @cfg {boolean} hover Add hover highlighting
2751  * @cfg {boolean} condensed Format condensed
2752  * @cfg {boolean} responsive Format condensed
2753  *
2754  
2755  
2756  * 
2757  * @constructor
2758  * Create a new Table
2759  * @param {Object} config The config object
2760  */
2761
2762 Roo.bootstrap.Table = function(config){
2763     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2764     
2765     if (this.sm) {
2766         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2767         this.sm = this.selModel;
2768         this.sm.xmodule = this.xmodule || false;
2769     }
2770     if (this.cm && typeof(this.cm.config) == 'undefined') {
2771         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2772         this.cm = this.colModel;
2773         this.cm.xmodule = this.xmodule || false;
2774     }
2775     if (this.store) {
2776         this.store= Roo.factory(this.store, Roo.data);
2777         this.ds = this.store;
2778         this.ds.xmodule = this.xmodule || false;
2779          
2780     }
2781 };
2782
2783 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2784     
2785     cls: false,
2786     align: false,
2787     bgcolor: false,
2788     border: false,
2789     cellpadding: false,
2790     cellspacing: false,
2791     frame: false,
2792     rules: false,
2793     sortable: false,
2794     summary: false,
2795     width: false,
2796     striped : false,
2797     bordered: false,
2798     hover:  false,
2799     condensed : false,
2800     responsive : false,
2801     sm : false,
2802     cm : false,
2803     store : false,
2804     
2805     getAutoCreate : function(){
2806         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2807         
2808         cfg = {
2809             tag: 'table',
2810             cls : 'table',
2811             cn : []
2812         }
2813             
2814         if (this.striped) {
2815             cfg.cls += ' table-striped';
2816         }
2817         if (this.hover) {
2818             cfg.cls += ' table-hover';
2819         }
2820         if (this.bordered) {
2821             cfg.cls += ' table-bordered';
2822         }
2823         if (this.condensed) {
2824             cfg.cls += ' table-condensed';
2825         }
2826         if (this.responsive) {
2827             cfg.cls += ' table-responsive';
2828         }
2829         
2830           
2831         
2832         
2833         if (this.cls) {
2834             cfg.cls+=  ' ' +this.cls;
2835         }
2836         
2837         // this lot should be simplifed...
2838         
2839         if (this.align) {
2840             cfg.align=this.align;
2841         }
2842         if (this.bgcolor) {
2843             cfg.bgcolor=this.bgcolor;
2844         }
2845         if (this.border) {
2846             cfg.border=this.border;
2847         }
2848         if (this.cellpadding) {
2849             cfg.cellpadding=this.cellpadding;
2850         }
2851         if (this.cellspacing) {
2852             cfg.cellspacing=this.cellspacing;
2853         }
2854         if (this.frame) {
2855             cfg.frame=this.frame;
2856         }
2857         if (this.rules) {
2858             cfg.rules=this.rules;
2859         }
2860         if (this.sortable) {
2861             cfg.sortable=this.sortable;
2862         }
2863         if (this.summary) {
2864             cfg.summary=this.summary;
2865         }
2866         if (this.width) {
2867             cfg.width=this.width;
2868         }
2869         
2870         if(this.store || this.cm){
2871             cfg.cn.push(this.renderHeader());
2872             cfg.cn.push(this.renderBody());
2873             cfg.cn.push(this.renderFooter());
2874             
2875             cfg.cls+=  ' TableGrid';
2876         }
2877         
2878         return cfg;
2879     },
2880 //    
2881 //    initTableGrid : function()
2882 //    {
2883 //        var cfg = {};
2884 //        
2885 //        var header = {
2886 //            tag: 'thead',
2887 //            cn : []
2888 //        };
2889 //        
2890 //        var cm = this.cm;
2891 //        
2892 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2893 //            header.cn.push({
2894 //                tag: 'th',
2895 //                html: cm.getColumnHeader(i)
2896 //            })
2897 //        }
2898 //        
2899 //        cfg.push(header);
2900 //        
2901 //        return cfg;
2902 //        
2903 //        
2904 //    },
2905     
2906     initEvents : function()
2907     {   
2908         if(!this.store || !this.cm){
2909             return;
2910         }
2911         
2912         Roo.log('initEvents with ds!!!!');
2913         
2914 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2915 //        this.maskEl.enableDisplayMode("block");
2916 //        this.maskEl.show();
2917         
2918         this.store.on('load', this.onLoad, this);
2919         this.store.on('beforeload', this.onBeforeLoad, this);
2920         
2921         this.store.load();
2922         
2923         
2924         
2925     },
2926     
2927     renderHeader : function()
2928     {
2929         var header = {
2930             tag: 'thead',
2931             cn : []
2932         };
2933         
2934         var cm = this.cm;
2935         
2936         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2937             header.cn.push({
2938                 tag: 'th',
2939                 html: cm.getColumnHeader(i)
2940             })
2941         }
2942         
2943         return header;
2944     },
2945     
2946     renderBody : function()
2947     {
2948         var body = {
2949             tag: 'tbody',
2950             cn : []
2951         };
2952         
2953         return body;
2954     },
2955     
2956     renderFooter : function()
2957     {
2958         var footer = {
2959             tag: 'tfoot',
2960             cn : []
2961         };
2962         
2963         return footer;
2964     },
2965     
2966     onLoad : function()
2967     {
2968         Roo.log('ds onload');
2969         
2970         var cm = this.cm;
2971         
2972         var tbody = this.el.select('tbody', true).first();
2973         
2974         var renders = [];
2975         
2976         if(this.store.getCount() > 0){
2977             this.store.data.each(function(d){
2978                 var row = {
2979                     tag : 'tr',
2980                     cn : []
2981                 };
2982                 
2983                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2984                     var renderer = cm.getRenderer(i);
2985                     var value = '';
2986                     var id = Roo.id();
2987                     
2988                     if(typeof(renderer) !== 'undefined'){
2989                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
2990                     }
2991                     
2992                     if(typeof(value) === 'object'){
2993                         renders.push({
2994                             id : id,
2995                             cfg : value 
2996                         })
2997                     }
2998                     
2999                     row.cn.push({
3000                         tag: 'td',
3001                         id: id,
3002                         html: (typeof(value) === 'object') ? '' : value
3003                     })
3004                    
3005                 }
3006                 
3007                 tbody.createChild(row);
3008                 
3009             });
3010         }
3011         
3012         if(renders.length){
3013             Roo.each(renders, function(r){
3014                 r.cfg.render(Roo.get(r.id));
3015             })
3016         }
3017 //        
3018 //        if(this.loadMask){
3019 //            this.maskEl.hide();
3020 //        }
3021     },
3022     
3023     onBeforeLoad : function()
3024     {
3025         Roo.log('ds onBeforeLoad');
3026         
3027         this.clear();
3028         
3029 //        if(this.loadMask){
3030 //            this.maskEl.show();
3031 //        }
3032     },
3033     
3034     clear : function()
3035     {
3036         this.el.select('tbody', true).first().dom.innerHTML = '';
3037     },
3038     
3039     getSelectionModel : function(){
3040         if(!this.selModel){
3041             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3042         }
3043         return this.selModel;
3044     }
3045    
3046 });
3047
3048  
3049
3050  /*
3051  * - LGPL
3052  *
3053  * table cell
3054  * 
3055  */
3056
3057 /**
3058  * @class Roo.bootstrap.TableCell
3059  * @extends Roo.bootstrap.Component
3060  * Bootstrap TableCell class
3061  * @cfg {String} html cell contain text
3062  * @cfg {String} cls cell class
3063  * @cfg {String} tag cell tag (td|th) default td
3064  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3065  * @cfg {String} align Aligns the content in a cell
3066  * @cfg {String} axis Categorizes cells
3067  * @cfg {String} bgcolor Specifies the background color of a cell
3068  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3069  * @cfg {Number} colspan Specifies the number of columns a cell should span
3070  * @cfg {String} headers Specifies one or more header cells a cell is related to
3071  * @cfg {Number} height Sets the height of a cell
3072  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3073  * @cfg {Number} rowspan Sets the number of rows a cell should span
3074  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3075  * @cfg {String} valign Vertical aligns the content in a cell
3076  * @cfg {Number} width Specifies the width of a cell
3077  * 
3078  * @constructor
3079  * Create a new TableCell
3080  * @param {Object} config The config object
3081  */
3082
3083 Roo.bootstrap.TableCell = function(config){
3084     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3085 };
3086
3087 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3088     
3089     html: false,
3090     cls: false,
3091     tag: false,
3092     abbr: false,
3093     align: false,
3094     axis: false,
3095     bgcolor: false,
3096     charoff: false,
3097     colspan: false,
3098     headers: false,
3099     height: false,
3100     nowrap: false,
3101     rowspan: false,
3102     scope: false,
3103     valign: false,
3104     width: false,
3105     
3106     
3107     getAutoCreate : function(){
3108         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3109         
3110         cfg = {
3111             tag: 'td'
3112         }
3113         
3114         if(this.tag){
3115             cfg.tag = this.tag;
3116         }
3117         
3118         if (this.html) {
3119             cfg.html=this.html
3120         }
3121         if (this.cls) {
3122             cfg.cls=this.cls
3123         }
3124         if (this.abbr) {
3125             cfg.abbr=this.abbr
3126         }
3127         if (this.align) {
3128             cfg.align=this.align
3129         }
3130         if (this.axis) {
3131             cfg.axis=this.axis
3132         }
3133         if (this.bgcolor) {
3134             cfg.bgcolor=this.bgcolor
3135         }
3136         if (this.charoff) {
3137             cfg.charoff=this.charoff
3138         }
3139         if (this.colspan) {
3140             cfg.colspan=this.colspan
3141         }
3142         if (this.headers) {
3143             cfg.headers=this.headers
3144         }
3145         if (this.height) {
3146             cfg.height=this.height
3147         }
3148         if (this.nowrap) {
3149             cfg.nowrap=this.nowrap
3150         }
3151         if (this.rowspan) {
3152             cfg.rowspan=this.rowspan
3153         }
3154         if (this.scope) {
3155             cfg.scope=this.scope
3156         }
3157         if (this.valign) {
3158             cfg.valign=this.valign
3159         }
3160         if (this.width) {
3161             cfg.width=this.width
3162         }
3163         
3164         
3165         return cfg;
3166     }
3167    
3168 });
3169
3170  
3171
3172  /*
3173  * - LGPL
3174  *
3175  * table row
3176  * 
3177  */
3178
3179 /**
3180  * @class Roo.bootstrap.TableRow
3181  * @extends Roo.bootstrap.Component
3182  * Bootstrap TableRow class
3183  * @cfg {String} cls row class
3184  * @cfg {String} align Aligns the content in a table row
3185  * @cfg {String} bgcolor Specifies a background color for a table row
3186  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3187  * @cfg {String} valign Vertical aligns the content in a table row
3188  * 
3189  * @constructor
3190  * Create a new TableRow
3191  * @param {Object} config The config object
3192  */
3193
3194 Roo.bootstrap.TableRow = function(config){
3195     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3196 };
3197
3198 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3199     
3200     cls: false,
3201     align: false,
3202     bgcolor: false,
3203     charoff: false,
3204     valign: false,
3205     
3206     getAutoCreate : function(){
3207         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3208         
3209         cfg = {
3210             tag: 'tr'
3211         }
3212             
3213         if(this.cls){
3214             cfg.cls = this.cls;
3215         }
3216         if(this.align){
3217             cfg.align = this.align;
3218         }
3219         if(this.bgcolor){
3220             cfg.bgcolor = this.bgcolor;
3221         }
3222         if(this.charoff){
3223             cfg.charoff = this.charoff;
3224         }
3225         if(this.valign){
3226             cfg.valign = this.valign;
3227         }
3228         
3229         return cfg;
3230     }
3231    
3232 });
3233
3234  
3235
3236  /*
3237  * - LGPL
3238  *
3239  * table body
3240  * 
3241  */
3242
3243 /**
3244  * @class Roo.bootstrap.TableBody
3245  * @extends Roo.bootstrap.Component
3246  * Bootstrap TableBody class
3247  * @cfg {String} cls element class
3248  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3249  * @cfg {String} align Aligns the content inside the element
3250  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3251  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3252  * 
3253  * @constructor
3254  * Create a new TableBody
3255  * @param {Object} config The config object
3256  */
3257
3258 Roo.bootstrap.TableBody = function(config){
3259     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3260 };
3261
3262 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3263     
3264     cls: false,
3265     tag: false,
3266     align: false,
3267     charoff: false,
3268     valign: false,
3269     
3270     getAutoCreate : function(){
3271         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3272         
3273         cfg = {
3274             tag: 'tbody'
3275         }
3276             
3277         if (this.cls) {
3278             cfg.cls=this.cls
3279         }
3280         if(this.tag){
3281             cfg.tag = this.tag;
3282         }
3283         
3284         if(this.align){
3285             cfg.align = this.align;
3286         }
3287         if(this.charoff){
3288             cfg.charoff = this.charoff;
3289         }
3290         if(this.valign){
3291             cfg.valign = this.valign;
3292         }
3293         
3294         return cfg;
3295     }
3296     
3297     
3298 //    initEvents : function()
3299 //    {
3300 //        
3301 //        if(!this.store){
3302 //            return;
3303 //        }
3304 //        
3305 //        this.store = Roo.factory(this.store, Roo.data);
3306 //        this.store.on('load', this.onLoad, this);
3307 //        
3308 //        this.store.load();
3309 //        
3310 //    },
3311 //    
3312 //    onLoad: function () 
3313 //    {   
3314 //        this.fireEvent('load', this);
3315 //    }
3316 //    
3317 //   
3318 });
3319
3320  
3321
3322  /*
3323  * Based on:
3324  * Ext JS Library 1.1.1
3325  * Copyright(c) 2006-2007, Ext JS, LLC.
3326  *
3327  * Originally Released Under LGPL - original licence link has changed is not relivant.
3328  *
3329  * Fork - LGPL
3330  * <script type="text/javascript">
3331  */
3332
3333 // as we use this in bootstrap.
3334 Roo.namespace('Roo.form');
3335  /**
3336  * @class Roo.form.Action
3337  * Internal Class used to handle form actions
3338  * @constructor
3339  * @param {Roo.form.BasicForm} el The form element or its id
3340  * @param {Object} config Configuration options
3341  */
3342
3343  
3344  
3345 // define the action interface
3346 Roo.form.Action = function(form, options){
3347     this.form = form;
3348     this.options = options || {};
3349 };
3350 /**
3351  * Client Validation Failed
3352  * @const 
3353  */
3354 Roo.form.Action.CLIENT_INVALID = 'client';
3355 /**
3356  * Server Validation Failed
3357  * @const 
3358  */
3359 Roo.form.Action.SERVER_INVALID = 'server';
3360  /**
3361  * Connect to Server Failed
3362  * @const 
3363  */
3364 Roo.form.Action.CONNECT_FAILURE = 'connect';
3365 /**
3366  * Reading Data from Server Failed
3367  * @const 
3368  */
3369 Roo.form.Action.LOAD_FAILURE = 'load';
3370
3371 Roo.form.Action.prototype = {
3372     type : 'default',
3373     failureType : undefined,
3374     response : undefined,
3375     result : undefined,
3376
3377     // interface method
3378     run : function(options){
3379
3380     },
3381
3382     // interface method
3383     success : function(response){
3384
3385     },
3386
3387     // interface method
3388     handleResponse : function(response){
3389
3390     },
3391
3392     // default connection failure
3393     failure : function(response){
3394         
3395         this.response = response;
3396         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3397         this.form.afterAction(this, false);
3398     },
3399
3400     processResponse : function(response){
3401         this.response = response;
3402         if(!response.responseText){
3403             return true;
3404         }
3405         this.result = this.handleResponse(response);
3406         return this.result;
3407     },
3408
3409     // utility functions used internally
3410     getUrl : function(appendParams){
3411         var url = this.options.url || this.form.url || this.form.el.dom.action;
3412         if(appendParams){
3413             var p = this.getParams();
3414             if(p){
3415                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3416             }
3417         }
3418         return url;
3419     },
3420
3421     getMethod : function(){
3422         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3423     },
3424
3425     getParams : function(){
3426         var bp = this.form.baseParams;
3427         var p = this.options.params;
3428         if(p){
3429             if(typeof p == "object"){
3430                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3431             }else if(typeof p == 'string' && bp){
3432                 p += '&' + Roo.urlEncode(bp);
3433             }
3434         }else if(bp){
3435             p = Roo.urlEncode(bp);
3436         }
3437         return p;
3438     },
3439
3440     createCallback : function(){
3441         return {
3442             success: this.success,
3443             failure: this.failure,
3444             scope: this,
3445             timeout: (this.form.timeout*1000),
3446             upload: this.form.fileUpload ? this.success : undefined
3447         };
3448     }
3449 };
3450
3451 Roo.form.Action.Submit = function(form, options){
3452     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3453 };
3454
3455 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3456     type : 'submit',
3457
3458     haveProgress : false,
3459     uploadComplete : false,
3460     
3461     // uploadProgress indicator.
3462     uploadProgress : function()
3463     {
3464         if (!this.form.progressUrl) {
3465             return;
3466         }
3467         
3468         if (!this.haveProgress) {
3469             Roo.MessageBox.progress("Uploading", "Uploading");
3470         }
3471         if (this.uploadComplete) {
3472            Roo.MessageBox.hide();
3473            return;
3474         }
3475         
3476         this.haveProgress = true;
3477    
3478         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3479         
3480         var c = new Roo.data.Connection();
3481         c.request({
3482             url : this.form.progressUrl,
3483             params: {
3484                 id : uid
3485             },
3486             method: 'GET',
3487             success : function(req){
3488                //console.log(data);
3489                 var rdata = false;
3490                 var edata;
3491                 try  {
3492                    rdata = Roo.decode(req.responseText)
3493                 } catch (e) {
3494                     Roo.log("Invalid data from server..");
3495                     Roo.log(edata);
3496                     return;
3497                 }
3498                 if (!rdata || !rdata.success) {
3499                     Roo.log(rdata);
3500                     Roo.MessageBox.alert(Roo.encode(rdata));
3501                     return;
3502                 }
3503                 var data = rdata.data;
3504                 
3505                 if (this.uploadComplete) {
3506                    Roo.MessageBox.hide();
3507                    return;
3508                 }
3509                    
3510                 if (data){
3511                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3512                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3513                     );
3514                 }
3515                 this.uploadProgress.defer(2000,this);
3516             },
3517        
3518             failure: function(data) {
3519                 Roo.log('progress url failed ');
3520                 Roo.log(data);
3521             },
3522             scope : this
3523         });
3524            
3525     },
3526     
3527     
3528     run : function()
3529     {
3530         // run get Values on the form, so it syncs any secondary forms.
3531         this.form.getValues();
3532         
3533         var o = this.options;
3534         var method = this.getMethod();
3535         var isPost = method == 'POST';
3536         if(o.clientValidation === false || this.form.isValid()){
3537             
3538             if (this.form.progressUrl) {
3539                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3540                     (new Date() * 1) + '' + Math.random());
3541                     
3542             } 
3543             
3544             
3545             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3546                 form:this.form.el.dom,
3547                 url:this.getUrl(!isPost),
3548                 method: method,
3549                 params:isPost ? this.getParams() : null,
3550                 isUpload: this.form.fileUpload
3551             }));
3552             
3553             this.uploadProgress();
3554
3555         }else if (o.clientValidation !== false){ // client validation failed
3556             this.failureType = Roo.form.Action.CLIENT_INVALID;
3557             this.form.afterAction(this, false);
3558         }
3559     },
3560
3561     success : function(response)
3562     {
3563         this.uploadComplete= true;
3564         if (this.haveProgress) {
3565             Roo.MessageBox.hide();
3566         }
3567         
3568         
3569         var result = this.processResponse(response);
3570         if(result === true || result.success){
3571             this.form.afterAction(this, true);
3572             return;
3573         }
3574         if(result.errors){
3575             this.form.markInvalid(result.errors);
3576             this.failureType = Roo.form.Action.SERVER_INVALID;
3577         }
3578         this.form.afterAction(this, false);
3579     },
3580     failure : function(response)
3581     {
3582         this.uploadComplete= true;
3583         if (this.haveProgress) {
3584             Roo.MessageBox.hide();
3585         }
3586         
3587         this.response = response;
3588         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3589         this.form.afterAction(this, false);
3590     },
3591     
3592     handleResponse : function(response){
3593         if(this.form.errorReader){
3594             var rs = this.form.errorReader.read(response);
3595             var errors = [];
3596             if(rs.records){
3597                 for(var i = 0, len = rs.records.length; i < len; i++) {
3598                     var r = rs.records[i];
3599                     errors[i] = r.data;
3600                 }
3601             }
3602             if(errors.length < 1){
3603                 errors = null;
3604             }
3605             return {
3606                 success : rs.success,
3607                 errors : errors
3608             };
3609         }
3610         var ret = false;
3611         try {
3612             ret = Roo.decode(response.responseText);
3613         } catch (e) {
3614             ret = {
3615                 success: false,
3616                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3617                 errors : []
3618             };
3619         }
3620         return ret;
3621         
3622     }
3623 });
3624
3625
3626 Roo.form.Action.Load = function(form, options){
3627     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3628     this.reader = this.form.reader;
3629 };
3630
3631 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3632     type : 'load',
3633
3634     run : function(){
3635         
3636         Roo.Ajax.request(Roo.apply(
3637                 this.createCallback(), {
3638                     method:this.getMethod(),
3639                     url:this.getUrl(false),
3640                     params:this.getParams()
3641         }));
3642     },
3643
3644     success : function(response){
3645         
3646         var result = this.processResponse(response);
3647         if(result === true || !result.success || !result.data){
3648             this.failureType = Roo.form.Action.LOAD_FAILURE;
3649             this.form.afterAction(this, false);
3650             return;
3651         }
3652         this.form.clearInvalid();
3653         this.form.setValues(result.data);
3654         this.form.afterAction(this, true);
3655     },
3656
3657     handleResponse : function(response){
3658         if(this.form.reader){
3659             var rs = this.form.reader.read(response);
3660             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3661             return {
3662                 success : rs.success,
3663                 data : data
3664             };
3665         }
3666         return Roo.decode(response.responseText);
3667     }
3668 });
3669
3670 Roo.form.Action.ACTION_TYPES = {
3671     'load' : Roo.form.Action.Load,
3672     'submit' : Roo.form.Action.Submit
3673 };/*
3674  * - LGPL
3675  *
3676  * form
3677  * 
3678  */
3679
3680 /**
3681  * @class Roo.bootstrap.Form
3682  * @extends Roo.bootstrap.Component
3683  * Bootstrap Form class
3684  * @cfg {String} method  GET | POST (default POST)
3685  * @cfg {String} labelAlign top | left (default top)
3686   * @cfg {String} align left  | right - for navbars
3687
3688  * 
3689  * @constructor
3690  * Create a new Form
3691  * @param {Object} config The config object
3692  */
3693
3694
3695 Roo.bootstrap.Form = function(config){
3696     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3697     this.addEvents({
3698         /**
3699          * @event clientvalidation
3700          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3701          * @param {Form} this
3702          * @param {Boolean} valid true if the form has passed client-side validation
3703          */
3704         clientvalidation: true,
3705         /**
3706          * @event beforeaction
3707          * Fires before any action is performed. Return false to cancel the action.
3708          * @param {Form} this
3709          * @param {Action} action The action to be performed
3710          */
3711         beforeaction: true,
3712         /**
3713          * @event actionfailed
3714          * Fires when an action fails.
3715          * @param {Form} this
3716          * @param {Action} action The action that failed
3717          */
3718         actionfailed : true,
3719         /**
3720          * @event actioncomplete
3721          * Fires when an action is completed.
3722          * @param {Form} this
3723          * @param {Action} action The action that completed
3724          */
3725         actioncomplete : true
3726     });
3727     
3728 };
3729
3730 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3731       
3732      /**
3733      * @cfg {String} method
3734      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3735      */
3736     method : 'POST',
3737     /**
3738      * @cfg {String} url
3739      * The URL to use for form actions if one isn't supplied in the action options.
3740      */
3741     /**
3742      * @cfg {Boolean} fileUpload
3743      * Set to true if this form is a file upload.
3744      */
3745      
3746     /**
3747      * @cfg {Object} baseParams
3748      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3749      */
3750       
3751     /**
3752      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3753      */
3754     timeout: 30,
3755     /**
3756      * @cfg {Sting} align (left|right) for navbar forms
3757      */
3758     align : 'left',
3759
3760     // private
3761     activeAction : null,
3762  
3763     /**
3764      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3765      * element by passing it or its id or mask the form itself by passing in true.
3766      * @type Mixed
3767      */
3768     waitMsgTarget : false,
3769     
3770      
3771     
3772     /**
3773      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3774      * element by passing it or its id or mask the form itself by passing in true.
3775      * @type Mixed
3776      */
3777     
3778     getAutoCreate : function(){
3779         
3780         var cfg = {
3781             tag: 'form',
3782             method : this.method || 'POST',
3783             id : this.id || Roo.id(),
3784             cls : ''
3785         }
3786         if (this.parent().xtype.match(/^Nav/)) {
3787             cfg.cls = 'navbar-form navbar-' + this.align;
3788             
3789         }
3790         
3791         if (this.labelAlign == 'left' ) {
3792             cfg.cls += ' form-horizontal';
3793         }
3794         
3795         
3796         return cfg;
3797     },
3798     initEvents : function()
3799     {
3800         this.el.on('submit', this.onSubmit, this);
3801         
3802         
3803     },
3804     // private
3805     onSubmit : function(e){
3806         e.stopEvent();
3807     },
3808     
3809      /**
3810      * Returns true if client-side validation on the form is successful.
3811      * @return Boolean
3812      */
3813     isValid : function(){
3814         var items = this.getItems();
3815         var valid = true;
3816         items.each(function(f){
3817            if(!f.validate()){
3818                valid = false;
3819                
3820            }
3821         });
3822         return valid;
3823     },
3824     /**
3825      * Returns true if any fields in this form have changed since their original load.
3826      * @return Boolean
3827      */
3828     isDirty : function(){
3829         var dirty = false;
3830         var items = this.getItems();
3831         items.each(function(f){
3832            if(f.isDirty()){
3833                dirty = true;
3834                return false;
3835            }
3836            return true;
3837         });
3838         return dirty;
3839     },
3840      /**
3841      * Performs a predefined action (submit or load) or custom actions you define on this form.
3842      * @param {String} actionName The name of the action type
3843      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3844      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3845      * accept other config options):
3846      * <pre>
3847 Property          Type             Description
3848 ----------------  ---------------  ----------------------------------------------------------------------------------
3849 url               String           The url for the action (defaults to the form's url)
3850 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3851 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3852 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3853                                    validate the form on the client (defaults to false)
3854      * </pre>
3855      * @return {BasicForm} this
3856      */
3857     doAction : function(action, options){
3858         if(typeof action == 'string'){
3859             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3860         }
3861         if(this.fireEvent('beforeaction', this, action) !== false){
3862             this.beforeAction(action);
3863             action.run.defer(100, action);
3864         }
3865         return this;
3866     },
3867     
3868     // private
3869     beforeAction : function(action){
3870         var o = action.options;
3871         
3872         // not really supported yet.. ??
3873         
3874         //if(this.waitMsgTarget === true){
3875             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3876         //}else if(this.waitMsgTarget){
3877         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3878         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3879         //}else {
3880         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3881        // }
3882          
3883     },
3884
3885     // private
3886     afterAction : function(action, success){
3887         this.activeAction = null;
3888         var o = action.options;
3889         
3890         //if(this.waitMsgTarget === true){
3891             this.el.unmask();
3892         //}else if(this.waitMsgTarget){
3893         //    this.waitMsgTarget.unmask();
3894         //}else{
3895         //    Roo.MessageBox.updateProgress(1);
3896         //    Roo.MessageBox.hide();
3897        // }
3898         // 
3899         if(success){
3900             if(o.reset){
3901                 this.reset();
3902             }
3903             Roo.callback(o.success, o.scope, [this, action]);
3904             this.fireEvent('actioncomplete', this, action);
3905             
3906         }else{
3907             
3908             // failure condition..
3909             // we have a scenario where updates need confirming.
3910             // eg. if a locking scenario exists..
3911             // we look for { errors : { needs_confirm : true }} in the response.
3912             if (
3913                 (typeof(action.result) != 'undefined')  &&
3914                 (typeof(action.result.errors) != 'undefined')  &&
3915                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3916            ){
3917                 var _t = this;
3918                 Roo.log("not supported yet");
3919                  /*
3920                 
3921                 Roo.MessageBox.confirm(
3922                     "Change requires confirmation",
3923                     action.result.errorMsg,
3924                     function(r) {
3925                         if (r != 'yes') {
3926                             return;
3927                         }
3928                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3929                     }
3930                     
3931                 );
3932                 */
3933                 
3934                 
3935                 return;
3936             }
3937             
3938             Roo.callback(o.failure, o.scope, [this, action]);
3939             // show an error message if no failed handler is set..
3940             if (!this.hasListener('actionfailed')) {
3941                 Roo.log("need to add dialog support");
3942                 /*
3943                 Roo.MessageBox.alert("Error",
3944                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
3945                         action.result.errorMsg :
3946                         "Saving Failed, please check your entries or try again"
3947                 );
3948                 */
3949             }
3950             
3951             this.fireEvent('actionfailed', this, action);
3952         }
3953         
3954     },
3955     /**
3956      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
3957      * @param {String} id The value to search for
3958      * @return Field
3959      */
3960     findField : function(id){
3961         var items = this.getItems();
3962         var field = items.get(id);
3963         if(!field){
3964              items.each(function(f){
3965                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
3966                     field = f;
3967                     return false;
3968                 }
3969                 return true;
3970             });
3971         }
3972         return field || null;
3973     },
3974      /**
3975      * Mark fields in this form invalid in bulk.
3976      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
3977      * @return {BasicForm} this
3978      */
3979     markInvalid : function(errors){
3980         if(errors instanceof Array){
3981             for(var i = 0, len = errors.length; i < len; i++){
3982                 var fieldError = errors[i];
3983                 var f = this.findField(fieldError.id);
3984                 if(f){
3985                     f.markInvalid(fieldError.msg);
3986                 }
3987             }
3988         }else{
3989             var field, id;
3990             for(id in errors){
3991                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
3992                     field.markInvalid(errors[id]);
3993                 }
3994             }
3995         }
3996         //Roo.each(this.childForms || [], function (f) {
3997         //    f.markInvalid(errors);
3998         //});
3999         
4000         return this;
4001     },
4002
4003     /**
4004      * Set values for fields in this form in bulk.
4005      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4006      * @return {BasicForm} this
4007      */
4008     setValues : function(values){
4009         if(values instanceof Array){ // array of objects
4010             for(var i = 0, len = values.length; i < len; i++){
4011                 var v = values[i];
4012                 var f = this.findField(v.id);
4013                 if(f){
4014                     f.setValue(v.value);
4015                     if(this.trackResetOnLoad){
4016                         f.originalValue = f.getValue();
4017                     }
4018                 }
4019             }
4020         }else{ // object hash
4021             var field, id;
4022             for(id in values){
4023                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4024                     
4025                     if (field.setFromData && 
4026                         field.valueField && 
4027                         field.displayField &&
4028                         // combos' with local stores can 
4029                         // be queried via setValue()
4030                         // to set their value..
4031                         (field.store && !field.store.isLocal)
4032                         ) {
4033                         // it's a combo
4034                         var sd = { };
4035                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4036                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4037                         field.setFromData(sd);
4038                         
4039                     } else {
4040                         field.setValue(values[id]);
4041                     }
4042                     
4043                     
4044                     if(this.trackResetOnLoad){
4045                         field.originalValue = field.getValue();
4046                     }
4047                 }
4048             }
4049         }
4050          
4051         //Roo.each(this.childForms || [], function (f) {
4052         //    f.setValues(values);
4053         //});
4054                 
4055         return this;
4056     },
4057
4058     /**
4059      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4060      * they are returned as an array.
4061      * @param {Boolean} asString
4062      * @return {Object}
4063      */
4064     getValues : function(asString){
4065         //if (this.childForms) {
4066             // copy values from the child forms
4067         //    Roo.each(this.childForms, function (f) {
4068         //        this.setValues(f.getValues());
4069         //    }, this);
4070         //}
4071         
4072         
4073         
4074         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4075         if(asString === true){
4076             return fs;
4077         }
4078         return Roo.urlDecode(fs);
4079     },
4080     
4081     /**
4082      * Returns the fields in this form as an object with key/value pairs. 
4083      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4084      * @return {Object}
4085      */
4086     getFieldValues : function(with_hidden)
4087     {
4088         var items = this.getItems();
4089         var ret = {};
4090         items.each(function(f){
4091             if (!f.getName()) {
4092                 return;
4093             }
4094             var v = f.getValue();
4095             if (f.inputType =='radio') {
4096                 if (typeof(ret[f.getName()]) == 'undefined') {
4097                     ret[f.getName()] = ''; // empty..
4098                 }
4099                 
4100                 if (!f.el.dom.checked) {
4101                     return;
4102                     
4103                 }
4104                 v = f.el.dom.value;
4105                 
4106             }
4107             
4108             // not sure if this supported any more..
4109             if ((typeof(v) == 'object') && f.getRawValue) {
4110                 v = f.getRawValue() ; // dates..
4111             }
4112             // combo boxes where name != hiddenName...
4113             if (f.name != f.getName()) {
4114                 ret[f.name] = f.getRawValue();
4115             }
4116             ret[f.getName()] = v;
4117         });
4118         
4119         return ret;
4120     },
4121
4122     /**
4123      * Clears all invalid messages in this form.
4124      * @return {BasicForm} this
4125      */
4126     clearInvalid : function(){
4127         var items = this.getItems();
4128         
4129         items.each(function(f){
4130            f.clearInvalid();
4131         });
4132         
4133         
4134         
4135         return this;
4136     },
4137
4138     /**
4139      * Resets this form.
4140      * @return {BasicForm} this
4141      */
4142     reset : function(){
4143         var items = this.getItems();
4144         items.each(function(f){
4145             f.reset();
4146         });
4147         
4148         Roo.each(this.childForms || [], function (f) {
4149             f.reset();
4150         });
4151        
4152         
4153         return this;
4154     },
4155     getItems : function()
4156     {
4157         var r=new Roo.util.MixedCollection(false, function(o){
4158             return o.id || (o.id = Roo.id());
4159         });
4160         var iter = function(el) {
4161             if (el.inputEl) {
4162                 r.add(el);
4163             }
4164             if (!el.items) {
4165                 return;
4166             }
4167             Roo.each(el.items,function(e) {
4168                 iter(e);
4169             });
4170             
4171             
4172         };
4173         iter(this);
4174         return r;
4175         
4176         
4177         
4178         
4179     }
4180     
4181 });
4182
4183  
4184 /*
4185  * Based on:
4186  * Ext JS Library 1.1.1
4187  * Copyright(c) 2006-2007, Ext JS, LLC.
4188  *
4189  * Originally Released Under LGPL - original licence link has changed is not relivant.
4190  *
4191  * Fork - LGPL
4192  * <script type="text/javascript">
4193  */
4194 /**
4195  * @class Roo.form.VTypes
4196  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4197  * @singleton
4198  */
4199 Roo.form.VTypes = function(){
4200     // closure these in so they are only created once.
4201     var alpha = /^[a-zA-Z_]+$/;
4202     var alphanum = /^[a-zA-Z0-9_]+$/;
4203     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4204     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4205
4206     // All these messages and functions are configurable
4207     return {
4208         /**
4209          * The function used to validate email addresses
4210          * @param {String} value The email address
4211          */
4212         'email' : function(v){
4213             return email.test(v);
4214         },
4215         /**
4216          * The error text to display when the email validation function returns false
4217          * @type String
4218          */
4219         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4220         /**
4221          * The keystroke filter mask to be applied on email input
4222          * @type RegExp
4223          */
4224         'emailMask' : /[a-z0-9_\.\-@]/i,
4225
4226         /**
4227          * The function used to validate URLs
4228          * @param {String} value The URL
4229          */
4230         'url' : function(v){
4231             return url.test(v);
4232         },
4233         /**
4234          * The error text to display when the url validation function returns false
4235          * @type String
4236          */
4237         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4238         
4239         /**
4240          * The function used to validate alpha values
4241          * @param {String} value The value
4242          */
4243         'alpha' : function(v){
4244             return alpha.test(v);
4245         },
4246         /**
4247          * The error text to display when the alpha validation function returns false
4248          * @type String
4249          */
4250         'alphaText' : 'This field should only contain letters and _',
4251         /**
4252          * The keystroke filter mask to be applied on alpha input
4253          * @type RegExp
4254          */
4255         'alphaMask' : /[a-z_]/i,
4256
4257         /**
4258          * The function used to validate alphanumeric values
4259          * @param {String} value The value
4260          */
4261         'alphanum' : function(v){
4262             return alphanum.test(v);
4263         },
4264         /**
4265          * The error text to display when the alphanumeric validation function returns false
4266          * @type String
4267          */
4268         'alphanumText' : 'This field should only contain letters, numbers and _',
4269         /**
4270          * The keystroke filter mask to be applied on alphanumeric input
4271          * @type RegExp
4272          */
4273         'alphanumMask' : /[a-z0-9_]/i
4274     };
4275 }();/*
4276  * - LGPL
4277  *
4278  * Input
4279  * 
4280  */
4281
4282 /**
4283  * @class Roo.bootstrap.Input
4284  * @extends Roo.bootstrap.Component
4285  * Bootstrap Input class
4286  * @cfg {Boolean} disabled is it disabled
4287  * @cfg {String} fieldLabel - the label associated
4288  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4289  * @cfg {String} name name of the input
4290  * @cfg {string} fieldLabel - the label associated
4291  * @cfg {string}  inputType - input / file submit ...
4292  * @cfg {string} placeholder - placeholder to put in text.
4293  * @cfg {string}  before - input group add on before
4294  * @cfg {string} after - input group add on after
4295  * @cfg {string} size - (lg|sm) or leave empty..
4296  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4297  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4298  * @cfg {Number} md colspan out of 12 for computer-sized screens
4299  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4300  * @cfg {string} value default value of the input
4301  * @cfg {Number} labelWidth set the width of label (0-12)
4302  * @cfg {String} labelAlign (top|left)
4303  * 
4304  * 
4305  * @constructor
4306  * Create a new Input
4307  * @param {Object} config The config object
4308  */
4309
4310 Roo.bootstrap.Input = function(config){
4311     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4312    
4313         this.addEvents({
4314             /**
4315              * @event focus
4316              * Fires when this field receives input focus.
4317              * @param {Roo.form.Field} this
4318              */
4319             focus : true,
4320             /**
4321              * @event blur
4322              * Fires when this field loses input focus.
4323              * @param {Roo.form.Field} this
4324              */
4325             blur : true,
4326             /**
4327              * @event specialkey
4328              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4329              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4330              * @param {Roo.form.Field} this
4331              * @param {Roo.EventObject} e The event object
4332              */
4333             specialkey : true,
4334             /**
4335              * @event change
4336              * Fires just before the field blurs if the field value has changed.
4337              * @param {Roo.form.Field} this
4338              * @param {Mixed} newValue The new value
4339              * @param {Mixed} oldValue The original value
4340              */
4341             change : true,
4342             /**
4343              * @event invalid
4344              * Fires after the field has been marked as invalid.
4345              * @param {Roo.form.Field} this
4346              * @param {String} msg The validation message
4347              */
4348             invalid : true,
4349             /**
4350              * @event valid
4351              * Fires after the field has been validated with no errors.
4352              * @param {Roo.form.Field} this
4353              */
4354             valid : true,
4355              /**
4356              * @event keyup
4357              * Fires after the key up
4358              * @param {Roo.form.Field} this
4359              * @param {Roo.EventObject}  e The event Object
4360              */
4361             keyup : true
4362         });
4363 };
4364
4365 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4366      /**
4367      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4368       automatic validation (defaults to "keyup").
4369      */
4370     validationEvent : "keyup",
4371      /**
4372      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4373      */
4374     validateOnBlur : true,
4375     /**
4376      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4377      */
4378     validationDelay : 250,
4379      /**
4380      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4381      */
4382     focusClass : "x-form-focus",  // not needed???
4383     
4384        
4385     /**
4386      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4387      */
4388     invalidClass : "has-error",
4389     
4390     /**
4391      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4392      */
4393     selectOnFocus : false,
4394     
4395      /**
4396      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4397      */
4398     maskRe : null,
4399        /**
4400      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4401      */
4402     vtype : null,
4403     
4404       /**
4405      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4406      */
4407     disableKeyFilter : false,
4408     
4409        /**
4410      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4411      */
4412     disabled : false,
4413      /**
4414      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4415      */
4416     allowBlank : true,
4417     /**
4418      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4419      */
4420     blankText : "This field is required",
4421     
4422      /**
4423      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4424      */
4425     minLength : 0,
4426     /**
4427      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4428      */
4429     maxLength : Number.MAX_VALUE,
4430     /**
4431      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4432      */
4433     minLengthText : "The minimum length for this field is {0}",
4434     /**
4435      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4436      */
4437     maxLengthText : "The maximum length for this field is {0}",
4438   
4439     
4440     /**
4441      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4442      * If available, this function will be called only after the basic validators all return true, and will be passed the
4443      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4444      */
4445     validator : null,
4446     /**
4447      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4448      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4449      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4450      */
4451     regex : null,
4452     /**
4453      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4454      */
4455     regexText : "",
4456     
4457     
4458     
4459     fieldLabel : '',
4460     inputType : 'text',
4461     
4462     name : false,
4463     placeholder: false,
4464     before : false,
4465     after : false,
4466     size : false,
4467     // private
4468     hasFocus : false,
4469     preventMark: false,
4470     isFormField : true,
4471     value : '',
4472     labelWidth : 2,
4473     labelAlign : false,
4474     
4475     parentLabelAlign : function()
4476     {
4477         var parent = this;
4478         while (parent.parent()) {
4479             parent = parent.parent();
4480             if (typeof(parent.labelAlign) !='undefined') {
4481                 return parent.labelAlign;
4482             }
4483         }
4484         return 'left';
4485         
4486     },
4487     
4488     getAutoCreate : function(){
4489         
4490         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4491         
4492         var id = Roo.id();
4493         
4494         var cfg = {};
4495         
4496         if(this.inputType != 'hidden'){
4497             cfg.cls = 'form-group' //input-group
4498         }
4499         
4500         var input =  {
4501             tag: 'input',
4502             id : id,
4503             type : this.inputType,
4504             value : this.value,
4505             cls : 'form-control',
4506             placeholder : this.placeholder || ''
4507             
4508         };
4509         
4510         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4511             input.maxLength = this.maxLength;
4512         }
4513         
4514         if (this.disabled) {
4515             input.disabled=true;
4516         }
4517         
4518         if (this.name) {
4519             input.name = this.name;
4520         }
4521         if (this.size) {
4522             input.cls += ' input-' + this.size;
4523         }
4524         var settings=this;
4525         ['xs','sm','md','lg'].map(function(size){
4526             if (settings[size]) {
4527                 cfg.cls += ' col-' + size + '-' + settings[size];
4528             }
4529         });
4530         
4531         var inputblock = input;
4532         
4533         if (this.before || this.after) {
4534             
4535             inputblock = {
4536                 cls : 'input-group',
4537                 cn :  [] 
4538             };
4539             if (this.before) {
4540                 inputblock.cn.push({
4541                     tag :'span',
4542                     cls : 'input-group-addon',
4543                     html : this.before
4544                 });
4545             }
4546             inputblock.cn.push(input);
4547             if (this.after) {
4548                 inputblock.cn.push({
4549                     tag :'span',
4550                     cls : 'input-group-addon',
4551                     html : this.after
4552                 });
4553             }
4554             
4555         };
4556         
4557         if (align ==='left' && this.fieldLabel.length) {
4558                 Roo.log("left and has label");
4559                 cfg.cn = [
4560                     
4561                     {
4562                         tag: 'label',
4563                         'for' :  id,
4564                         cls : 'control-label col-sm-' + this.labelWidth,
4565                         html : this.fieldLabel
4566                         
4567                     },
4568                     {
4569                         cls : "col-sm-" + (12 - this.labelWidth), 
4570                         cn: [
4571                             inputblock
4572                         ]
4573                     }
4574                     
4575                 ];
4576         } else if ( this.fieldLabel.length) {
4577                 Roo.log(" label");
4578                  cfg.cn = [
4579                    
4580                     {
4581                         tag: 'label',
4582                         //cls : 'input-group-addon',
4583                         html : this.fieldLabel
4584                         
4585                     },
4586                     
4587                     inputblock
4588                     
4589                 ];
4590
4591         } else {
4592             
4593                 Roo.log(" no label && no align");
4594                 cfg.cn = [
4595                     
4596                         inputblock
4597                     
4598                 ];
4599                 
4600                 
4601         };
4602         Roo.log('input-parentType: ' + this.parentType);
4603         
4604         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4605            cfg.cls += ' navbar-form';
4606            Roo.log(cfg);
4607         }
4608         
4609         return cfg;
4610         
4611     },
4612     /**
4613      * return the real input element.
4614      */
4615     inputEl: function ()
4616     {
4617         return this.el.select('input.form-control',true).first();
4618     },
4619     setDisabled : function(v)
4620     {
4621         var i  = this.inputEl().dom;
4622         if (!v) {
4623             i.removeAttribute('disabled');
4624             return;
4625             
4626         }
4627         i.setAttribute('disabled','true');
4628     },
4629     initEvents : function()
4630     {
4631         
4632         this.inputEl().on("keydown" , this.fireKey,  this);
4633         this.inputEl().on("focus", this.onFocus,  this);
4634         this.inputEl().on("blur", this.onBlur,  this);
4635         
4636         this.inputEl().relayEvent('keyup', this);
4637
4638         // reference to original value for reset
4639         this.originalValue = this.getValue();
4640         //Roo.form.TextField.superclass.initEvents.call(this);
4641         if(this.validationEvent == 'keyup'){
4642             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4643             this.inputEl().on('keyup', this.filterValidation, this);
4644         }
4645         else if(this.validationEvent !== false){
4646             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4647         }
4648         
4649         if(this.selectOnFocus){
4650             this.on("focus", this.preFocus, this);
4651             
4652         }
4653         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4654             this.inputEl().on("keypress", this.filterKeys, this);
4655         }
4656        /* if(this.grow){
4657             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4658             this.el.on("click", this.autoSize,  this);
4659         }
4660         */
4661         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4662             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4663         }
4664         
4665     },
4666     filterValidation : function(e){
4667         if(!e.isNavKeyPress()){
4668             this.validationTask.delay(this.validationDelay);
4669         }
4670     },
4671      /**
4672      * Validates the field value
4673      * @return {Boolean} True if the value is valid, else false
4674      */
4675     validate : function(){
4676         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4677         if(this.disabled || this.validateValue(this.getRawValue())){
4678             this.clearInvalid();
4679             return true;
4680         }
4681         return false;
4682     },
4683     
4684     
4685     /**
4686      * Validates a value according to the field's validation rules and marks the field as invalid
4687      * if the validation fails
4688      * @param {Mixed} value The value to validate
4689      * @return {Boolean} True if the value is valid, else false
4690      */
4691     validateValue : function(value){
4692         if(value.length < 1)  { // if it's blank
4693              if(this.allowBlank){
4694                 this.clearInvalid();
4695                 return true;
4696              }else{
4697                 this.markInvalid(this.blankText);
4698                 return false;
4699              }
4700         }
4701         if(value.length < this.minLength){
4702             this.markInvalid(String.format(this.minLengthText, this.minLength));
4703             return false;
4704         }
4705         if(value.length > this.maxLength){
4706             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4707             return false;
4708         }
4709         if(this.vtype){
4710             var vt = Roo.form.VTypes;
4711             if(!vt[this.vtype](value, this)){
4712                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4713                 return false;
4714             }
4715         }
4716         if(typeof this.validator == "function"){
4717             var msg = this.validator(value);
4718             if(msg !== true){
4719                 this.markInvalid(msg);
4720                 return false;
4721             }
4722         }
4723         if(this.regex && !this.regex.test(value)){
4724             this.markInvalid(this.regexText);
4725             return false;
4726         }
4727         return true;
4728     },
4729
4730     
4731     
4732      // private
4733     fireKey : function(e){
4734         //Roo.log('field ' + e.getKey());
4735         if(e.isNavKeyPress()){
4736             this.fireEvent("specialkey", this, e);
4737         }
4738     },
4739     focus : function (selectText){
4740         if(this.rendered){
4741             this.inputEl().focus();
4742             if(selectText === true){
4743                 this.inputEl().dom.select();
4744             }
4745         }
4746         return this;
4747     } ,
4748     
4749     onFocus : function(){
4750         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4751            // this.el.addClass(this.focusClass);
4752         }
4753         if(!this.hasFocus){
4754             this.hasFocus = true;
4755             this.startValue = this.getValue();
4756             this.fireEvent("focus", this);
4757         }
4758     },
4759     
4760     beforeBlur : Roo.emptyFn,
4761
4762     
4763     // private
4764     onBlur : function(){
4765         this.beforeBlur();
4766         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4767             //this.el.removeClass(this.focusClass);
4768         }
4769         this.hasFocus = false;
4770         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4771             this.validate();
4772         }
4773         var v = this.getValue();
4774         if(String(v) !== String(this.startValue)){
4775             this.fireEvent('change', this, v, this.startValue);
4776         }
4777         this.fireEvent("blur", this);
4778     },
4779     
4780     /**
4781      * Resets the current field value to the originally loaded value and clears any validation messages
4782      */
4783     reset : function(){
4784         this.setValue(this.originalValue);
4785         this.clearInvalid();
4786     },
4787      /**
4788      * Returns the name of the field
4789      * @return {Mixed} name The name field
4790      */
4791     getName: function(){
4792         return this.name;
4793     },
4794      /**
4795      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4796      * @return {Mixed} value The field value
4797      */
4798     getValue : function(){
4799         return this.inputEl().getValue();
4800     },
4801     /**
4802      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4803      * @return {Mixed} value The field value
4804      */
4805     getRawValue : function(){
4806         var v = this.inputEl().getValue();
4807         
4808         return v;
4809     },
4810     
4811     /**
4812      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4813      * @param {Mixed} value The value to set
4814      */
4815     setRawValue : function(v){
4816         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4817     },
4818     
4819     selectText : function(start, end){
4820         var v = this.getRawValue();
4821         if(v.length > 0){
4822             start = start === undefined ? 0 : start;
4823             end = end === undefined ? v.length : end;
4824             var d = this.inputEl().dom;
4825             if(d.setSelectionRange){
4826                 d.setSelectionRange(start, end);
4827             }else if(d.createTextRange){
4828                 var range = d.createTextRange();
4829                 range.moveStart("character", start);
4830                 range.moveEnd("character", v.length-end);
4831                 range.select();
4832             }
4833         }
4834     },
4835     
4836     /**
4837      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4838      * @param {Mixed} value The value to set
4839      */
4840     setValue : function(v){
4841         this.value = v;
4842         if(this.rendered){
4843             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4844             this.validate();
4845         }
4846     },
4847     
4848     /*
4849     processValue : function(value){
4850         if(this.stripCharsRe){
4851             var newValue = value.replace(this.stripCharsRe, '');
4852             if(newValue !== value){
4853                 this.setRawValue(newValue);
4854                 return newValue;
4855             }
4856         }
4857         return value;
4858     },
4859   */
4860     preFocus : function(){
4861         
4862         if(this.selectOnFocus){
4863             this.inputEl().dom.select();
4864         }
4865     },
4866     filterKeys : function(e){
4867         var k = e.getKey();
4868         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4869             return;
4870         }
4871         var c = e.getCharCode(), cc = String.fromCharCode(c);
4872         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4873             return;
4874         }
4875         if(!this.maskRe.test(cc)){
4876             e.stopEvent();
4877         }
4878     },
4879      /**
4880      * Clear any invalid styles/messages for this field
4881      */
4882     clearInvalid : function(){
4883         
4884         if(!this.el || this.preventMark){ // not rendered
4885             return;
4886         }
4887         this.el.removeClass(this.invalidClass);
4888         /*
4889         switch(this.msgTarget){
4890             case 'qtip':
4891                 this.el.dom.qtip = '';
4892                 break;
4893             case 'title':
4894                 this.el.dom.title = '';
4895                 break;
4896             case 'under':
4897                 if(this.errorEl){
4898                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4899                 }
4900                 break;
4901             case 'side':
4902                 if(this.errorIcon){
4903                     this.errorIcon.dom.qtip = '';
4904                     this.errorIcon.hide();
4905                     this.un('resize', this.alignErrorIcon, this);
4906                 }
4907                 break;
4908             default:
4909                 var t = Roo.getDom(this.msgTarget);
4910                 t.innerHTML = '';
4911                 t.style.display = 'none';
4912                 break;
4913         }
4914         */
4915         this.fireEvent('valid', this);
4916     },
4917      /**
4918      * Mark this field as invalid
4919      * @param {String} msg The validation message
4920      */
4921     markInvalid : function(msg){
4922         if(!this.el  || this.preventMark){ // not rendered
4923             return;
4924         }
4925         this.el.addClass(this.invalidClass);
4926         /*
4927         msg = msg || this.invalidText;
4928         switch(this.msgTarget){
4929             case 'qtip':
4930                 this.el.dom.qtip = msg;
4931                 this.el.dom.qclass = 'x-form-invalid-tip';
4932                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
4933                     Roo.QuickTips.enable();
4934                 }
4935                 break;
4936             case 'title':
4937                 this.el.dom.title = msg;
4938                 break;
4939             case 'under':
4940                 if(!this.errorEl){
4941                     var elp = this.el.findParent('.x-form-element', 5, true);
4942                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
4943                     this.errorEl.setWidth(elp.getWidth(true)-20);
4944                 }
4945                 this.errorEl.update(msg);
4946                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
4947                 break;
4948             case 'side':
4949                 if(!this.errorIcon){
4950                     var elp = this.el.findParent('.x-form-element', 5, true);
4951                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
4952                 }
4953                 this.alignErrorIcon();
4954                 this.errorIcon.dom.qtip = msg;
4955                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
4956                 this.errorIcon.show();
4957                 this.on('resize', this.alignErrorIcon, this);
4958                 break;
4959             default:
4960                 var t = Roo.getDom(this.msgTarget);
4961                 t.innerHTML = msg;
4962                 t.style.display = this.msgDisplay;
4963                 break;
4964         }
4965         */
4966         this.fireEvent('invalid', this, msg);
4967     },
4968     // private
4969     SafariOnKeyDown : function(event)
4970     {
4971         // this is a workaround for a password hang bug on chrome/ webkit.
4972         
4973         var isSelectAll = false;
4974         
4975         if(this.inputEl().dom.selectionEnd > 0){
4976             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
4977         }
4978         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
4979             event.preventDefault();
4980             this.setValue('');
4981             return;
4982         }
4983         
4984         if(isSelectAll){ // backspace and delete key
4985             
4986             event.preventDefault();
4987             // this is very hacky as keydown always get's upper case.
4988             //
4989             var cc = String.fromCharCode(event.getCharCode());
4990             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
4991             
4992         }
4993     },
4994     adjustWidth : function(tag, w){
4995         tag = tag.toLowerCase();
4996         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
4997             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
4998                 if(tag == 'input'){
4999                     return w + 2;
5000                 }
5001                 if(tag == 'textarea'){
5002                     return w-2;
5003                 }
5004             }else if(Roo.isOpera){
5005                 if(tag == 'input'){
5006                     return w + 2;
5007                 }
5008                 if(tag == 'textarea'){
5009                     return w-2;
5010                 }
5011             }
5012         }
5013         return w;
5014     }
5015     
5016 });
5017
5018  
5019 /*
5020  * - LGPL
5021  *
5022  * Input
5023  * 
5024  */
5025
5026 /**
5027  * @class Roo.bootstrap.TextArea
5028  * @extends Roo.bootstrap.Input
5029  * Bootstrap TextArea class
5030  * @cfg {Number} cols Specifies the visible width of a text area
5031  * @cfg {Number} rows Specifies the visible number of lines in a text area
5032  * @cfg {Number} readOnly Specifies that a text area should be read-only
5033  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5034  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5035  * @cfg {string} html text
5036  * 
5037  * @constructor
5038  * Create a new TextArea
5039  * @param {Object} config The config object
5040  */
5041
5042 Roo.bootstrap.TextArea = function(config){
5043     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5044    
5045 };
5046
5047 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5048      
5049     cols : false,
5050     rows : 5,
5051     readOnly : false,
5052     warp : 'soft',
5053     resize : false,
5054     value: false,
5055     html: false,
5056     
5057     getAutoCreate : function(){
5058         
5059         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5060         
5061         var id = Roo.id();
5062         
5063         var cfg = {};
5064         
5065         var input =  {
5066             tag: 'textarea',
5067             id : id,
5068             warp : this.warp,
5069             rows : this.rows,
5070             value : this.value || '',
5071             html: this.html || '',
5072             cls : 'form-control',
5073             placeholder : this.placeholder || '' 
5074             
5075         };
5076         
5077         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5078             input.maxLength = this.maxLength;
5079         }
5080         
5081         if(this.resize){
5082             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5083         }
5084         
5085         if(this.cols){
5086             input.cols = this.cols;
5087         }
5088         
5089         if (this.readOnly) {
5090             input.readonly = true;
5091         }
5092         
5093         if (this.name) {
5094             input.name = this.name;
5095         }
5096         
5097         if (this.size) {
5098             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5099         }
5100         
5101         var settings=this;
5102         ['xs','sm','md','lg'].map(function(size){
5103             if (settings[size]) {
5104                 cfg.cls += ' col-' + size + '-' + settings[size];
5105             }
5106         });
5107         
5108         var inputblock = input;
5109         
5110         if (this.before || this.after) {
5111             
5112             inputblock = {
5113                 cls : 'input-group',
5114                 cn :  [] 
5115             };
5116             if (this.before) {
5117                 inputblock.cn.push({
5118                     tag :'span',
5119                     cls : 'input-group-addon',
5120                     html : this.before
5121                 });
5122             }
5123             inputblock.cn.push(input);
5124             if (this.after) {
5125                 inputblock.cn.push({
5126                     tag :'span',
5127                     cls : 'input-group-addon',
5128                     html : this.after
5129                 });
5130             }
5131             
5132         }
5133         
5134         if (align ==='left' && this.fieldLabel.length) {
5135                 Roo.log("left and has label");
5136                 cfg.cn = [
5137                     
5138                     {
5139                         tag: 'label',
5140                         'for' :  id,
5141                         cls : 'control-label col-sm-' + this.labelWidth,
5142                         html : this.fieldLabel
5143                         
5144                     },
5145                     {
5146                         cls : "col-sm-" + (12 - this.labelWidth), 
5147                         cn: [
5148                             inputblock
5149                         ]
5150                     }
5151                     
5152                 ];
5153         } else if ( this.fieldLabel.length) {
5154                 Roo.log(" label");
5155                  cfg.cn = [
5156                    
5157                     {
5158                         tag: 'label',
5159                         //cls : 'input-group-addon',
5160                         html : this.fieldLabel
5161                         
5162                     },
5163                     
5164                     inputblock
5165                     
5166                 ];
5167
5168         } else {
5169             
5170                    Roo.log(" no label && no align");
5171                 cfg.cn = [
5172                     
5173                         inputblock
5174                     
5175                 ];
5176                 
5177                 
5178         }
5179         
5180         if (this.disabled) {
5181             input.disabled=true;
5182         }
5183         
5184         return cfg;
5185         
5186     },
5187     /**
5188      * return the real textarea element.
5189      */
5190     inputEl: function ()
5191     {
5192         return this.el.select('textarea.form-control',true).first();
5193     }
5194 });
5195
5196  
5197 /*
5198  * - LGPL
5199  *
5200  * trigger field - base class for combo..
5201  * 
5202  */
5203  
5204 /**
5205  * @class Roo.bootstrap.TriggerField
5206  * @extends Roo.bootstrap.Input
5207  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5208  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5209  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5210  * for which you can provide a custom implementation.  For example:
5211  * <pre><code>
5212 var trigger = new Roo.bootstrap.TriggerField();
5213 trigger.onTriggerClick = myTriggerFn;
5214 trigger.applyTo('my-field');
5215 </code></pre>
5216  *
5217  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5218  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5219  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5220  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5221  * @constructor
5222  * Create a new TriggerField.
5223  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5224  * to the base TextField)
5225  */
5226 Roo.bootstrap.TriggerField = function(config){
5227     this.mimicing = false;
5228     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5229 };
5230
5231 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5232     /**
5233      * @cfg {String} triggerClass A CSS class to apply to the trigger
5234      */
5235      /**
5236      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5237      */
5238     hideTrigger:false,
5239
5240     /** @cfg {Boolean} grow @hide */
5241     /** @cfg {Number} growMin @hide */
5242     /** @cfg {Number} growMax @hide */
5243
5244     /**
5245      * @hide 
5246      * @method
5247      */
5248     autoSize: Roo.emptyFn,
5249     // private
5250     monitorTab : true,
5251     // private
5252     deferHeight : true,
5253
5254     
5255     actionMode : 'wrap',
5256     
5257     
5258     
5259     getAutoCreate : function(){
5260        
5261         var parent = this.parent();
5262         
5263         var align = this.parentLabelAlign();
5264         
5265         var id = Roo.id();
5266         
5267         var cfg = {
5268             cls: 'form-group' //input-group
5269         };
5270         
5271         
5272         var input =  {
5273             tag: 'input',
5274             id : id,
5275             type : this.inputType,
5276             cls : 'form-control',
5277             autocomplete: 'off',
5278             placeholder : this.placeholder || '' 
5279             
5280         };
5281         if (this.name) {
5282             input.name = this.name;
5283         }
5284         if (this.size) {
5285             input.cls += ' input-' + this.size;
5286         }
5287         
5288         if (this.disabled) {
5289             input.disabled=true;
5290         }
5291         
5292         var inputblock = input;
5293         
5294         if (this.before || this.after) {
5295             
5296             inputblock = {
5297                 cls : 'input-group',
5298                 cn :  [] 
5299             };
5300             if (this.before) {
5301                 inputblock.cn.push({
5302                     tag :'span',
5303                     cls : 'input-group-addon',
5304                     html : this.before
5305                 });
5306             }
5307             inputblock.cn.push(input);
5308             if (this.after) {
5309                 inputblock.cn.push({
5310                     tag :'span',
5311                     cls : 'input-group-addon',
5312                     html : this.after
5313                 });
5314             }
5315             
5316         };
5317         
5318         var box = {
5319             tag: 'div',
5320             cn: [
5321                 {
5322                     tag: 'input',
5323                     type : 'hidden',
5324                     cls: 'form-hidden-field'
5325                 },
5326                 inputblock
5327             ]
5328             
5329         };
5330         
5331         if(this.multiple){
5332             Roo.log('multiple');
5333             
5334             box = {
5335                 tag: 'div',
5336                 cn: [
5337                     {
5338                         tag: 'input',
5339                         type : 'hidden',
5340                         cls: 'form-hidden-field'
5341                     },
5342                     {
5343                         tag: 'ul',
5344                         cls: 'select2-choices',
5345                         cn:[
5346                             {
5347                                 tag: 'li',
5348                                 cls: 'select2-search-field',
5349                                 cn: [
5350
5351                                     inputblock
5352                                 ]
5353                             }
5354                         ]
5355                     }
5356                 ]
5357             }
5358         };
5359         
5360         var combobox = {
5361             cls: 'select2-container input-group',
5362             cn: [
5363                 box,
5364                 {
5365                     tag: 'ul',
5366                     cls: 'typeahead typeahead-long dropdown-menu',
5367                     style: 'display:none'
5368                 }
5369             ]
5370         };
5371         
5372         if(!this.multiple){
5373             combobox.cn.push({
5374                 tag :'span',
5375                 cls : 'input-group-addon btn dropdown-toggle',
5376                 cn : [
5377                     {
5378                         tag: 'span',
5379                         cls: 'caret'
5380                     },
5381                     {
5382                         tag: 'span',
5383                         cls: 'combobox-clear',
5384                         cn  : [
5385                             {
5386                                 tag : 'i',
5387                                 cls: 'icon-remove'
5388                             }
5389                         ]
5390                     }
5391                 ]
5392
5393             })
5394         }
5395         
5396         if(this.multiple){
5397             combobox.cls += ' select2-container-multi';
5398         }
5399         
5400         if (align ==='left' && this.fieldLabel.length) {
5401             
5402                 Roo.log("left and has label");
5403                 cfg.cn = [
5404                     
5405                     {
5406                         tag: 'label',
5407                         'for' :  id,
5408                         cls : 'control-label col-sm-' + this.labelWidth,
5409                         html : this.fieldLabel
5410                         
5411                     },
5412                     {
5413                         cls : "col-sm-" + (12 - this.labelWidth), 
5414                         cn: [
5415                             combobox
5416                         ]
5417                     }
5418                     
5419                 ];
5420         } else if ( this.fieldLabel.length) {
5421                 Roo.log(" label");
5422                  cfg.cn = [
5423                    
5424                     {
5425                         tag: 'label',
5426                         //cls : 'input-group-addon',
5427                         html : this.fieldLabel
5428                         
5429                     },
5430                     
5431                     combobox
5432                     
5433                 ];
5434
5435         } else {
5436             
5437                 Roo.log(" no label && no align");
5438                 cfg = combobox
5439                      
5440                 
5441         }
5442          
5443         var settings=this;
5444         ['xs','sm','md','lg'].map(function(size){
5445             if (settings[size]) {
5446                 cfg.cls += ' col-' + size + '-' + settings[size];
5447             }
5448         });
5449         
5450         return cfg;
5451         
5452     },
5453     
5454     
5455     
5456     // private
5457     onResize : function(w, h){
5458 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5459 //        if(typeof w == 'number'){
5460 //            var x = w - this.trigger.getWidth();
5461 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5462 //            this.trigger.setStyle('left', x+'px');
5463 //        }
5464     },
5465
5466     // private
5467     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5468
5469     // private
5470     getResizeEl : function(){
5471         return this.inputEl();
5472     },
5473
5474     // private
5475     getPositionEl : function(){
5476         return this.inputEl();
5477     },
5478
5479     // private
5480     alignErrorIcon : function(){
5481         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5482     },
5483
5484     // private
5485     initEvents : function(){
5486         
5487         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5488         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5489         if(!this.multiple){
5490             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5491             if(this.hideTrigger){
5492                 this.trigger.setDisplayed(false);
5493             }
5494             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5495         }
5496         
5497         if(this.multiple){
5498             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5499         }
5500         
5501         //this.trigger.addClassOnOver('x-form-trigger-over');
5502         //this.trigger.addClassOnClick('x-form-trigger-click');
5503         
5504         //if(!this.width){
5505         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5506         //}
5507     },
5508
5509     // private
5510     initTrigger : function(){
5511        
5512     },
5513
5514     // private
5515     onDestroy : function(){
5516         if(this.trigger){
5517             this.trigger.removeAllListeners();
5518           //  this.trigger.remove();
5519         }
5520         //if(this.wrap){
5521         //    this.wrap.remove();
5522         //}
5523         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5524     },
5525
5526     // private
5527     onFocus : function(){
5528         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5529         /*
5530         if(!this.mimicing){
5531             this.wrap.addClass('x-trigger-wrap-focus');
5532             this.mimicing = true;
5533             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5534             if(this.monitorTab){
5535                 this.el.on("keydown", this.checkTab, this);
5536             }
5537         }
5538         */
5539     },
5540
5541     // private
5542     checkTab : function(e){
5543         if(e.getKey() == e.TAB){
5544             this.triggerBlur();
5545         }
5546     },
5547
5548     // private
5549     onBlur : function(){
5550         // do nothing
5551     },
5552
5553     // private
5554     mimicBlur : function(e, t){
5555         /*
5556         if(!this.wrap.contains(t) && this.validateBlur()){
5557             this.triggerBlur();
5558         }
5559         */
5560     },
5561
5562     // private
5563     triggerBlur : function(){
5564         this.mimicing = false;
5565         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5566         if(this.monitorTab){
5567             this.el.un("keydown", this.checkTab, this);
5568         }
5569         //this.wrap.removeClass('x-trigger-wrap-focus');
5570         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5571     },
5572
5573     // private
5574     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5575     validateBlur : function(e, t){
5576         return true;
5577     },
5578
5579     // private
5580     onDisable : function(){
5581         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5582         //if(this.wrap){
5583         //    this.wrap.addClass('x-item-disabled');
5584         //}
5585     },
5586
5587     // private
5588     onEnable : function(){
5589         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5590         //if(this.wrap){
5591         //    this.el.removeClass('x-item-disabled');
5592         //}
5593     },
5594
5595     // private
5596     onShow : function(){
5597         var ae = this.getActionEl();
5598         
5599         if(ae){
5600             ae.dom.style.display = '';
5601             ae.dom.style.visibility = 'visible';
5602         }
5603     },
5604
5605     // private
5606     
5607     onHide : function(){
5608         var ae = this.getActionEl();
5609         ae.dom.style.display = 'none';
5610     },
5611
5612     /**
5613      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5614      * by an implementing function.
5615      * @method
5616      * @param {EventObject} e
5617      */
5618     onTriggerClick : Roo.emptyFn
5619 });
5620  /*
5621  * Based on:
5622  * Ext JS Library 1.1.1
5623  * Copyright(c) 2006-2007, Ext JS, LLC.
5624  *
5625  * Originally Released Under LGPL - original licence link has changed is not relivant.
5626  *
5627  * Fork - LGPL
5628  * <script type="text/javascript">
5629  */
5630
5631
5632 /**
5633  * @class Roo.data.SortTypes
5634  * @singleton
5635  * Defines the default sorting (casting?) comparison functions used when sorting data.
5636  */
5637 Roo.data.SortTypes = {
5638     /**
5639      * Default sort that does nothing
5640      * @param {Mixed} s The value being converted
5641      * @return {Mixed} The comparison value
5642      */
5643     none : function(s){
5644         return s;
5645     },
5646     
5647     /**
5648      * The regular expression used to strip tags
5649      * @type {RegExp}
5650      * @property
5651      */
5652     stripTagsRE : /<\/?[^>]+>/gi,
5653     
5654     /**
5655      * Strips all HTML tags to sort on text only
5656      * @param {Mixed} s The value being converted
5657      * @return {String} The comparison value
5658      */
5659     asText : function(s){
5660         return String(s).replace(this.stripTagsRE, "");
5661     },
5662     
5663     /**
5664      * Strips all HTML tags to sort on text only - Case insensitive
5665      * @param {Mixed} s The value being converted
5666      * @return {String} The comparison value
5667      */
5668     asUCText : function(s){
5669         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5670     },
5671     
5672     /**
5673      * Case insensitive string
5674      * @param {Mixed} s The value being converted
5675      * @return {String} The comparison value
5676      */
5677     asUCString : function(s) {
5678         return String(s).toUpperCase();
5679     },
5680     
5681     /**
5682      * Date sorting
5683      * @param {Mixed} s The value being converted
5684      * @return {Number} The comparison value
5685      */
5686     asDate : function(s) {
5687         if(!s){
5688             return 0;
5689         }
5690         if(s instanceof Date){
5691             return s.getTime();
5692         }
5693         return Date.parse(String(s));
5694     },
5695     
5696     /**
5697      * Float sorting
5698      * @param {Mixed} s The value being converted
5699      * @return {Float} The comparison value
5700      */
5701     asFloat : function(s) {
5702         var val = parseFloat(String(s).replace(/,/g, ""));
5703         if(isNaN(val)) val = 0;
5704         return val;
5705     },
5706     
5707     /**
5708      * Integer sorting
5709      * @param {Mixed} s The value being converted
5710      * @return {Number} The comparison value
5711      */
5712     asInt : function(s) {
5713         var val = parseInt(String(s).replace(/,/g, ""));
5714         if(isNaN(val)) val = 0;
5715         return val;
5716     }
5717 };/*
5718  * Based on:
5719  * Ext JS Library 1.1.1
5720  * Copyright(c) 2006-2007, Ext JS, LLC.
5721  *
5722  * Originally Released Under LGPL - original licence link has changed is not relivant.
5723  *
5724  * Fork - LGPL
5725  * <script type="text/javascript">
5726  */
5727
5728 /**
5729 * @class Roo.data.Record
5730  * Instances of this class encapsulate both record <em>definition</em> information, and record
5731  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5732  * to access Records cached in an {@link Roo.data.Store} object.<br>
5733  * <p>
5734  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5735  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5736  * objects.<br>
5737  * <p>
5738  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5739  * @constructor
5740  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5741  * {@link #create}. The parameters are the same.
5742  * @param {Array} data An associative Array of data values keyed by the field name.
5743  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5744  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5745  * not specified an integer id is generated.
5746  */
5747 Roo.data.Record = function(data, id){
5748     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5749     this.data = data;
5750 };
5751
5752 /**
5753  * Generate a constructor for a specific record layout.
5754  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5755  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5756  * Each field definition object may contain the following properties: <ul>
5757  * <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,
5758  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5759  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5760  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5761  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5762  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5763  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5764  * this may be omitted.</p></li>
5765  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5766  * <ul><li>auto (Default, implies no conversion)</li>
5767  * <li>string</li>
5768  * <li>int</li>
5769  * <li>float</li>
5770  * <li>boolean</li>
5771  * <li>date</li></ul></p></li>
5772  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5773  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5774  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5775  * by the Reader into an object that will be stored in the Record. It is passed the
5776  * following parameters:<ul>
5777  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5778  * </ul></p></li>
5779  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5780  * </ul>
5781  * <br>usage:<br><pre><code>
5782 var TopicRecord = Roo.data.Record.create(
5783     {name: 'title', mapping: 'topic_title'},
5784     {name: 'author', mapping: 'username'},
5785     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5786     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5787     {name: 'lastPoster', mapping: 'user2'},
5788     {name: 'excerpt', mapping: 'post_text'}
5789 );
5790
5791 var myNewRecord = new TopicRecord({
5792     title: 'Do my job please',
5793     author: 'noobie',
5794     totalPosts: 1,
5795     lastPost: new Date(),
5796     lastPoster: 'Animal',
5797     excerpt: 'No way dude!'
5798 });
5799 myStore.add(myNewRecord);
5800 </code></pre>
5801  * @method create
5802  * @static
5803  */
5804 Roo.data.Record.create = function(o){
5805     var f = function(){
5806         f.superclass.constructor.apply(this, arguments);
5807     };
5808     Roo.extend(f, Roo.data.Record);
5809     var p = f.prototype;
5810     p.fields = new Roo.util.MixedCollection(false, function(field){
5811         return field.name;
5812     });
5813     for(var i = 0, len = o.length; i < len; i++){
5814         p.fields.add(new Roo.data.Field(o[i]));
5815     }
5816     f.getField = function(name){
5817         return p.fields.get(name);  
5818     };
5819     return f;
5820 };
5821
5822 Roo.data.Record.AUTO_ID = 1000;
5823 Roo.data.Record.EDIT = 'edit';
5824 Roo.data.Record.REJECT = 'reject';
5825 Roo.data.Record.COMMIT = 'commit';
5826
5827 Roo.data.Record.prototype = {
5828     /**
5829      * Readonly flag - true if this record has been modified.
5830      * @type Boolean
5831      */
5832     dirty : false,
5833     editing : false,
5834     error: null,
5835     modified: null,
5836
5837     // private
5838     join : function(store){
5839         this.store = store;
5840     },
5841
5842     /**
5843      * Set the named field to the specified value.
5844      * @param {String} name The name of the field to set.
5845      * @param {Object} value The value to set the field to.
5846      */
5847     set : function(name, value){
5848         if(this.data[name] == value){
5849             return;
5850         }
5851         this.dirty = true;
5852         if(!this.modified){
5853             this.modified = {};
5854         }
5855         if(typeof this.modified[name] == 'undefined'){
5856             this.modified[name] = this.data[name];
5857         }
5858         this.data[name] = value;
5859         if(!this.editing && this.store){
5860             this.store.afterEdit(this);
5861         }       
5862     },
5863
5864     /**
5865      * Get the value of the named field.
5866      * @param {String} name The name of the field to get the value of.
5867      * @return {Object} The value of the field.
5868      */
5869     get : function(name){
5870         return this.data[name]; 
5871     },
5872
5873     // private
5874     beginEdit : function(){
5875         this.editing = true;
5876         this.modified = {}; 
5877     },
5878
5879     // private
5880     cancelEdit : function(){
5881         this.editing = false;
5882         delete this.modified;
5883     },
5884
5885     // private
5886     endEdit : function(){
5887         this.editing = false;
5888         if(this.dirty && this.store){
5889             this.store.afterEdit(this);
5890         }
5891     },
5892
5893     /**
5894      * Usually called by the {@link Roo.data.Store} which owns the Record.
5895      * Rejects all changes made to the Record since either creation, or the last commit operation.
5896      * Modified fields are reverted to their original values.
5897      * <p>
5898      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5899      * of reject operations.
5900      */
5901     reject : function(){
5902         var m = this.modified;
5903         for(var n in m){
5904             if(typeof m[n] != "function"){
5905                 this.data[n] = m[n];
5906             }
5907         }
5908         this.dirty = false;
5909         delete this.modified;
5910         this.editing = false;
5911         if(this.store){
5912             this.store.afterReject(this);
5913         }
5914     },
5915
5916     /**
5917      * Usually called by the {@link Roo.data.Store} which owns the Record.
5918      * Commits all changes made to the Record since either creation, or the last commit operation.
5919      * <p>
5920      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5921      * of commit operations.
5922      */
5923     commit : function(){
5924         this.dirty = false;
5925         delete this.modified;
5926         this.editing = false;
5927         if(this.store){
5928             this.store.afterCommit(this);
5929         }
5930     },
5931
5932     // private
5933     hasError : function(){
5934         return this.error != null;
5935     },
5936
5937     // private
5938     clearError : function(){
5939         this.error = null;
5940     },
5941
5942     /**
5943      * Creates a copy of this record.
5944      * @param {String} id (optional) A new record id if you don't want to use this record's id
5945      * @return {Record}
5946      */
5947     copy : function(newId) {
5948         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
5949     }
5950 };/*
5951  * Based on:
5952  * Ext JS Library 1.1.1
5953  * Copyright(c) 2006-2007, Ext JS, LLC.
5954  *
5955  * Originally Released Under LGPL - original licence link has changed is not relivant.
5956  *
5957  * Fork - LGPL
5958  * <script type="text/javascript">
5959  */
5960
5961
5962
5963 /**
5964  * @class Roo.data.Store
5965  * @extends Roo.util.Observable
5966  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
5967  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
5968  * <p>
5969  * 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
5970  * has no knowledge of the format of the data returned by the Proxy.<br>
5971  * <p>
5972  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
5973  * instances from the data object. These records are cached and made available through accessor functions.
5974  * @constructor
5975  * Creates a new Store.
5976  * @param {Object} config A config object containing the objects needed for the Store to access data,
5977  * and read the data into Records.
5978  */
5979 Roo.data.Store = function(config){
5980     this.data = new Roo.util.MixedCollection(false);
5981     this.data.getKey = function(o){
5982         return o.id;
5983     };
5984     this.baseParams = {};
5985     // private
5986     this.paramNames = {
5987         "start" : "start",
5988         "limit" : "limit",
5989         "sort" : "sort",
5990         "dir" : "dir",
5991         "multisort" : "_multisort"
5992     };
5993
5994     if(config && config.data){
5995         this.inlineData = config.data;
5996         delete config.data;
5997     }
5998
5999     Roo.apply(this, config);
6000     
6001     if(this.reader){ // reader passed
6002         this.reader = Roo.factory(this.reader, Roo.data);
6003         this.reader.xmodule = this.xmodule || false;
6004         if(!this.recordType){
6005             this.recordType = this.reader.recordType;
6006         }
6007         if(this.reader.onMetaChange){
6008             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6009         }
6010     }
6011
6012     if(this.recordType){
6013         this.fields = this.recordType.prototype.fields;
6014     }
6015     this.modified = [];
6016
6017     this.addEvents({
6018         /**
6019          * @event datachanged
6020          * Fires when the data cache has changed, and a widget which is using this Store
6021          * as a Record cache should refresh its view.
6022          * @param {Store} this
6023          */
6024         datachanged : true,
6025         /**
6026          * @event metachange
6027          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6028          * @param {Store} this
6029          * @param {Object} meta The JSON metadata
6030          */
6031         metachange : true,
6032         /**
6033          * @event add
6034          * Fires when Records have been added to the Store
6035          * @param {Store} this
6036          * @param {Roo.data.Record[]} records The array of Records added
6037          * @param {Number} index The index at which the record(s) were added
6038          */
6039         add : true,
6040         /**
6041          * @event remove
6042          * Fires when a Record has been removed from the Store
6043          * @param {Store} this
6044          * @param {Roo.data.Record} record The Record that was removed
6045          * @param {Number} index The index at which the record was removed
6046          */
6047         remove : true,
6048         /**
6049          * @event update
6050          * Fires when a Record has been updated
6051          * @param {Store} this
6052          * @param {Roo.data.Record} record The Record that was updated
6053          * @param {String} operation The update operation being performed.  Value may be one of:
6054          * <pre><code>
6055  Roo.data.Record.EDIT
6056  Roo.data.Record.REJECT
6057  Roo.data.Record.COMMIT
6058          * </code></pre>
6059          */
6060         update : true,
6061         /**
6062          * @event clear
6063          * Fires when the data cache has been cleared.
6064          * @param {Store} this
6065          */
6066         clear : true,
6067         /**
6068          * @event beforeload
6069          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6070          * the load action will be canceled.
6071          * @param {Store} this
6072          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6073          */
6074         beforeload : true,
6075         /**
6076          * @event beforeloadadd
6077          * Fires after a new set of Records has been loaded.
6078          * @param {Store} this
6079          * @param {Roo.data.Record[]} records The Records that were loaded
6080          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6081          */
6082         beforeloadadd : true,
6083         /**
6084          * @event load
6085          * Fires after a new set of Records has been loaded, before they are added to the store.
6086          * @param {Store} this
6087          * @param {Roo.data.Record[]} records The Records that were loaded
6088          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6089          * @params {Object} return from reader
6090          */
6091         load : true,
6092         /**
6093          * @event loadexception
6094          * Fires if an exception occurs in the Proxy during loading.
6095          * Called with the signature of the Proxy's "loadexception" event.
6096          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6097          * 
6098          * @param {Proxy} 
6099          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6100          * @param {Object} load options 
6101          * @param {Object} jsonData from your request (normally this contains the Exception)
6102          */
6103         loadexception : true
6104     });
6105     
6106     if(this.proxy){
6107         this.proxy = Roo.factory(this.proxy, Roo.data);
6108         this.proxy.xmodule = this.xmodule || false;
6109         this.relayEvents(this.proxy,  ["loadexception"]);
6110     }
6111     this.sortToggle = {};
6112     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6113
6114     Roo.data.Store.superclass.constructor.call(this);
6115
6116     if(this.inlineData){
6117         this.loadData(this.inlineData);
6118         delete this.inlineData;
6119     }
6120 };
6121
6122 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6123      /**
6124     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6125     * without a remote query - used by combo/forms at present.
6126     */
6127     
6128     /**
6129     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6130     */
6131     /**
6132     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6133     */
6134     /**
6135     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6136     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6137     */
6138     /**
6139     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6140     * on any HTTP request
6141     */
6142     /**
6143     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6144     */
6145     /**
6146     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6147     */
6148     multiSort: false,
6149     /**
6150     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6151     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6152     */
6153     remoteSort : false,
6154
6155     /**
6156     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6157      * loaded or when a record is removed. (defaults to false).
6158     */
6159     pruneModifiedRecords : false,
6160
6161     // private
6162     lastOptions : null,
6163
6164     /**
6165      * Add Records to the Store and fires the add event.
6166      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6167      */
6168     add : function(records){
6169         records = [].concat(records);
6170         for(var i = 0, len = records.length; i < len; i++){
6171             records[i].join(this);
6172         }
6173         var index = this.data.length;
6174         this.data.addAll(records);
6175         this.fireEvent("add", this, records, index);
6176     },
6177
6178     /**
6179      * Remove a Record from the Store and fires the remove event.
6180      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6181      */
6182     remove : function(record){
6183         var index = this.data.indexOf(record);
6184         this.data.removeAt(index);
6185         if(this.pruneModifiedRecords){
6186             this.modified.remove(record);
6187         }
6188         this.fireEvent("remove", this, record, index);
6189     },
6190
6191     /**
6192      * Remove all Records from the Store and fires the clear event.
6193      */
6194     removeAll : function(){
6195         this.data.clear();
6196         if(this.pruneModifiedRecords){
6197             this.modified = [];
6198         }
6199         this.fireEvent("clear", this);
6200     },
6201
6202     /**
6203      * Inserts Records to the Store at the given index and fires the add event.
6204      * @param {Number} index The start index at which to insert the passed Records.
6205      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6206      */
6207     insert : function(index, records){
6208         records = [].concat(records);
6209         for(var i = 0, len = records.length; i < len; i++){
6210             this.data.insert(index, records[i]);
6211             records[i].join(this);
6212         }
6213         this.fireEvent("add", this, records, index);
6214     },
6215
6216     /**
6217      * Get the index within the cache of the passed Record.
6218      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6219      * @return {Number} The index of the passed Record. Returns -1 if not found.
6220      */
6221     indexOf : function(record){
6222         return this.data.indexOf(record);
6223     },
6224
6225     /**
6226      * Get the index within the cache of the Record with the passed id.
6227      * @param {String} id The id of the Record to find.
6228      * @return {Number} The index of the Record. Returns -1 if not found.
6229      */
6230     indexOfId : function(id){
6231         return this.data.indexOfKey(id);
6232     },
6233
6234     /**
6235      * Get the Record with the specified id.
6236      * @param {String} id The id of the Record to find.
6237      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6238      */
6239     getById : function(id){
6240         return this.data.key(id);
6241     },
6242
6243     /**
6244      * Get the Record at the specified index.
6245      * @param {Number} index The index of the Record to find.
6246      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6247      */
6248     getAt : function(index){
6249         return this.data.itemAt(index);
6250     },
6251
6252     /**
6253      * Returns a range of Records between specified indices.
6254      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6255      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6256      * @return {Roo.data.Record[]} An array of Records
6257      */
6258     getRange : function(start, end){
6259         return this.data.getRange(start, end);
6260     },
6261
6262     // private
6263     storeOptions : function(o){
6264         o = Roo.apply({}, o);
6265         delete o.callback;
6266         delete o.scope;
6267         this.lastOptions = o;
6268     },
6269
6270     /**
6271      * Loads the Record cache from the configured Proxy using the configured Reader.
6272      * <p>
6273      * If using remote paging, then the first load call must specify the <em>start</em>
6274      * and <em>limit</em> properties in the options.params property to establish the initial
6275      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6276      * <p>
6277      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6278      * and this call will return before the new data has been loaded. Perform any post-processing
6279      * in a callback function, or in a "load" event handler.</strong>
6280      * <p>
6281      * @param {Object} options An object containing properties which control loading options:<ul>
6282      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6283      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6284      * passed the following arguments:<ul>
6285      * <li>r : Roo.data.Record[]</li>
6286      * <li>options: Options object from the load call</li>
6287      * <li>success: Boolean success indicator</li></ul></li>
6288      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6289      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6290      * </ul>
6291      */
6292     load : function(options){
6293         options = options || {};
6294         if(this.fireEvent("beforeload", this, options) !== false){
6295             this.storeOptions(options);
6296             var p = Roo.apply(options.params || {}, this.baseParams);
6297             // if meta was not loaded from remote source.. try requesting it.
6298             if (!this.reader.metaFromRemote) {
6299                 p._requestMeta = 1;
6300             }
6301             if(this.sortInfo && this.remoteSort){
6302                 var pn = this.paramNames;
6303                 p[pn["sort"]] = this.sortInfo.field;
6304                 p[pn["dir"]] = this.sortInfo.direction;
6305             }
6306             if (this.multiSort) {
6307                 var pn = this.paramNames;
6308                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6309             }
6310             
6311             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6312         }
6313     },
6314
6315     /**
6316      * Reloads the Record cache from the configured Proxy using the configured Reader and
6317      * the options from the last load operation performed.
6318      * @param {Object} options (optional) An object containing properties which may override the options
6319      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6320      * the most recently used options are reused).
6321      */
6322     reload : function(options){
6323         this.load(Roo.applyIf(options||{}, this.lastOptions));
6324     },
6325
6326     // private
6327     // Called as a callback by the Reader during a load operation.
6328     loadRecords : function(o, options, success){
6329         if(!o || success === false){
6330             if(success !== false){
6331                 this.fireEvent("load", this, [], options, o);
6332             }
6333             if(options.callback){
6334                 options.callback.call(options.scope || this, [], options, false);
6335             }
6336             return;
6337         }
6338         // if data returned failure - throw an exception.
6339         if (o.success === false) {
6340             // show a message if no listener is registered.
6341             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6342                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6343             }
6344             // loadmask wil be hooked into this..
6345             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6346             return;
6347         }
6348         var r = o.records, t = o.totalRecords || r.length;
6349         
6350         this.fireEvent("beforeloadadd", this, r, options, o);
6351         
6352         if(!options || options.add !== true){
6353             if(this.pruneModifiedRecords){
6354                 this.modified = [];
6355             }
6356             for(var i = 0, len = r.length; i < len; i++){
6357                 r[i].join(this);
6358             }
6359             if(this.snapshot){
6360                 this.data = this.snapshot;
6361                 delete this.snapshot;
6362             }
6363             this.data.clear();
6364             this.data.addAll(r);
6365             this.totalLength = t;
6366             this.applySort();
6367             this.fireEvent("datachanged", this);
6368         }else{
6369             this.totalLength = Math.max(t, this.data.length+r.length);
6370             this.add(r);
6371         }
6372         this.fireEvent("load", this, r, options, o);
6373         if(options.callback){
6374             options.callback.call(options.scope || this, r, options, true);
6375         }
6376     },
6377
6378
6379     /**
6380      * Loads data from a passed data block. A Reader which understands the format of the data
6381      * must have been configured in the constructor.
6382      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6383      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6384      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6385      */
6386     loadData : function(o, append){
6387         var r = this.reader.readRecords(o);
6388         this.loadRecords(r, {add: append}, true);
6389     },
6390
6391     /**
6392      * Gets the number of cached records.
6393      * <p>
6394      * <em>If using paging, this may not be the total size of the dataset. If the data object
6395      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6396      * the data set size</em>
6397      */
6398     getCount : function(){
6399         return this.data.length || 0;
6400     },
6401
6402     /**
6403      * Gets the total number of records in the dataset as returned by the server.
6404      * <p>
6405      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6406      * the dataset size</em>
6407      */
6408     getTotalCount : function(){
6409         return this.totalLength || 0;
6410     },
6411
6412     /**
6413      * Returns the sort state of the Store as an object with two properties:
6414      * <pre><code>
6415  field {String} The name of the field by which the Records are sorted
6416  direction {String} The sort order, "ASC" or "DESC"
6417      * </code></pre>
6418      */
6419     getSortState : function(){
6420         return this.sortInfo;
6421     },
6422
6423     // private
6424     applySort : function(){
6425         if(this.sortInfo && !this.remoteSort){
6426             var s = this.sortInfo, f = s.field;
6427             var st = this.fields.get(f).sortType;
6428             var fn = function(r1, r2){
6429                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6430                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6431             };
6432             this.data.sort(s.direction, fn);
6433             if(this.snapshot && this.snapshot != this.data){
6434                 this.snapshot.sort(s.direction, fn);
6435             }
6436         }
6437     },
6438
6439     /**
6440      * Sets the default sort column and order to be used by the next load operation.
6441      * @param {String} fieldName The name of the field to sort by.
6442      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6443      */
6444     setDefaultSort : function(field, dir){
6445         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6446     },
6447
6448     /**
6449      * Sort the Records.
6450      * If remote sorting is used, the sort is performed on the server, and the cache is
6451      * reloaded. If local sorting is used, the cache is sorted internally.
6452      * @param {String} fieldName The name of the field to sort by.
6453      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6454      */
6455     sort : function(fieldName, dir){
6456         var f = this.fields.get(fieldName);
6457         if(!dir){
6458             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6459             
6460             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6461                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6462             }else{
6463                 dir = f.sortDir;
6464             }
6465         }
6466         this.sortToggle[f.name] = dir;
6467         this.sortInfo = {field: f.name, direction: dir};
6468         if(!this.remoteSort){
6469             this.applySort();
6470             this.fireEvent("datachanged", this);
6471         }else{
6472             this.load(this.lastOptions);
6473         }
6474     },
6475
6476     /**
6477      * Calls the specified function for each of the Records in the cache.
6478      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6479      * Returning <em>false</em> aborts and exits the iteration.
6480      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6481      */
6482     each : function(fn, scope){
6483         this.data.each(fn, scope);
6484     },
6485
6486     /**
6487      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6488      * (e.g., during paging).
6489      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6490      */
6491     getModifiedRecords : function(){
6492         return this.modified;
6493     },
6494
6495     // private
6496     createFilterFn : function(property, value, anyMatch){
6497         if(!value.exec){ // not a regex
6498             value = String(value);
6499             if(value.length == 0){
6500                 return false;
6501             }
6502             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6503         }
6504         return function(r){
6505             return value.test(r.data[property]);
6506         };
6507     },
6508
6509     /**
6510      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6511      * @param {String} property A field on your records
6512      * @param {Number} start The record index to start at (defaults to 0)
6513      * @param {Number} end The last record index to include (defaults to length - 1)
6514      * @return {Number} The sum
6515      */
6516     sum : function(property, start, end){
6517         var rs = this.data.items, v = 0;
6518         start = start || 0;
6519         end = (end || end === 0) ? end : rs.length-1;
6520
6521         for(var i = start; i <= end; i++){
6522             v += (rs[i].data[property] || 0);
6523         }
6524         return v;
6525     },
6526
6527     /**
6528      * Filter the records by a specified property.
6529      * @param {String} field A field on your records
6530      * @param {String/RegExp} value Either a string that the field
6531      * should start with or a RegExp to test against the field
6532      * @param {Boolean} anyMatch True to match any part not just the beginning
6533      */
6534     filter : function(property, value, anyMatch){
6535         var fn = this.createFilterFn(property, value, anyMatch);
6536         return fn ? this.filterBy(fn) : this.clearFilter();
6537     },
6538
6539     /**
6540      * Filter by a function. The specified function will be called with each
6541      * record in this data source. If the function returns true the record is included,
6542      * otherwise it is filtered.
6543      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6544      * @param {Object} scope (optional) The scope of the function (defaults to this)
6545      */
6546     filterBy : function(fn, scope){
6547         this.snapshot = this.snapshot || this.data;
6548         this.data = this.queryBy(fn, scope||this);
6549         this.fireEvent("datachanged", this);
6550     },
6551
6552     /**
6553      * Query the records by a specified property.
6554      * @param {String} field A field on your records
6555      * @param {String/RegExp} value Either a string that the field
6556      * should start with or a RegExp to test against the field
6557      * @param {Boolean} anyMatch True to match any part not just the beginning
6558      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6559      */
6560     query : function(property, value, anyMatch){
6561         var fn = this.createFilterFn(property, value, anyMatch);
6562         return fn ? this.queryBy(fn) : this.data.clone();
6563     },
6564
6565     /**
6566      * Query by a function. The specified function will be called with each
6567      * record in this data source. If the function returns true the record is included
6568      * in the results.
6569      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6570      * @param {Object} scope (optional) The scope of the function (defaults to this)
6571       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6572      **/
6573     queryBy : function(fn, scope){
6574         var data = this.snapshot || this.data;
6575         return data.filterBy(fn, scope||this);
6576     },
6577
6578     /**
6579      * Collects unique values for a particular dataIndex from this store.
6580      * @param {String} dataIndex The property to collect
6581      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6582      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6583      * @return {Array} An array of the unique values
6584      **/
6585     collect : function(dataIndex, allowNull, bypassFilter){
6586         var d = (bypassFilter === true && this.snapshot) ?
6587                 this.snapshot.items : this.data.items;
6588         var v, sv, r = [], l = {};
6589         for(var i = 0, len = d.length; i < len; i++){
6590             v = d[i].data[dataIndex];
6591             sv = String(v);
6592             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6593                 l[sv] = true;
6594                 r[r.length] = v;
6595             }
6596         }
6597         return r;
6598     },
6599
6600     /**
6601      * Revert to a view of the Record cache with no filtering applied.
6602      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6603      */
6604     clearFilter : function(suppressEvent){
6605         if(this.snapshot && this.snapshot != this.data){
6606             this.data = this.snapshot;
6607             delete this.snapshot;
6608             if(suppressEvent !== true){
6609                 this.fireEvent("datachanged", this);
6610             }
6611         }
6612     },
6613
6614     // private
6615     afterEdit : function(record){
6616         if(this.modified.indexOf(record) == -1){
6617             this.modified.push(record);
6618         }
6619         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6620     },
6621     
6622     // private
6623     afterReject : function(record){
6624         this.modified.remove(record);
6625         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6626     },
6627
6628     // private
6629     afterCommit : function(record){
6630         this.modified.remove(record);
6631         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6632     },
6633
6634     /**
6635      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6636      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6637      */
6638     commitChanges : function(){
6639         var m = this.modified.slice(0);
6640         this.modified = [];
6641         for(var i = 0, len = m.length; i < len; i++){
6642             m[i].commit();
6643         }
6644     },
6645
6646     /**
6647      * Cancel outstanding changes on all changed records.
6648      */
6649     rejectChanges : function(){
6650         var m = this.modified.slice(0);
6651         this.modified = [];
6652         for(var i = 0, len = m.length; i < len; i++){
6653             m[i].reject();
6654         }
6655     },
6656
6657     onMetaChange : function(meta, rtype, o){
6658         this.recordType = rtype;
6659         this.fields = rtype.prototype.fields;
6660         delete this.snapshot;
6661         this.sortInfo = meta.sortInfo || this.sortInfo;
6662         this.modified = [];
6663         this.fireEvent('metachange', this, this.reader.meta);
6664     }
6665 });/*
6666  * Based on:
6667  * Ext JS Library 1.1.1
6668  * Copyright(c) 2006-2007, Ext JS, LLC.
6669  *
6670  * Originally Released Under LGPL - original licence link has changed is not relivant.
6671  *
6672  * Fork - LGPL
6673  * <script type="text/javascript">
6674  */
6675
6676 /**
6677  * @class Roo.data.SimpleStore
6678  * @extends Roo.data.Store
6679  * Small helper class to make creating Stores from Array data easier.
6680  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6681  * @cfg {Array} fields An array of field definition objects, or field name strings.
6682  * @cfg {Array} data The multi-dimensional array of data
6683  * @constructor
6684  * @param {Object} config
6685  */
6686 Roo.data.SimpleStore = function(config){
6687     Roo.data.SimpleStore.superclass.constructor.call(this, {
6688         isLocal : true,
6689         reader: new Roo.data.ArrayReader({
6690                 id: config.id
6691             },
6692             Roo.data.Record.create(config.fields)
6693         ),
6694         proxy : new Roo.data.MemoryProxy(config.data)
6695     });
6696     this.load();
6697 };
6698 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6699  * Based on:
6700  * Ext JS Library 1.1.1
6701  * Copyright(c) 2006-2007, Ext JS, LLC.
6702  *
6703  * Originally Released Under LGPL - original licence link has changed is not relivant.
6704  *
6705  * Fork - LGPL
6706  * <script type="text/javascript">
6707  */
6708
6709 /**
6710 /**
6711  * @extends Roo.data.Store
6712  * @class Roo.data.JsonStore
6713  * Small helper class to make creating Stores for JSON data easier. <br/>
6714 <pre><code>
6715 var store = new Roo.data.JsonStore({
6716     url: 'get-images.php',
6717     root: 'images',
6718     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6719 });
6720 </code></pre>
6721  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6722  * JsonReader and HttpProxy (unless inline data is provided).</b>
6723  * @cfg {Array} fields An array of field definition objects, or field name strings.
6724  * @constructor
6725  * @param {Object} config
6726  */
6727 Roo.data.JsonStore = function(c){
6728     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6729         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6730         reader: new Roo.data.JsonReader(c, c.fields)
6731     }));
6732 };
6733 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6734  * Based on:
6735  * Ext JS Library 1.1.1
6736  * Copyright(c) 2006-2007, Ext JS, LLC.
6737  *
6738  * Originally Released Under LGPL - original licence link has changed is not relivant.
6739  *
6740  * Fork - LGPL
6741  * <script type="text/javascript">
6742  */
6743
6744  
6745 Roo.data.Field = function(config){
6746     if(typeof config == "string"){
6747         config = {name: config};
6748     }
6749     Roo.apply(this, config);
6750     
6751     if(!this.type){
6752         this.type = "auto";
6753     }
6754     
6755     var st = Roo.data.SortTypes;
6756     // named sortTypes are supported, here we look them up
6757     if(typeof this.sortType == "string"){
6758         this.sortType = st[this.sortType];
6759     }
6760     
6761     // set default sortType for strings and dates
6762     if(!this.sortType){
6763         switch(this.type){
6764             case "string":
6765                 this.sortType = st.asUCString;
6766                 break;
6767             case "date":
6768                 this.sortType = st.asDate;
6769                 break;
6770             default:
6771                 this.sortType = st.none;
6772         }
6773     }
6774
6775     // define once
6776     var stripRe = /[\$,%]/g;
6777
6778     // prebuilt conversion function for this field, instead of
6779     // switching every time we're reading a value
6780     if(!this.convert){
6781         var cv, dateFormat = this.dateFormat;
6782         switch(this.type){
6783             case "":
6784             case "auto":
6785             case undefined:
6786                 cv = function(v){ return v; };
6787                 break;
6788             case "string":
6789                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6790                 break;
6791             case "int":
6792                 cv = function(v){
6793                     return v !== undefined && v !== null && v !== '' ?
6794                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6795                     };
6796                 break;
6797             case "float":
6798                 cv = function(v){
6799                     return v !== undefined && v !== null && v !== '' ?
6800                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6801                     };
6802                 break;
6803             case "bool":
6804             case "boolean":
6805                 cv = function(v){ return v === true || v === "true" || v == 1; };
6806                 break;
6807             case "date":
6808                 cv = function(v){
6809                     if(!v){
6810                         return '';
6811                     }
6812                     if(v instanceof Date){
6813                         return v;
6814                     }
6815                     if(dateFormat){
6816                         if(dateFormat == "timestamp"){
6817                             return new Date(v*1000);
6818                         }
6819                         return Date.parseDate(v, dateFormat);
6820                     }
6821                     var parsed = Date.parse(v);
6822                     return parsed ? new Date(parsed) : null;
6823                 };
6824              break;
6825             
6826         }
6827         this.convert = cv;
6828     }
6829 };
6830
6831 Roo.data.Field.prototype = {
6832     dateFormat: null,
6833     defaultValue: "",
6834     mapping: null,
6835     sortType : null,
6836     sortDir : "ASC"
6837 };/*
6838  * Based on:
6839  * Ext JS Library 1.1.1
6840  * Copyright(c) 2006-2007, Ext JS, LLC.
6841  *
6842  * Originally Released Under LGPL - original licence link has changed is not relivant.
6843  *
6844  * Fork - LGPL
6845  * <script type="text/javascript">
6846  */
6847  
6848 // Base class for reading structured data from a data source.  This class is intended to be
6849 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6850
6851 /**
6852  * @class Roo.data.DataReader
6853  * Base class for reading structured data from a data source.  This class is intended to be
6854  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6855  */
6856
6857 Roo.data.DataReader = function(meta, recordType){
6858     
6859     this.meta = meta;
6860     
6861     this.recordType = recordType instanceof Array ? 
6862         Roo.data.Record.create(recordType) : recordType;
6863 };
6864
6865 Roo.data.DataReader.prototype = {
6866      /**
6867      * Create an empty record
6868      * @param {Object} data (optional) - overlay some values
6869      * @return {Roo.data.Record} record created.
6870      */
6871     newRow :  function(d) {
6872         var da =  {};
6873         this.recordType.prototype.fields.each(function(c) {
6874             switch( c.type) {
6875                 case 'int' : da[c.name] = 0; break;
6876                 case 'date' : da[c.name] = new Date(); break;
6877                 case 'float' : da[c.name] = 0.0; break;
6878                 case 'boolean' : da[c.name] = false; break;
6879                 default : da[c.name] = ""; break;
6880             }
6881             
6882         });
6883         return new this.recordType(Roo.apply(da, d));
6884     }
6885     
6886 };/*
6887  * Based on:
6888  * Ext JS Library 1.1.1
6889  * Copyright(c) 2006-2007, Ext JS, LLC.
6890  *
6891  * Originally Released Under LGPL - original licence link has changed is not relivant.
6892  *
6893  * Fork - LGPL
6894  * <script type="text/javascript">
6895  */
6896
6897 /**
6898  * @class Roo.data.DataProxy
6899  * @extends Roo.data.Observable
6900  * This class is an abstract base class for implementations which provide retrieval of
6901  * unformatted data objects.<br>
6902  * <p>
6903  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6904  * (of the appropriate type which knows how to parse the data object) to provide a block of
6905  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6906  * <p>
6907  * Custom implementations must implement the load method as described in
6908  * {@link Roo.data.HttpProxy#load}.
6909  */
6910 Roo.data.DataProxy = function(){
6911     this.addEvents({
6912         /**
6913          * @event beforeload
6914          * Fires before a network request is made to retrieve a data object.
6915          * @param {Object} This DataProxy object.
6916          * @param {Object} params The params parameter to the load function.
6917          */
6918         beforeload : true,
6919         /**
6920          * @event load
6921          * Fires before the load method's callback is called.
6922          * @param {Object} This DataProxy object.
6923          * @param {Object} o The data object.
6924          * @param {Object} arg The callback argument object passed to the load function.
6925          */
6926         load : true,
6927         /**
6928          * @event loadexception
6929          * Fires if an Exception occurs during data retrieval.
6930          * @param {Object} This DataProxy object.
6931          * @param {Object} o The data object.
6932          * @param {Object} arg The callback argument object passed to the load function.
6933          * @param {Object} e The Exception.
6934          */
6935         loadexception : true
6936     });
6937     Roo.data.DataProxy.superclass.constructor.call(this);
6938 };
6939
6940 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
6941
6942     /**
6943      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
6944      */
6945 /*
6946  * Based on:
6947  * Ext JS Library 1.1.1
6948  * Copyright(c) 2006-2007, Ext JS, LLC.
6949  *
6950  * Originally Released Under LGPL - original licence link has changed is not relivant.
6951  *
6952  * Fork - LGPL
6953  * <script type="text/javascript">
6954  */
6955 /**
6956  * @class Roo.data.MemoryProxy
6957  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
6958  * to the Reader when its load method is called.
6959  * @constructor
6960  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
6961  */
6962 Roo.data.MemoryProxy = function(data){
6963     if (data.data) {
6964         data = data.data;
6965     }
6966     Roo.data.MemoryProxy.superclass.constructor.call(this);
6967     this.data = data;
6968 };
6969
6970 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
6971     /**
6972      * Load data from the requested source (in this case an in-memory
6973      * data object passed to the constructor), read the data object into
6974      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6975      * process that block using the passed callback.
6976      * @param {Object} params This parameter is not used by the MemoryProxy class.
6977      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6978      * object into a block of Roo.data.Records.
6979      * @param {Function} callback The function into which to pass the block of Roo.data.records.
6980      * The function must be passed <ul>
6981      * <li>The Record block object</li>
6982      * <li>The "arg" argument from the load function</li>
6983      * <li>A boolean success indicator</li>
6984      * </ul>
6985      * @param {Object} scope The scope in which to call the callback
6986      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6987      */
6988     load : function(params, reader, callback, scope, arg){
6989         params = params || {};
6990         var result;
6991         try {
6992             result = reader.readRecords(this.data);
6993         }catch(e){
6994             this.fireEvent("loadexception", this, arg, null, e);
6995             callback.call(scope, null, arg, false);
6996             return;
6997         }
6998         callback.call(scope, result, arg, true);
6999     },
7000     
7001     // private
7002     update : function(params, records){
7003         
7004     }
7005 });/*
7006  * Based on:
7007  * Ext JS Library 1.1.1
7008  * Copyright(c) 2006-2007, Ext JS, LLC.
7009  *
7010  * Originally Released Under LGPL - original licence link has changed is not relivant.
7011  *
7012  * Fork - LGPL
7013  * <script type="text/javascript">
7014  */
7015 /**
7016  * @class Roo.data.HttpProxy
7017  * @extends Roo.data.DataProxy
7018  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7019  * configured to reference a certain URL.<br><br>
7020  * <p>
7021  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7022  * from which the running page was served.<br><br>
7023  * <p>
7024  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7025  * <p>
7026  * Be aware that to enable the browser to parse an XML document, the server must set
7027  * the Content-Type header in the HTTP response to "text/xml".
7028  * @constructor
7029  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7030  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7031  * will be used to make the request.
7032  */
7033 Roo.data.HttpProxy = function(conn){
7034     Roo.data.HttpProxy.superclass.constructor.call(this);
7035     // is conn a conn config or a real conn?
7036     this.conn = conn;
7037     this.useAjax = !conn || !conn.events;
7038   
7039 };
7040
7041 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7042     // thse are take from connection...
7043     
7044     /**
7045      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7046      */
7047     /**
7048      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7049      * extra parameters to each request made by this object. (defaults to undefined)
7050      */
7051     /**
7052      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7053      *  to each request made by this object. (defaults to undefined)
7054      */
7055     /**
7056      * @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)
7057      */
7058     /**
7059      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7060      */
7061      /**
7062      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7063      * @type Boolean
7064      */
7065   
7066
7067     /**
7068      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7069      * @type Boolean
7070      */
7071     /**
7072      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7073      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7074      * a finer-grained basis than the DataProxy events.
7075      */
7076     getConnection : function(){
7077         return this.useAjax ? Roo.Ajax : this.conn;
7078     },
7079
7080     /**
7081      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7082      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7083      * process that block using the passed callback.
7084      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7085      * for the request to the remote server.
7086      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7087      * object into a block of Roo.data.Records.
7088      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7089      * The function must be passed <ul>
7090      * <li>The Record block object</li>
7091      * <li>The "arg" argument from the load function</li>
7092      * <li>A boolean success indicator</li>
7093      * </ul>
7094      * @param {Object} scope The scope in which to call the callback
7095      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7096      */
7097     load : function(params, reader, callback, scope, arg){
7098         if(this.fireEvent("beforeload", this, params) !== false){
7099             var  o = {
7100                 params : params || {},
7101                 request: {
7102                     callback : callback,
7103                     scope : scope,
7104                     arg : arg
7105                 },
7106                 reader: reader,
7107                 callback : this.loadResponse,
7108                 scope: this
7109             };
7110             if(this.useAjax){
7111                 Roo.applyIf(o, this.conn);
7112                 if(this.activeRequest){
7113                     Roo.Ajax.abort(this.activeRequest);
7114                 }
7115                 this.activeRequest = Roo.Ajax.request(o);
7116             }else{
7117                 this.conn.request(o);
7118             }
7119         }else{
7120             callback.call(scope||this, null, arg, false);
7121         }
7122     },
7123
7124     // private
7125     loadResponse : function(o, success, response){
7126         delete this.activeRequest;
7127         if(!success){
7128             this.fireEvent("loadexception", this, o, response);
7129             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7130             return;
7131         }
7132         var result;
7133         try {
7134             result = o.reader.read(response);
7135         }catch(e){
7136             this.fireEvent("loadexception", this, o, response, e);
7137             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7138             return;
7139         }
7140         
7141         this.fireEvent("load", this, o, o.request.arg);
7142         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7143     },
7144
7145     // private
7146     update : function(dataSet){
7147
7148     },
7149
7150     // private
7151     updateResponse : function(dataSet){
7152
7153     }
7154 });/*
7155  * Based on:
7156  * Ext JS Library 1.1.1
7157  * Copyright(c) 2006-2007, Ext JS, LLC.
7158  *
7159  * Originally Released Under LGPL - original licence link has changed is not relivant.
7160  *
7161  * Fork - LGPL
7162  * <script type="text/javascript">
7163  */
7164
7165 /**
7166  * @class Roo.data.ScriptTagProxy
7167  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7168  * other than the originating domain of the running page.<br><br>
7169  * <p>
7170  * <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
7171  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7172  * <p>
7173  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7174  * source code that is used as the source inside a &lt;script> tag.<br><br>
7175  * <p>
7176  * In order for the browser to process the returned data, the server must wrap the data object
7177  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7178  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7179  * depending on whether the callback name was passed:
7180  * <p>
7181  * <pre><code>
7182 boolean scriptTag = false;
7183 String cb = request.getParameter("callback");
7184 if (cb != null) {
7185     scriptTag = true;
7186     response.setContentType("text/javascript");
7187 } else {
7188     response.setContentType("application/x-json");
7189 }
7190 Writer out = response.getWriter();
7191 if (scriptTag) {
7192     out.write(cb + "(");
7193 }
7194 out.print(dataBlock.toJsonString());
7195 if (scriptTag) {
7196     out.write(");");
7197 }
7198 </pre></code>
7199  *
7200  * @constructor
7201  * @param {Object} config A configuration object.
7202  */
7203 Roo.data.ScriptTagProxy = function(config){
7204     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7205     Roo.apply(this, config);
7206     this.head = document.getElementsByTagName("head")[0];
7207 };
7208
7209 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7210
7211 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7212     /**
7213      * @cfg {String} url The URL from which to request the data object.
7214      */
7215     /**
7216      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7217      */
7218     timeout : 30000,
7219     /**
7220      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7221      * the server the name of the callback function set up by the load call to process the returned data object.
7222      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7223      * javascript output which calls this named function passing the data object as its only parameter.
7224      */
7225     callbackParam : "callback",
7226     /**
7227      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7228      * name to the request.
7229      */
7230     nocache : true,
7231
7232     /**
7233      * Load data from the configured URL, read the data object into
7234      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7235      * process that block using the passed callback.
7236      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7237      * for the request to the remote server.
7238      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7239      * object into a block of Roo.data.Records.
7240      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7241      * The function must be passed <ul>
7242      * <li>The Record block object</li>
7243      * <li>The "arg" argument from the load function</li>
7244      * <li>A boolean success indicator</li>
7245      * </ul>
7246      * @param {Object} scope The scope in which to call the callback
7247      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7248      */
7249     load : function(params, reader, callback, scope, arg){
7250         if(this.fireEvent("beforeload", this, params) !== false){
7251
7252             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7253
7254             var url = this.url;
7255             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7256             if(this.nocache){
7257                 url += "&_dc=" + (new Date().getTime());
7258             }
7259             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7260             var trans = {
7261                 id : transId,
7262                 cb : "stcCallback"+transId,
7263                 scriptId : "stcScript"+transId,
7264                 params : params,
7265                 arg : arg,
7266                 url : url,
7267                 callback : callback,
7268                 scope : scope,
7269                 reader : reader
7270             };
7271             var conn = this;
7272
7273             window[trans.cb] = function(o){
7274                 conn.handleResponse(o, trans);
7275             };
7276
7277             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7278
7279             if(this.autoAbort !== false){
7280                 this.abort();
7281             }
7282
7283             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7284
7285             var script = document.createElement("script");
7286             script.setAttribute("src", url);
7287             script.setAttribute("type", "text/javascript");
7288             script.setAttribute("id", trans.scriptId);
7289             this.head.appendChild(script);
7290
7291             this.trans = trans;
7292         }else{
7293             callback.call(scope||this, null, arg, false);
7294         }
7295     },
7296
7297     // private
7298     isLoading : function(){
7299         return this.trans ? true : false;
7300     },
7301
7302     /**
7303      * Abort the current server request.
7304      */
7305     abort : function(){
7306         if(this.isLoading()){
7307             this.destroyTrans(this.trans);
7308         }
7309     },
7310
7311     // private
7312     destroyTrans : function(trans, isLoaded){
7313         this.head.removeChild(document.getElementById(trans.scriptId));
7314         clearTimeout(trans.timeoutId);
7315         if(isLoaded){
7316             window[trans.cb] = undefined;
7317             try{
7318                 delete window[trans.cb];
7319             }catch(e){}
7320         }else{
7321             // if hasn't been loaded, wait for load to remove it to prevent script error
7322             window[trans.cb] = function(){
7323                 window[trans.cb] = undefined;
7324                 try{
7325                     delete window[trans.cb];
7326                 }catch(e){}
7327             };
7328         }
7329     },
7330
7331     // private
7332     handleResponse : function(o, trans){
7333         this.trans = false;
7334         this.destroyTrans(trans, true);
7335         var result;
7336         try {
7337             result = trans.reader.readRecords(o);
7338         }catch(e){
7339             this.fireEvent("loadexception", this, o, trans.arg, e);
7340             trans.callback.call(trans.scope||window, null, trans.arg, false);
7341             return;
7342         }
7343         this.fireEvent("load", this, o, trans.arg);
7344         trans.callback.call(trans.scope||window, result, trans.arg, true);
7345     },
7346
7347     // private
7348     handleFailure : function(trans){
7349         this.trans = false;
7350         this.destroyTrans(trans, false);
7351         this.fireEvent("loadexception", this, null, trans.arg);
7352         trans.callback.call(trans.scope||window, null, trans.arg, false);
7353     }
7354 });/*
7355  * Based on:
7356  * Ext JS Library 1.1.1
7357  * Copyright(c) 2006-2007, Ext JS, LLC.
7358  *
7359  * Originally Released Under LGPL - original licence link has changed is not relivant.
7360  *
7361  * Fork - LGPL
7362  * <script type="text/javascript">
7363  */
7364
7365 /**
7366  * @class Roo.data.JsonReader
7367  * @extends Roo.data.DataReader
7368  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7369  * based on mappings in a provided Roo.data.Record constructor.
7370  * 
7371  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7372  * in the reply previously. 
7373  * 
7374  * <p>
7375  * Example code:
7376  * <pre><code>
7377 var RecordDef = Roo.data.Record.create([
7378     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7379     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7380 ]);
7381 var myReader = new Roo.data.JsonReader({
7382     totalProperty: "results",    // The property which contains the total dataset size (optional)
7383     root: "rows",                // The property which contains an Array of row objects
7384     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7385 }, RecordDef);
7386 </code></pre>
7387  * <p>
7388  * This would consume a JSON file like this:
7389  * <pre><code>
7390 { 'results': 2, 'rows': [
7391     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7392     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7393 }
7394 </code></pre>
7395  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7396  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7397  * paged from the remote server.
7398  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7399  * @cfg {String} root name of the property which contains the Array of row objects.
7400  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7401  * @constructor
7402  * Create a new JsonReader
7403  * @param {Object} meta Metadata configuration options
7404  * @param {Object} recordType Either an Array of field definition objects,
7405  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7406  */
7407 Roo.data.JsonReader = function(meta, recordType){
7408     
7409     meta = meta || {};
7410     // set some defaults:
7411     Roo.applyIf(meta, {
7412         totalProperty: 'total',
7413         successProperty : 'success',
7414         root : 'data',
7415         id : 'id'
7416     });
7417     
7418     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7419 };
7420 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7421     
7422     /**
7423      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7424      * Used by Store query builder to append _requestMeta to params.
7425      * 
7426      */
7427     metaFromRemote : false,
7428     /**
7429      * This method is only used by a DataProxy which has retrieved data from a remote server.
7430      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7431      * @return {Object} data A data block which is used by an Roo.data.Store object as
7432      * a cache of Roo.data.Records.
7433      */
7434     read : function(response){
7435         var json = response.responseText;
7436        
7437         var o = /* eval:var:o */ eval("("+json+")");
7438         if(!o) {
7439             throw {message: "JsonReader.read: Json object not found"};
7440         }
7441         
7442         if(o.metaData){
7443             
7444             delete this.ef;
7445             this.metaFromRemote = true;
7446             this.meta = o.metaData;
7447             this.recordType = Roo.data.Record.create(o.metaData.fields);
7448             this.onMetaChange(this.meta, this.recordType, o);
7449         }
7450         return this.readRecords(o);
7451     },
7452
7453     // private function a store will implement
7454     onMetaChange : function(meta, recordType, o){
7455
7456     },
7457
7458     /**
7459          * @ignore
7460          */
7461     simpleAccess: function(obj, subsc) {
7462         return obj[subsc];
7463     },
7464
7465         /**
7466          * @ignore
7467          */
7468     getJsonAccessor: function(){
7469         var re = /[\[\.]/;
7470         return function(expr) {
7471             try {
7472                 return(re.test(expr))
7473                     ? new Function("obj", "return obj." + expr)
7474                     : function(obj){
7475                         return obj[expr];
7476                     };
7477             } catch(e){}
7478             return Roo.emptyFn;
7479         };
7480     }(),
7481
7482     /**
7483      * Create a data block containing Roo.data.Records from an XML document.
7484      * @param {Object} o An object which contains an Array of row objects in the property specified
7485      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7486      * which contains the total size of the dataset.
7487      * @return {Object} data A data block which is used by an Roo.data.Store object as
7488      * a cache of Roo.data.Records.
7489      */
7490     readRecords : function(o){
7491         /**
7492          * After any data loads, the raw JSON data is available for further custom processing.
7493          * @type Object
7494          */
7495         this.o = o;
7496         var s = this.meta, Record = this.recordType,
7497             f = Record.prototype.fields, fi = f.items, fl = f.length;
7498
7499 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7500         if (!this.ef) {
7501             if(s.totalProperty) {
7502                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7503                 }
7504                 if(s.successProperty) {
7505                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7506                 }
7507                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7508                 if (s.id) {
7509                         var g = this.getJsonAccessor(s.id);
7510                         this.getId = function(rec) {
7511                                 var r = g(rec);
7512                                 return (r === undefined || r === "") ? null : r;
7513                         };
7514                 } else {
7515                         this.getId = function(){return null;};
7516                 }
7517             this.ef = [];
7518             for(var jj = 0; jj < fl; jj++){
7519                 f = fi[jj];
7520                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7521                 this.ef[jj] = this.getJsonAccessor(map);
7522             }
7523         }
7524
7525         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7526         if(s.totalProperty){
7527             var vt = parseInt(this.getTotal(o), 10);
7528             if(!isNaN(vt)){
7529                 totalRecords = vt;
7530             }
7531         }
7532         if(s.successProperty){
7533             var vs = this.getSuccess(o);
7534             if(vs === false || vs === 'false'){
7535                 success = false;
7536             }
7537         }
7538         var records = [];
7539             for(var i = 0; i < c; i++){
7540                     var n = root[i];
7541                 var values = {};
7542                 var id = this.getId(n);
7543                 for(var j = 0; j < fl; j++){
7544                     f = fi[j];
7545                 var v = this.ef[j](n);
7546                 if (!f.convert) {
7547                     Roo.log('missing convert for ' + f.name);
7548                     Roo.log(f);
7549                     continue;
7550                 }
7551                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7552                 }
7553                 var record = new Record(values, id);
7554                 record.json = n;
7555                 records[i] = record;
7556             }
7557             return {
7558             raw : o,
7559                 success : success,
7560                 records : records,
7561                 totalRecords : totalRecords
7562             };
7563     }
7564 });/*
7565  * Based on:
7566  * Ext JS Library 1.1.1
7567  * Copyright(c) 2006-2007, Ext JS, LLC.
7568  *
7569  * Originally Released Under LGPL - original licence link has changed is not relivant.
7570  *
7571  * Fork - LGPL
7572  * <script type="text/javascript">
7573  */
7574
7575 /**
7576  * @class Roo.data.ArrayReader
7577  * @extends Roo.data.DataReader
7578  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7579  * Each element of that Array represents a row of data fields. The
7580  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7581  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7582  * <p>
7583  * Example code:.
7584  * <pre><code>
7585 var RecordDef = Roo.data.Record.create([
7586     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7587     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7588 ]);
7589 var myReader = new Roo.data.ArrayReader({
7590     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7591 }, RecordDef);
7592 </code></pre>
7593  * <p>
7594  * This would consume an Array like this:
7595  * <pre><code>
7596 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7597   </code></pre>
7598  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7599  * @constructor
7600  * Create a new JsonReader
7601  * @param {Object} meta Metadata configuration options.
7602  * @param {Object} recordType Either an Array of field definition objects
7603  * as specified to {@link Roo.data.Record#create},
7604  * or an {@link Roo.data.Record} object
7605  * created using {@link Roo.data.Record#create}.
7606  */
7607 Roo.data.ArrayReader = function(meta, recordType){
7608     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7609 };
7610
7611 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7612     /**
7613      * Create a data block containing Roo.data.Records from an XML document.
7614      * @param {Object} o An Array of row objects which represents the dataset.
7615      * @return {Object} data A data block which is used by an Roo.data.Store object as
7616      * a cache of Roo.data.Records.
7617      */
7618     readRecords : function(o){
7619         var sid = this.meta ? this.meta.id : null;
7620         var recordType = this.recordType, fields = recordType.prototype.fields;
7621         var records = [];
7622         var root = o;
7623             for(var i = 0; i < root.length; i++){
7624                     var n = root[i];
7625                 var values = {};
7626                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7627                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7628                 var f = fields.items[j];
7629                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7630                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7631                 v = f.convert(v);
7632                 values[f.name] = v;
7633             }
7634                 var record = new recordType(values, id);
7635                 record.json = n;
7636                 records[records.length] = record;
7637             }
7638             return {
7639                 records : records,
7640                 totalRecords : records.length
7641             };
7642     }
7643 });/*
7644  * - LGPL
7645  * * 
7646  */
7647
7648 /**
7649  * @class Roo.bootstrap.ComboBox
7650  * @extends Roo.bootstrap.TriggerField
7651  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7652  * @cfg {Boolean} append (true|false) default false
7653  * @constructor
7654  * Create a new ComboBox.
7655  * @param {Object} config Configuration options
7656  */
7657 Roo.bootstrap.ComboBox = function(config){
7658     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7659     this.addEvents({
7660         /**
7661          * @event expand
7662          * Fires when the dropdown list is expanded
7663              * @param {Roo.bootstrap.ComboBox} combo This combo box
7664              */
7665         'expand' : true,
7666         /**
7667          * @event collapse
7668          * Fires when the dropdown list is collapsed
7669              * @param {Roo.bootstrap.ComboBox} combo This combo box
7670              */
7671         'collapse' : true,
7672         /**
7673          * @event beforeselect
7674          * Fires before a list item is selected. Return false to cancel the selection.
7675              * @param {Roo.bootstrap.ComboBox} combo This combo box
7676              * @param {Roo.data.Record} record The data record returned from the underlying store
7677              * @param {Number} index The index of the selected item in the dropdown list
7678              */
7679         'beforeselect' : true,
7680         /**
7681          * @event select
7682          * Fires when a list item is selected
7683              * @param {Roo.bootstrap.ComboBox} combo This combo box
7684              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7685              * @param {Number} index The index of the selected item in the dropdown list
7686              */
7687         'select' : true,
7688         /**
7689          * @event beforequery
7690          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7691          * The event object passed has these properties:
7692              * @param {Roo.bootstrap.ComboBox} combo This combo box
7693              * @param {String} query The query
7694              * @param {Boolean} forceAll true to force "all" query
7695              * @param {Boolean} cancel true to cancel the query
7696              * @param {Object} e The query event object
7697              */
7698         'beforequery': true,
7699          /**
7700          * @event add
7701          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7702              * @param {Roo.bootstrap.ComboBox} combo This combo box
7703              */
7704         'add' : true,
7705         /**
7706          * @event edit
7707          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7708              * @param {Roo.bootstrap.ComboBox} combo This combo box
7709              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7710              */
7711         'edit' : true,
7712         /**
7713          * @event remove
7714          * Fires when the remove value from the combobox array
7715              * @param {Roo.bootstrap.ComboBox} combo This combo box
7716              */
7717         'remove' : true
7718         
7719     });
7720     
7721     
7722     this.selectedIndex = -1;
7723     if(this.mode == 'local'){
7724         if(config.queryDelay === undefined){
7725             this.queryDelay = 10;
7726         }
7727         if(config.minChars === undefined){
7728             this.minChars = 0;
7729         }
7730     }
7731 };
7732
7733 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7734      
7735     /**
7736      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7737      * rendering into an Roo.Editor, defaults to false)
7738      */
7739     /**
7740      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7741      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7742      */
7743     /**
7744      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7745      */
7746     /**
7747      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7748      * the dropdown list (defaults to undefined, with no header element)
7749      */
7750
7751      /**
7752      * @cfg {String/Roo.Template} tpl The template to use to render the output
7753      */
7754      
7755      /**
7756      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7757      */
7758     listWidth: undefined,
7759     /**
7760      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7761      * mode = 'remote' or 'text' if mode = 'local')
7762      */
7763     displayField: undefined,
7764     /**
7765      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7766      * mode = 'remote' or 'value' if mode = 'local'). 
7767      * Note: use of a valueField requires the user make a selection
7768      * in order for a value to be mapped.
7769      */
7770     valueField: undefined,
7771     
7772     
7773     /**
7774      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7775      * field's data value (defaults to the underlying DOM element's name)
7776      */
7777     hiddenName: undefined,
7778     /**
7779      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7780      */
7781     listClass: '',
7782     /**
7783      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7784      */
7785     selectedClass: 'active',
7786     
7787     /**
7788      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7789      */
7790     shadow:'sides',
7791     /**
7792      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7793      * anchor positions (defaults to 'tl-bl')
7794      */
7795     listAlign: 'tl-bl?',
7796     /**
7797      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7798      */
7799     maxHeight: 300,
7800     /**
7801      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7802      * query specified by the allQuery config option (defaults to 'query')
7803      */
7804     triggerAction: 'query',
7805     /**
7806      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7807      * (defaults to 4, does not apply if editable = false)
7808      */
7809     minChars : 4,
7810     /**
7811      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7812      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7813      */
7814     typeAhead: false,
7815     /**
7816      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7817      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7818      */
7819     queryDelay: 500,
7820     /**
7821      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7822      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7823      */
7824     pageSize: 0,
7825     /**
7826      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7827      * when editable = true (defaults to false)
7828      */
7829     selectOnFocus:false,
7830     /**
7831      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7832      */
7833     queryParam: 'query',
7834     /**
7835      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7836      * when mode = 'remote' (defaults to 'Loading...')
7837      */
7838     loadingText: 'Loading...',
7839     /**
7840      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7841      */
7842     resizable: false,
7843     /**
7844      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7845      */
7846     handleHeight : 8,
7847     /**
7848      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7849      * traditional select (defaults to true)
7850      */
7851     editable: true,
7852     /**
7853      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7854      */
7855     allQuery: '',
7856     /**
7857      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7858      */
7859     mode: 'remote',
7860     /**
7861      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7862      * listWidth has a higher value)
7863      */
7864     minListWidth : 70,
7865     /**
7866      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7867      * allow the user to set arbitrary text into the field (defaults to false)
7868      */
7869     forceSelection:false,
7870     /**
7871      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7872      * if typeAhead = true (defaults to 250)
7873      */
7874     typeAheadDelay : 250,
7875     /**
7876      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7877      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7878      */
7879     valueNotFoundText : undefined,
7880     /**
7881      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7882      */
7883     blockFocus : false,
7884     
7885     /**
7886      * @cfg {Boolean} disableClear Disable showing of clear button.
7887      */
7888     disableClear : false,
7889     /**
7890      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7891      */
7892     alwaysQuery : false,
7893     
7894     /**
7895      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7896      */
7897     multiple : false,
7898     
7899     //private
7900     addicon : false,
7901     editicon: false,
7902     
7903     page: 0,
7904     hasQuery: false,
7905     append: false,
7906     loadNext: false,
7907     item: [],
7908     
7909     // element that contains real text value.. (when hidden is used..)
7910      
7911     // private
7912     initEvents: function(){
7913         
7914         if (!this.store) {
7915             throw "can not find store for combo";
7916         }
7917         this.store = Roo.factory(this.store, Roo.data);
7918         
7919         
7920         
7921         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
7922         
7923         
7924         if(this.hiddenName){
7925             
7926             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
7927             
7928             this.hiddenField.dom.value =
7929                 this.hiddenValue !== undefined ? this.hiddenValue :
7930                 this.value !== undefined ? this.value : '';
7931
7932             // prevent input submission
7933             this.el.dom.removeAttribute('name');
7934             this.hiddenField.dom.setAttribute('name', this.hiddenName);
7935              
7936              
7937         }
7938         //if(Roo.isGecko){
7939         //    this.el.dom.setAttribute('autocomplete', 'off');
7940         //}
7941
7942         var cls = 'x-combo-list';
7943         this.list = this.el.select('ul.dropdown-menu',true).first();
7944
7945         //this.list = new Roo.Layer({
7946         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
7947         //});
7948         
7949         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
7950         this.list.setWidth(lw);
7951         
7952         this.list.on('mouseover', this.onViewOver, this);
7953         this.list.on('mousemove', this.onViewMove, this);
7954         
7955         this.list.on('scroll', this.onViewScroll, this);
7956         
7957         /*
7958         this.list.swallowEvent('mousewheel');
7959         this.assetHeight = 0;
7960
7961         if(this.title){
7962             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
7963             this.assetHeight += this.header.getHeight();
7964         }
7965
7966         this.innerList = this.list.createChild({cls:cls+'-inner'});
7967         this.innerList.on('mouseover', this.onViewOver, this);
7968         this.innerList.on('mousemove', this.onViewMove, this);
7969         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
7970         
7971         if(this.allowBlank && !this.pageSize && !this.disableClear){
7972             this.footer = this.list.createChild({cls:cls+'-ft'});
7973             this.pageTb = new Roo.Toolbar(this.footer);
7974            
7975         }
7976         if(this.pageSize){
7977             this.footer = this.list.createChild({cls:cls+'-ft'});
7978             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
7979                     {pageSize: this.pageSize});
7980             
7981         }
7982         
7983         if (this.pageTb && this.allowBlank && !this.disableClear) {
7984             var _this = this;
7985             this.pageTb.add(new Roo.Toolbar.Fill(), {
7986                 cls: 'x-btn-icon x-btn-clear',
7987                 text: '&#160;',
7988                 handler: function()
7989                 {
7990                     _this.collapse();
7991                     _this.clearValue();
7992                     _this.onSelect(false, -1);
7993                 }
7994             });
7995         }
7996         if (this.footer) {
7997             this.assetHeight += this.footer.getHeight();
7998         }
7999         */
8000             
8001         if(!this.tpl){
8002             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8003         }
8004
8005         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8006             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8007         });
8008         //this.view.wrapEl.setDisplayed(false);
8009         this.view.on('click', this.onViewClick, this);
8010         
8011         
8012         
8013         this.store.on('beforeload', this.onBeforeLoad, this);
8014         this.store.on('load', this.onLoad, this);
8015         this.store.on('loadexception', this.onLoadException, this);
8016         /*
8017         if(this.resizable){
8018             this.resizer = new Roo.Resizable(this.list,  {
8019                pinned:true, handles:'se'
8020             });
8021             this.resizer.on('resize', function(r, w, h){
8022                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8023                 this.listWidth = w;
8024                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8025                 this.restrictHeight();
8026             }, this);
8027             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8028         }
8029         */
8030         if(!this.editable){
8031             this.editable = true;
8032             this.setEditable(false);
8033         }
8034         
8035         /*
8036         
8037         if (typeof(this.events.add.listeners) != 'undefined') {
8038             
8039             this.addicon = this.wrap.createChild(
8040                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8041        
8042             this.addicon.on('click', function(e) {
8043                 this.fireEvent('add', this);
8044             }, this);
8045         }
8046         if (typeof(this.events.edit.listeners) != 'undefined') {
8047             
8048             this.editicon = this.wrap.createChild(
8049                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8050             if (this.addicon) {
8051                 this.editicon.setStyle('margin-left', '40px');
8052             }
8053             this.editicon.on('click', function(e) {
8054                 
8055                 // we fire even  if inothing is selected..
8056                 this.fireEvent('edit', this, this.lastData );
8057                 
8058             }, this);
8059         }
8060         */
8061         
8062         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8063             "up" : function(e){
8064                 this.inKeyMode = true;
8065                 this.selectPrev();
8066             },
8067
8068             "down" : function(e){
8069                 if(!this.isExpanded()){
8070                     this.onTriggerClick();
8071                 }else{
8072                     this.inKeyMode = true;
8073                     this.selectNext();
8074                 }
8075             },
8076
8077             "enter" : function(e){
8078                 this.onViewClick();
8079                 //return true;
8080             },
8081
8082             "esc" : function(e){
8083                 this.collapse();
8084             },
8085
8086             "tab" : function(e){
8087                 this.collapse();
8088                 
8089                 if(this.fireEvent("specialkey", this, e)){
8090                     this.onViewClick(false);
8091                 }
8092                 
8093                 return true;
8094             },
8095
8096             scope : this,
8097
8098             doRelay : function(foo, bar, hname){
8099                 if(hname == 'down' || this.scope.isExpanded()){
8100                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8101                 }
8102                 return true;
8103             },
8104
8105             forceKeyDown: true
8106         });
8107         
8108         
8109         this.queryDelay = Math.max(this.queryDelay || 10,
8110                 this.mode == 'local' ? 10 : 250);
8111         
8112         
8113         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8114         
8115         if(this.typeAhead){
8116             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8117         }
8118         if(this.editable !== false){
8119             this.inputEl().on("keyup", this.onKeyUp, this);
8120         }
8121         if(this.forceSelection){
8122             this.on('blur', this.doForce, this);
8123         }
8124         
8125         if(this.multiple){
8126             this.choices = this.el.select('ul.select2-choices', true).first();
8127             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8128         }
8129     },
8130
8131     onDestroy : function(){
8132         if(this.view){
8133             this.view.setStore(null);
8134             this.view.el.removeAllListeners();
8135             this.view.el.remove();
8136             this.view.purgeListeners();
8137         }
8138         if(this.list){
8139             this.list.dom.innerHTML  = '';
8140         }
8141         if(this.store){
8142             this.store.un('beforeload', this.onBeforeLoad, this);
8143             this.store.un('load', this.onLoad, this);
8144             this.store.un('loadexception', this.onLoadException, this);
8145         }
8146         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8147     },
8148
8149     // private
8150     fireKey : function(e){
8151         if(e.isNavKeyPress() && !this.list.isVisible()){
8152             this.fireEvent("specialkey", this, e);
8153         }
8154     },
8155
8156     // private
8157     onResize: function(w, h){
8158 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8159 //        
8160 //        if(typeof w != 'number'){
8161 //            // we do not handle it!?!?
8162 //            return;
8163 //        }
8164 //        var tw = this.trigger.getWidth();
8165 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8166 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8167 //        var x = w - tw;
8168 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8169 //            
8170 //        //this.trigger.setStyle('left', x+'px');
8171 //        
8172 //        if(this.list && this.listWidth === undefined){
8173 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8174 //            this.list.setWidth(lw);
8175 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8176 //        }
8177         
8178     
8179         
8180     },
8181
8182     /**
8183      * Allow or prevent the user from directly editing the field text.  If false is passed,
8184      * the user will only be able to select from the items defined in the dropdown list.  This method
8185      * is the runtime equivalent of setting the 'editable' config option at config time.
8186      * @param {Boolean} value True to allow the user to directly edit the field text
8187      */
8188     setEditable : function(value){
8189         if(value == this.editable){
8190             return;
8191         }
8192         this.editable = value;
8193         if(!value){
8194             this.inputEl().dom.setAttribute('readOnly', true);
8195             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8196             this.inputEl().addClass('x-combo-noedit');
8197         }else{
8198             this.inputEl().dom.setAttribute('readOnly', false);
8199             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8200             this.inputEl().removeClass('x-combo-noedit');
8201         }
8202     },
8203
8204     // private
8205     
8206     onBeforeLoad : function(combo,opts){
8207         if(!this.hasFocus){
8208             return;
8209         }
8210          if (!opts.add) {
8211             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8212          }
8213         this.restrictHeight();
8214         this.selectedIndex = -1;
8215     },
8216
8217     // private
8218     onLoad : function(){
8219         
8220         this.hasQuery = false;
8221         
8222         if(!this.hasFocus){
8223             return;
8224         }
8225         
8226         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8227             this.loading.hide();
8228         }
8229         
8230         if(this.store.getCount() > 0){
8231             this.expand();
8232             this.restrictHeight();
8233             if(this.lastQuery == this.allQuery){
8234                 if(this.editable){
8235                     this.inputEl().dom.select();
8236                 }
8237                 if(!this.selectByValue(this.value, true)){
8238                     this.select(0, true);
8239                 }
8240             }else{
8241                 this.selectNext();
8242                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8243                     this.taTask.delay(this.typeAheadDelay);
8244                 }
8245             }
8246         }else{
8247             this.onEmptyResults();
8248         }
8249         
8250         //this.el.focus();
8251     },
8252     // private
8253     onLoadException : function()
8254     {
8255         this.hasQuery = false;
8256         
8257         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8258             this.loading.hide();
8259         }
8260         
8261         this.collapse();
8262         Roo.log(this.store.reader.jsonData);
8263         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8264             // fixme
8265             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8266         }
8267         
8268         
8269     },
8270     // private
8271     onTypeAhead : function(){
8272         if(this.store.getCount() > 0){
8273             var r = this.store.getAt(0);
8274             var newValue = r.data[this.displayField];
8275             var len = newValue.length;
8276             var selStart = this.getRawValue().length;
8277             
8278             if(selStart != len){
8279                 this.setRawValue(newValue);
8280                 this.selectText(selStart, newValue.length);
8281             }
8282         }
8283     },
8284
8285     // private
8286     onSelect : function(record, index){
8287         
8288         if(this.fireEvent('beforeselect', this, record, index) !== false){
8289         
8290             this.setFromData(index > -1 ? record.data : false);
8291             
8292             this.collapse();
8293             this.fireEvent('select', this, record, index);
8294         }
8295     },
8296
8297     /**
8298      * Returns the currently selected field value or empty string if no value is set.
8299      * @return {String} value The selected value
8300      */
8301     getValue : function(){
8302         
8303         if(this.multiple){
8304             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8305         }
8306         
8307         if(this.valueField){
8308             return typeof this.value != 'undefined' ? this.value : '';
8309         }else{
8310             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8311         }
8312     },
8313
8314     /**
8315      * Clears any text/value currently set in the field
8316      */
8317     clearValue : function(){
8318         if(this.hiddenField){
8319             this.hiddenField.dom.value = '';
8320         }
8321         this.value = '';
8322         this.setRawValue('');
8323         this.lastSelectionText = '';
8324         
8325     },
8326
8327     /**
8328      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8329      * will be displayed in the field.  If the value does not match the data value of an existing item,
8330      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8331      * Otherwise the field will be blank (although the value will still be set).
8332      * @param {String} value The value to match
8333      */
8334     setValue : function(v){
8335         if(this.multiple){
8336             this.syncValue();
8337             return;
8338         }
8339         
8340         var text = v;
8341         if(this.valueField){
8342             var r = this.findRecord(this.valueField, v);
8343             if(r){
8344                 text = r.data[this.displayField];
8345             }else if(this.valueNotFoundText !== undefined){
8346                 text = this.valueNotFoundText;
8347             }
8348         }
8349         this.lastSelectionText = text;
8350         if(this.hiddenField){
8351             this.hiddenField.dom.value = v;
8352         }
8353         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8354         this.value = v;
8355     },
8356     /**
8357      * @property {Object} the last set data for the element
8358      */
8359     
8360     lastData : false,
8361     /**
8362      * Sets the value of the field based on a object which is related to the record format for the store.
8363      * @param {Object} value the value to set as. or false on reset?
8364      */
8365     setFromData : function(o){
8366         
8367         if(this.multiple){
8368             this.addItem(o);
8369             return;
8370         }
8371             
8372         var dv = ''; // display value
8373         var vv = ''; // value value..
8374         this.lastData = o;
8375         if (this.displayField) {
8376             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8377         } else {
8378             // this is an error condition!!!
8379             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8380         }
8381         
8382         if(this.valueField){
8383             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8384         }
8385         
8386         if(this.hiddenField){
8387             this.hiddenField.dom.value = vv;
8388             
8389             this.lastSelectionText = dv;
8390             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8391             this.value = vv;
8392             return;
8393         }
8394         // no hidden field.. - we store the value in 'value', but still display
8395         // display field!!!!
8396         this.lastSelectionText = dv;
8397         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8398         this.value = vv;
8399         
8400         
8401     },
8402     // private
8403     reset : function(){
8404         // overridden so that last data is reset..
8405         this.setValue(this.originalValue);
8406         this.clearInvalid();
8407         this.lastData = false;
8408         if (this.view) {
8409             this.view.clearSelections();
8410         }
8411     },
8412     // private
8413     findRecord : function(prop, value){
8414         var record;
8415         if(this.store.getCount() > 0){
8416             this.store.each(function(r){
8417                 if(r.data[prop] == value){
8418                     record = r;
8419                     return false;
8420                 }
8421                 return true;
8422             });
8423         }
8424         return record;
8425     },
8426     
8427     getName: function()
8428     {
8429         // returns hidden if it's set..
8430         if (!this.rendered) {return ''};
8431         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8432         
8433     },
8434     // private
8435     onViewMove : function(e, t){
8436         this.inKeyMode = false;
8437     },
8438
8439     // private
8440     onViewOver : function(e, t){
8441         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8442             return;
8443         }
8444         var item = this.view.findItemFromChild(t);
8445         if(item){
8446             var index = this.view.indexOf(item);
8447             this.select(index, false);
8448         }
8449     },
8450
8451     // private
8452     onViewClick : function(doFocus)
8453     {
8454         var index = this.view.getSelectedIndexes()[0];
8455         var r = this.store.getAt(index);
8456         if(r){
8457             this.onSelect(r, index);
8458         }
8459         if(doFocus !== false && !this.blockFocus){
8460             this.inputEl().focus();
8461         }
8462     },
8463
8464     // private
8465     restrictHeight : function(){
8466         //this.innerList.dom.style.height = '';
8467         //var inner = this.innerList.dom;
8468         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8469         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8470         //this.list.beginUpdate();
8471         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8472         this.list.alignTo(this.inputEl(), this.listAlign);
8473         //this.list.endUpdate();
8474     },
8475
8476     // private
8477     onEmptyResults : function(){
8478         this.collapse();
8479     },
8480
8481     /**
8482      * Returns true if the dropdown list is expanded, else false.
8483      */
8484     isExpanded : function(){
8485         return this.list.isVisible();
8486     },
8487
8488     /**
8489      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8490      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8491      * @param {String} value The data value of the item to select
8492      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8493      * selected item if it is not currently in view (defaults to true)
8494      * @return {Boolean} True if the value matched an item in the list, else false
8495      */
8496     selectByValue : function(v, scrollIntoView){
8497         if(v !== undefined && v !== null){
8498             var r = this.findRecord(this.valueField || this.displayField, v);
8499             if(r){
8500                 this.select(this.store.indexOf(r), scrollIntoView);
8501                 return true;
8502             }
8503         }
8504         return false;
8505     },
8506
8507     /**
8508      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8509      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8510      * @param {Number} index The zero-based index of the list item to select
8511      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8512      * selected item if it is not currently in view (defaults to true)
8513      */
8514     select : function(index, scrollIntoView){
8515         this.selectedIndex = index;
8516         this.view.select(index);
8517         if(scrollIntoView !== false){
8518             var el = this.view.getNode(index);
8519             if(el){
8520                 //this.innerList.scrollChildIntoView(el, false);
8521                 
8522             }
8523         }
8524     },
8525
8526     // private
8527     selectNext : function(){
8528         var ct = this.store.getCount();
8529         if(ct > 0){
8530             if(this.selectedIndex == -1){
8531                 this.select(0);
8532             }else if(this.selectedIndex < ct-1){
8533                 this.select(this.selectedIndex+1);
8534             }
8535         }
8536     },
8537
8538     // private
8539     selectPrev : function(){
8540         var ct = this.store.getCount();
8541         if(ct > 0){
8542             if(this.selectedIndex == -1){
8543                 this.select(0);
8544             }else if(this.selectedIndex != 0){
8545                 this.select(this.selectedIndex-1);
8546             }
8547         }
8548     },
8549
8550     // private
8551     onKeyUp : function(e){
8552         if(this.editable !== false && !e.isSpecialKey()){
8553             this.lastKey = e.getKey();
8554             this.dqTask.delay(this.queryDelay);
8555         }
8556     },
8557
8558     // private
8559     validateBlur : function(){
8560         return !this.list || !this.list.isVisible();   
8561     },
8562
8563     // private
8564     initQuery : function(){
8565         this.doQuery(this.getRawValue());
8566     },
8567
8568     // private
8569     doForce : function(){
8570         if(this.el.dom.value.length > 0){
8571             this.el.dom.value =
8572                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8573              
8574         }
8575     },
8576
8577     /**
8578      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8579      * query allowing the query action to be canceled if needed.
8580      * @param {String} query The SQL query to execute
8581      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8582      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8583      * saved in the current store (defaults to false)
8584      */
8585     doQuery : function(q, forceAll){
8586         
8587         if(q === undefined || q === null){
8588             q = '';
8589         }
8590         var qe = {
8591             query: q,
8592             forceAll: forceAll,
8593             combo: this,
8594             cancel:false
8595         };
8596         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8597             return false;
8598         }
8599         q = qe.query;
8600         
8601         forceAll = qe.forceAll;
8602         if(forceAll === true || (q.length >= this.minChars)){
8603             
8604             this.hasQuery = true;
8605             
8606             if(this.lastQuery != q || this.alwaysQuery){
8607                 this.lastQuery = q;
8608                 if(this.mode == 'local'){
8609                     this.selectedIndex = -1;
8610                     if(forceAll){
8611                         this.store.clearFilter();
8612                     }else{
8613                         this.store.filter(this.displayField, q);
8614                     }
8615                     this.onLoad();
8616                 }else{
8617                     this.store.baseParams[this.queryParam] = q;
8618                     
8619                     var options = {params : this.getParams(q)};
8620                     
8621                     if(this.loadNext){
8622                         options.add = true;
8623                         options.params.start = this.page * this.pageSize;
8624                     }
8625                     
8626                     this.store.load(options);
8627                     this.expand();
8628                 }
8629             }else{
8630                 this.selectedIndex = -1;
8631                 this.onLoad();   
8632             }
8633         }
8634         
8635         this.loadNext = false;
8636     },
8637
8638     // private
8639     getParams : function(q){
8640         var p = {};
8641         //p[this.queryParam] = q;
8642         
8643         if(this.pageSize){
8644             p.start = 0;
8645             p.limit = this.pageSize;
8646         }
8647         return p;
8648     },
8649
8650     /**
8651      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8652      */
8653     collapse : function(){
8654         if(!this.isExpanded()){
8655             return;
8656         }
8657         
8658         this.list.hide();
8659         Roo.get(document).un('mousedown', this.collapseIf, this);
8660         Roo.get(document).un('mousewheel', this.collapseIf, this);
8661         if (!this.editable) {
8662             Roo.get(document).un('keydown', this.listKeyPress, this);
8663         }
8664         this.fireEvent('collapse', this);
8665     },
8666
8667     // private
8668     collapseIf : function(e){
8669         var in_combo  = e.within(this.el);
8670         var in_list =  e.within(this.list);
8671         
8672         if (in_combo || in_list) {
8673             //e.stopPropagation();
8674             return;
8675         }
8676
8677         this.collapse();
8678         
8679     },
8680
8681     /**
8682      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8683      */
8684     expand : function(){
8685        
8686         if(this.isExpanded() || !this.hasFocus){
8687             return;
8688         }
8689          Roo.log('expand');
8690         this.list.alignTo(this.inputEl(), this.listAlign);
8691         this.list.show();
8692         Roo.get(document).on('mousedown', this.collapseIf, this);
8693         Roo.get(document).on('mousewheel', this.collapseIf, this);
8694         if (!this.editable) {
8695             Roo.get(document).on('keydown', this.listKeyPress, this);
8696         }
8697         
8698         this.fireEvent('expand', this);
8699     },
8700
8701     // private
8702     // Implements the default empty TriggerField.onTriggerClick function
8703     onTriggerClick : function()
8704     {
8705         Roo.log('trigger click');
8706         
8707         if(this.disabled){
8708             return;
8709         }
8710         
8711         this.page = 0;
8712         this.loadNext = false;
8713         
8714         if(this.isExpanded()){
8715             this.collapse();
8716             if (!this.blockFocus) {
8717                 this.inputEl().focus();
8718             }
8719             
8720         }else {
8721             this.hasFocus = true;
8722             if(this.triggerAction == 'all') {
8723                 this.doQuery(this.allQuery, true);
8724             } else {
8725                 this.doQuery(this.getRawValue());
8726             }
8727             if (!this.blockFocus) {
8728                 this.inputEl().focus();
8729             }
8730         }
8731     },
8732     listKeyPress : function(e)
8733     {
8734         //Roo.log('listkeypress');
8735         // scroll to first matching element based on key pres..
8736         if (e.isSpecialKey()) {
8737             return false;
8738         }
8739         var k = String.fromCharCode(e.getKey()).toUpperCase();
8740         //Roo.log(k);
8741         var match  = false;
8742         var csel = this.view.getSelectedNodes();
8743         var cselitem = false;
8744         if (csel.length) {
8745             var ix = this.view.indexOf(csel[0]);
8746             cselitem  = this.store.getAt(ix);
8747             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8748                 cselitem = false;
8749             }
8750             
8751         }
8752         
8753         this.store.each(function(v) { 
8754             if (cselitem) {
8755                 // start at existing selection.
8756                 if (cselitem.id == v.id) {
8757                     cselitem = false;
8758                 }
8759                 return true;
8760             }
8761                 
8762             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8763                 match = this.store.indexOf(v);
8764                 return false;
8765             }
8766             return true;
8767         }, this);
8768         
8769         if (match === false) {
8770             return true; // no more action?
8771         }
8772         // scroll to?
8773         this.view.select(match);
8774         var sn = Roo.get(this.view.getSelectedNodes()[0])
8775         //sn.scrollIntoView(sn.dom.parentNode, false);
8776     },
8777     
8778     onViewScroll : function(e, t){
8779         
8780         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8781             return;
8782         }
8783         
8784         this.hasQuery = true;
8785         
8786         this.loading = this.list.select('.loading', true).first();
8787         
8788         if(this.loading === null){
8789             this.list.createChild({
8790                 tag: 'div',
8791                 cls: 'loading select2-more-results select2-active',
8792                 html: 'Loading more results...'
8793             })
8794             
8795             this.loading = this.list.select('.loading', true).first();
8796             
8797             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8798             
8799             this.loading.hide();
8800         }
8801         
8802         this.loading.show();
8803         
8804         var _combo = this;
8805         
8806         this.page++;
8807         this.loadNext = true;
8808         
8809         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8810         
8811         return;
8812     },
8813     
8814     addItem : function(o)
8815     {   
8816         var dv = ''; // display value
8817         
8818         if (this.displayField) {
8819             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8820         } else {
8821             // this is an error condition!!!
8822             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8823         }
8824         
8825         if(!dv.length){
8826             return;
8827         }
8828         
8829         var choice = this.choices.createChild({
8830             tag: 'li',
8831             cls: 'select2-search-choice',
8832             cn: [
8833                 {
8834                     tag: 'div',
8835                     html: dv
8836                 },
8837                 {
8838                     tag: 'a',
8839                     href: '#',
8840                     cls: 'select2-search-choice-close',
8841                     tabindex: '-1'
8842                 }
8843             ]
8844             
8845         }, this.searchField);
8846         
8847         var close = choice.select('a.select2-search-choice-close', true).first()
8848         
8849         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8850         
8851         this.item.push(o);
8852         this.lastData = o;
8853         
8854         this.syncValue();
8855         
8856         this.inputEl().dom.value = '';
8857         
8858     },
8859     
8860     onRemoveItem : function(e, _self, o)
8861     {
8862         Roo.log('remove item');
8863         var index = this.item.indexOf(o.data) * 1;
8864         
8865         if( index < 0){
8866             Roo.log('not this item?!');
8867             return;
8868         }
8869         
8870         this.item.splice(index, 1);
8871         o.item.remove();
8872         
8873         this.syncValue();
8874         
8875         this.fireEvent('remove', this);
8876         
8877     },
8878     
8879     syncValue : function()
8880     {
8881         if(!this.item.length){
8882             this.clearValue();
8883             return;
8884         }
8885             
8886         var value = [];
8887         var _this = this;
8888         Roo.each(this.item, function(i){
8889             if(_this.valueField){
8890                 value.push(i[_this.valueField]);
8891                 return;
8892             }
8893
8894             value.push(i);
8895         });
8896
8897         this.value = value.join(',');
8898
8899         if(this.hiddenField){
8900             this.hiddenField.dom.value = this.value;
8901         }
8902     }
8903
8904     /** 
8905     * @cfg {Boolean} grow 
8906     * @hide 
8907     */
8908     /** 
8909     * @cfg {Number} growMin 
8910     * @hide 
8911     */
8912     /** 
8913     * @cfg {Number} growMax 
8914     * @hide 
8915     */
8916     /**
8917      * @hide
8918      * @method autoSize
8919      */
8920 });
8921 /*
8922  * Based on:
8923  * Ext JS Library 1.1.1
8924  * Copyright(c) 2006-2007, Ext JS, LLC.
8925  *
8926  * Originally Released Under LGPL - original licence link has changed is not relivant.
8927  *
8928  * Fork - LGPL
8929  * <script type="text/javascript">
8930  */
8931
8932 /**
8933  * @class Roo.View
8934  * @extends Roo.util.Observable
8935  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8936  * This class also supports single and multi selection modes. <br>
8937  * Create a data model bound view:
8938  <pre><code>
8939  var store = new Roo.data.Store(...);
8940
8941  var view = new Roo.View({
8942     el : "my-element",
8943     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8944  
8945     singleSelect: true,
8946     selectedClass: "ydataview-selected",
8947     store: store
8948  });
8949
8950  // listen for node click?
8951  view.on("click", function(vw, index, node, e){
8952  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8953  });
8954
8955  // load XML data
8956  dataModel.load("foobar.xml");
8957  </code></pre>
8958  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8959  * <br><br>
8960  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8961  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8962  * 
8963  * Note: old style constructor is still suported (container, template, config)
8964  * 
8965  * @constructor
8966  * Create a new View
8967  * @param {Object} config The config object
8968  * 
8969  */
8970 Roo.View = function(config, depreciated_tpl, depreciated_config){
8971     
8972     if (typeof(depreciated_tpl) == 'undefined') {
8973         // new way.. - universal constructor.
8974         Roo.apply(this, config);
8975         this.el  = Roo.get(this.el);
8976     } else {
8977         // old format..
8978         this.el  = Roo.get(config);
8979         this.tpl = depreciated_tpl;
8980         Roo.apply(this, depreciated_config);
8981     }
8982     this.wrapEl  = this.el.wrap().wrap();
8983     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8984     
8985     
8986     if(typeof(this.tpl) == "string"){
8987         this.tpl = new Roo.Template(this.tpl);
8988     } else {
8989         // support xtype ctors..
8990         this.tpl = new Roo.factory(this.tpl, Roo);
8991     }
8992     
8993     
8994     this.tpl.compile();
8995    
8996   
8997     
8998      
8999     /** @private */
9000     this.addEvents({
9001         /**
9002          * @event beforeclick
9003          * Fires before a click is processed. Returns false to cancel the default action.
9004          * @param {Roo.View} this
9005          * @param {Number} index The index of the target node
9006          * @param {HTMLElement} node The target node
9007          * @param {Roo.EventObject} e The raw event object
9008          */
9009             "beforeclick" : true,
9010         /**
9011          * @event click
9012          * Fires when a template node is clicked.
9013          * @param {Roo.View} this
9014          * @param {Number} index The index of the target node
9015          * @param {HTMLElement} node The target node
9016          * @param {Roo.EventObject} e The raw event object
9017          */
9018             "click" : true,
9019         /**
9020          * @event dblclick
9021          * Fires when a template node is double clicked.
9022          * @param {Roo.View} this
9023          * @param {Number} index The index of the target node
9024          * @param {HTMLElement} node The target node
9025          * @param {Roo.EventObject} e The raw event object
9026          */
9027             "dblclick" : true,
9028         /**
9029          * @event contextmenu
9030          * Fires when a template node is right clicked.
9031          * @param {Roo.View} this
9032          * @param {Number} index The index of the target node
9033          * @param {HTMLElement} node The target node
9034          * @param {Roo.EventObject} e The raw event object
9035          */
9036             "contextmenu" : true,
9037         /**
9038          * @event selectionchange
9039          * Fires when the selected nodes change.
9040          * @param {Roo.View} this
9041          * @param {Array} selections Array of the selected nodes
9042          */
9043             "selectionchange" : true,
9044     
9045         /**
9046          * @event beforeselect
9047          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9048          * @param {Roo.View} this
9049          * @param {HTMLElement} node The node to be selected
9050          * @param {Array} selections Array of currently selected nodes
9051          */
9052             "beforeselect" : true,
9053         /**
9054          * @event preparedata
9055          * Fires on every row to render, to allow you to change the data.
9056          * @param {Roo.View} this
9057          * @param {Object} data to be rendered (change this)
9058          */
9059           "preparedata" : true
9060           
9061           
9062         });
9063
9064
9065
9066     this.el.on({
9067         "click": this.onClick,
9068         "dblclick": this.onDblClick,
9069         "contextmenu": this.onContextMenu,
9070         scope:this
9071     });
9072
9073     this.selections = [];
9074     this.nodes = [];
9075     this.cmp = new Roo.CompositeElementLite([]);
9076     if(this.store){
9077         this.store = Roo.factory(this.store, Roo.data);
9078         this.setStore(this.store, true);
9079     }
9080     
9081     if ( this.footer && this.footer.xtype) {
9082            
9083          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9084         
9085         this.footer.dataSource = this.store
9086         this.footer.container = fctr;
9087         this.footer = Roo.factory(this.footer, Roo);
9088         fctr.insertFirst(this.el);
9089         
9090         // this is a bit insane - as the paging toolbar seems to detach the el..
9091 //        dom.parentNode.parentNode.parentNode
9092          // they get detached?
9093     }
9094     
9095     
9096     Roo.View.superclass.constructor.call(this);
9097     
9098     
9099 };
9100
9101 Roo.extend(Roo.View, Roo.util.Observable, {
9102     
9103      /**
9104      * @cfg {Roo.data.Store} store Data store to load data from.
9105      */
9106     store : false,
9107     
9108     /**
9109      * @cfg {String|Roo.Element} el The container element.
9110      */
9111     el : '',
9112     
9113     /**
9114      * @cfg {String|Roo.Template} tpl The template used by this View 
9115      */
9116     tpl : false,
9117     /**
9118      * @cfg {String} dataName the named area of the template to use as the data area
9119      *                          Works with domtemplates roo-name="name"
9120      */
9121     dataName: false,
9122     /**
9123      * @cfg {String} selectedClass The css class to add to selected nodes
9124      */
9125     selectedClass : "x-view-selected",
9126      /**
9127      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9128      */
9129     emptyText : "",
9130     
9131     /**
9132      * @cfg {String} text to display on mask (default Loading)
9133      */
9134     mask : false,
9135     /**
9136      * @cfg {Boolean} multiSelect Allow multiple selection
9137      */
9138     multiSelect : false,
9139     /**
9140      * @cfg {Boolean} singleSelect Allow single selection
9141      */
9142     singleSelect:  false,
9143     
9144     /**
9145      * @cfg {Boolean} toggleSelect - selecting 
9146      */
9147     toggleSelect : false,
9148     
9149     /**
9150      * Returns the element this view is bound to.
9151      * @return {Roo.Element}
9152      */
9153     getEl : function(){
9154         return this.wrapEl;
9155     },
9156     
9157     
9158
9159     /**
9160      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9161      */
9162     refresh : function(){
9163         Roo.log('refresh');
9164         var t = this.tpl;
9165         
9166         // if we are using something like 'domtemplate', then
9167         // the what gets used is:
9168         // t.applySubtemplate(NAME, data, wrapping data..)
9169         // the outer template then get' applied with
9170         //     the store 'extra data'
9171         // and the body get's added to the
9172         //      roo-name="data" node?
9173         //      <span class='roo-tpl-{name}'></span> ?????
9174         
9175         
9176         
9177         this.clearSelections();
9178         this.el.update("");
9179         var html = [];
9180         var records = this.store.getRange();
9181         if(records.length < 1) {
9182             
9183             // is this valid??  = should it render a template??
9184             
9185             this.el.update(this.emptyText);
9186             return;
9187         }
9188         var el = this.el;
9189         if (this.dataName) {
9190             this.el.update(t.apply(this.store.meta)); //????
9191             el = this.el.child('.roo-tpl-' + this.dataName);
9192         }
9193         
9194         for(var i = 0, len = records.length; i < len; i++){
9195             var data = this.prepareData(records[i].data, i, records[i]);
9196             this.fireEvent("preparedata", this, data, i, records[i]);
9197             html[html.length] = Roo.util.Format.trim(
9198                 this.dataName ?
9199                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9200                     t.apply(data)
9201             );
9202         }
9203         
9204         
9205         
9206         el.update(html.join(""));
9207         this.nodes = el.dom.childNodes;
9208         this.updateIndexes(0);
9209     },
9210     
9211
9212     /**
9213      * Function to override to reformat the data that is sent to
9214      * the template for each node.
9215      * DEPRICATED - use the preparedata event handler.
9216      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9217      * a JSON object for an UpdateManager bound view).
9218      */
9219     prepareData : function(data, index, record)
9220     {
9221         this.fireEvent("preparedata", this, data, index, record);
9222         return data;
9223     },
9224
9225     onUpdate : function(ds, record){
9226          Roo.log('on update');   
9227         this.clearSelections();
9228         var index = this.store.indexOf(record);
9229         var n = this.nodes[index];
9230         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9231         n.parentNode.removeChild(n);
9232         this.updateIndexes(index, index);
9233     },
9234
9235     
9236     
9237 // --------- FIXME     
9238     onAdd : function(ds, records, index)
9239     {
9240         Roo.log(['on Add', ds, records, index] );        
9241         this.clearSelections();
9242         if(this.nodes.length == 0){
9243             this.refresh();
9244             return;
9245         }
9246         var n = this.nodes[index];
9247         for(var i = 0, len = records.length; i < len; i++){
9248             var d = this.prepareData(records[i].data, i, records[i]);
9249             if(n){
9250                 this.tpl.insertBefore(n, d);
9251             }else{
9252                 
9253                 this.tpl.append(this.el, d);
9254             }
9255         }
9256         this.updateIndexes(index);
9257     },
9258
9259     onRemove : function(ds, record, index){
9260         Roo.log('onRemove');
9261         this.clearSelections();
9262         var el = this.dataName  ?
9263             this.el.child('.roo-tpl-' + this.dataName) :
9264             this.el; 
9265         
9266         el.dom.removeChild(this.nodes[index]);
9267         this.updateIndexes(index);
9268     },
9269
9270     /**
9271      * Refresh an individual node.
9272      * @param {Number} index
9273      */
9274     refreshNode : function(index){
9275         this.onUpdate(this.store, this.store.getAt(index));
9276     },
9277
9278     updateIndexes : function(startIndex, endIndex){
9279         var ns = this.nodes;
9280         startIndex = startIndex || 0;
9281         endIndex = endIndex || ns.length - 1;
9282         for(var i = startIndex; i <= endIndex; i++){
9283             ns[i].nodeIndex = i;
9284         }
9285     },
9286
9287     /**
9288      * Changes the data store this view uses and refresh the view.
9289      * @param {Store} store
9290      */
9291     setStore : function(store, initial){
9292         if(!initial && this.store){
9293             this.store.un("datachanged", this.refresh);
9294             this.store.un("add", this.onAdd);
9295             this.store.un("remove", this.onRemove);
9296             this.store.un("update", this.onUpdate);
9297             this.store.un("clear", this.refresh);
9298             this.store.un("beforeload", this.onBeforeLoad);
9299             this.store.un("load", this.onLoad);
9300             this.store.un("loadexception", this.onLoad);
9301         }
9302         if(store){
9303           
9304             store.on("datachanged", this.refresh, this);
9305             store.on("add", this.onAdd, this);
9306             store.on("remove", this.onRemove, this);
9307             store.on("update", this.onUpdate, this);
9308             store.on("clear", this.refresh, this);
9309             store.on("beforeload", this.onBeforeLoad, this);
9310             store.on("load", this.onLoad, this);
9311             store.on("loadexception", this.onLoad, this);
9312         }
9313         
9314         if(store){
9315             this.refresh();
9316         }
9317     },
9318     /**
9319      * onbeforeLoad - masks the loading area.
9320      *
9321      */
9322     onBeforeLoad : function(store,opts)
9323     {
9324          Roo.log('onBeforeLoad');   
9325         if (!opts.add) {
9326             this.el.update("");
9327         }
9328         this.el.mask(this.mask ? this.mask : "Loading" ); 
9329     },
9330     onLoad : function ()
9331     {
9332         this.el.unmask();
9333     },
9334     
9335
9336     /**
9337      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9338      * @param {HTMLElement} node
9339      * @return {HTMLElement} The template node
9340      */
9341     findItemFromChild : function(node){
9342         var el = this.dataName  ?
9343             this.el.child('.roo-tpl-' + this.dataName,true) :
9344             this.el.dom; 
9345         
9346         if(!node || node.parentNode == el){
9347                     return node;
9348             }
9349             var p = node.parentNode;
9350             while(p && p != el){
9351             if(p.parentNode == el){
9352                 return p;
9353             }
9354             p = p.parentNode;
9355         }
9356             return null;
9357     },
9358
9359     /** @ignore */
9360     onClick : function(e){
9361         var item = this.findItemFromChild(e.getTarget());
9362         if(item){
9363             var index = this.indexOf(item);
9364             if(this.onItemClick(item, index, e) !== false){
9365                 this.fireEvent("click", this, index, item, e);
9366             }
9367         }else{
9368             this.clearSelections();
9369         }
9370     },
9371
9372     /** @ignore */
9373     onContextMenu : function(e){
9374         var item = this.findItemFromChild(e.getTarget());
9375         if(item){
9376             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9377         }
9378     },
9379
9380     /** @ignore */
9381     onDblClick : function(e){
9382         var item = this.findItemFromChild(e.getTarget());
9383         if(item){
9384             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9385         }
9386     },
9387
9388     onItemClick : function(item, index, e)
9389     {
9390         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9391             return false;
9392         }
9393         if (this.toggleSelect) {
9394             var m = this.isSelected(item) ? 'unselect' : 'select';
9395             Roo.log(m);
9396             var _t = this;
9397             _t[m](item, true, false);
9398             return true;
9399         }
9400         if(this.multiSelect || this.singleSelect){
9401             if(this.multiSelect && e.shiftKey && this.lastSelection){
9402                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9403             }else{
9404                 this.select(item, this.multiSelect && e.ctrlKey);
9405                 this.lastSelection = item;
9406             }
9407             e.preventDefault();
9408         }
9409         return true;
9410     },
9411
9412     /**
9413      * Get the number of selected nodes.
9414      * @return {Number}
9415      */
9416     getSelectionCount : function(){
9417         return this.selections.length;
9418     },
9419
9420     /**
9421      * Get the currently selected nodes.
9422      * @return {Array} An array of HTMLElements
9423      */
9424     getSelectedNodes : function(){
9425         return this.selections;
9426     },
9427
9428     /**
9429      * Get the indexes of the selected nodes.
9430      * @return {Array}
9431      */
9432     getSelectedIndexes : function(){
9433         var indexes = [], s = this.selections;
9434         for(var i = 0, len = s.length; i < len; i++){
9435             indexes.push(s[i].nodeIndex);
9436         }
9437         return indexes;
9438     },
9439
9440     /**
9441      * Clear all selections
9442      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9443      */
9444     clearSelections : function(suppressEvent){
9445         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9446             this.cmp.elements = this.selections;
9447             this.cmp.removeClass(this.selectedClass);
9448             this.selections = [];
9449             if(!suppressEvent){
9450                 this.fireEvent("selectionchange", this, this.selections);
9451             }
9452         }
9453     },
9454
9455     /**
9456      * Returns true if the passed node is selected
9457      * @param {HTMLElement/Number} node The node or node index
9458      * @return {Boolean}
9459      */
9460     isSelected : function(node){
9461         var s = this.selections;
9462         if(s.length < 1){
9463             return false;
9464         }
9465         node = this.getNode(node);
9466         return s.indexOf(node) !== -1;
9467     },
9468
9469     /**
9470      * Selects nodes.
9471      * @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
9472      * @param {Boolean} keepExisting (optional) true to keep existing selections
9473      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9474      */
9475     select : function(nodeInfo, keepExisting, suppressEvent){
9476         if(nodeInfo instanceof Array){
9477             if(!keepExisting){
9478                 this.clearSelections(true);
9479             }
9480             for(var i = 0, len = nodeInfo.length; i < len; i++){
9481                 this.select(nodeInfo[i], true, true);
9482             }
9483             return;
9484         } 
9485         var node = this.getNode(nodeInfo);
9486         if(!node || this.isSelected(node)){
9487             return; // already selected.
9488         }
9489         if(!keepExisting){
9490             this.clearSelections(true);
9491         }
9492         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9493             Roo.fly(node).addClass(this.selectedClass);
9494             this.selections.push(node);
9495             if(!suppressEvent){
9496                 this.fireEvent("selectionchange", this, this.selections);
9497             }
9498         }
9499         
9500         
9501     },
9502       /**
9503      * Unselects nodes.
9504      * @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
9505      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9506      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9507      */
9508     unselect : function(nodeInfo, keepExisting, suppressEvent)
9509     {
9510         if(nodeInfo instanceof Array){
9511             Roo.each(this.selections, function(s) {
9512                 this.unselect(s, nodeInfo);
9513             }, this);
9514             return;
9515         }
9516         var node = this.getNode(nodeInfo);
9517         if(!node || !this.isSelected(node)){
9518             Roo.log("not selected");
9519             return; // not selected.
9520         }
9521         // fireevent???
9522         var ns = [];
9523         Roo.each(this.selections, function(s) {
9524             if (s == node ) {
9525                 Roo.fly(node).removeClass(this.selectedClass);
9526
9527                 return;
9528             }
9529             ns.push(s);
9530         },this);
9531         
9532         this.selections= ns;
9533         this.fireEvent("selectionchange", this, this.selections);
9534     },
9535
9536     /**
9537      * Gets a template node.
9538      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9539      * @return {HTMLElement} The node or null if it wasn't found
9540      */
9541     getNode : function(nodeInfo){
9542         if(typeof nodeInfo == "string"){
9543             return document.getElementById(nodeInfo);
9544         }else if(typeof nodeInfo == "number"){
9545             return this.nodes[nodeInfo];
9546         }
9547         return nodeInfo;
9548     },
9549
9550     /**
9551      * Gets a range template nodes.
9552      * @param {Number} startIndex
9553      * @param {Number} endIndex
9554      * @return {Array} An array of nodes
9555      */
9556     getNodes : function(start, end){
9557         var ns = this.nodes;
9558         start = start || 0;
9559         end = typeof end == "undefined" ? ns.length - 1 : end;
9560         var nodes = [];
9561         if(start <= end){
9562             for(var i = start; i <= end; i++){
9563                 nodes.push(ns[i]);
9564             }
9565         } else{
9566             for(var i = start; i >= end; i--){
9567                 nodes.push(ns[i]);
9568             }
9569         }
9570         return nodes;
9571     },
9572
9573     /**
9574      * Finds the index of the passed node
9575      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9576      * @return {Number} The index of the node or -1
9577      */
9578     indexOf : function(node){
9579         node = this.getNode(node);
9580         if(typeof node.nodeIndex == "number"){
9581             return node.nodeIndex;
9582         }
9583         var ns = this.nodes;
9584         for(var i = 0, len = ns.length; i < len; i++){
9585             if(ns[i] == node){
9586                 return i;
9587             }
9588         }
9589         return -1;
9590     }
9591 });
9592 /*
9593  * - LGPL
9594  *
9595  * based on jquery fullcalendar
9596  * 
9597  */
9598
9599 Roo.bootstrap = Roo.bootstrap || {};
9600 /**
9601  * @class Roo.bootstrap.Calendar
9602  * @extends Roo.bootstrap.Component
9603  * Bootstrap Calendar class
9604  * @cfg {Boolean} loadMask (true|false) default false
9605     
9606  * @constructor
9607  * Create a new Container
9608  * @param {Object} config The config object
9609  */
9610
9611
9612
9613 Roo.bootstrap.Calendar = function(config){
9614     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9615      this.addEvents({
9616         /**
9617              * @event select
9618              * Fires when a date is selected
9619              * @param {DatePicker} this
9620              * @param {Date} date The selected date
9621              */
9622         'select': true,
9623         /**
9624              * @event monthchange
9625              * Fires when the displayed month changes 
9626              * @param {DatePicker} this
9627              * @param {Date} date The selected month
9628              */
9629         'monthchange': true,
9630         /**
9631              * @event evententer
9632              * Fires when mouse over an event
9633              * @param {Calendar} this
9634              * @param {event} Event
9635              */
9636         'evententer': true,
9637         /**
9638              * @event eventleave
9639              * Fires when the mouse leaves an
9640              * @param {Calendar} this
9641              * @param {event}
9642              */
9643         'eventleave': true,
9644         /**
9645              * @event eventclick
9646              * Fires when the mouse click an
9647              * @param {Calendar} this
9648              * @param {event}
9649              */
9650         'eventclick': true
9651         
9652     });
9653
9654 };
9655
9656 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9657     
9658      /**
9659      * @cfg {Number} startDay
9660      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9661      */
9662     startDay : 0,
9663     
9664     loadMask : false,
9665       
9666     getAutoCreate : function(){
9667         
9668         
9669         var fc_button = function(name, corner, style, content ) {
9670             return Roo.apply({},{
9671                 tag : 'span',
9672                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9673                          (corner.length ?
9674                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9675                             ''
9676                         ),
9677                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9678                 unselectable: 'on'
9679             });
9680         };
9681         
9682         var header = {
9683             tag : 'table',
9684             cls : 'fc-header',
9685             style : 'width:100%',
9686             cn : [
9687                 {
9688                     tag: 'tr',
9689                     cn : [
9690                         {
9691                             tag : 'td',
9692                             cls : 'fc-header-left',
9693                             cn : [
9694                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9695                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9696                                 { tag: 'span', cls: 'fc-header-space' },
9697                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9698                                 
9699                                 
9700                             ]
9701                         },
9702                         
9703                         {
9704                             tag : 'td',
9705                             cls : 'fc-header-center',
9706                             cn : [
9707                                 {
9708                                     tag: 'span',
9709                                     cls: 'fc-header-title',
9710                                     cn : {
9711                                         tag: 'H2',
9712                                         html : 'month / year'
9713                                     }
9714                                 }
9715                                 
9716                             ]
9717                         },
9718                         {
9719                             tag : 'td',
9720                             cls : 'fc-header-right',
9721                             cn : [
9722                           /*      fc_button('month', 'left', '', 'month' ),
9723                                 fc_button('week', '', '', 'week' ),
9724                                 fc_button('day', 'right', '', 'day' )
9725                             */    
9726                                 
9727                             ]
9728                         }
9729                         
9730                     ]
9731                 }
9732             ]
9733         };
9734         
9735        
9736         var cal_heads = function() {
9737             var ret = [];
9738             // fixme - handle this.
9739             
9740             for (var i =0; i < Date.dayNames.length; i++) {
9741                 var d = Date.dayNames[i];
9742                 ret.push({
9743                     tag: 'th',
9744                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9745                     html : d.substring(0,3)
9746                 });
9747                 
9748             }
9749             ret[0].cls += ' fc-first';
9750             ret[6].cls += ' fc-last';
9751             return ret;
9752         };
9753         var cal_cell = function(n) {
9754             return  {
9755                 tag: 'td',
9756                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9757                 cn : [
9758                     {
9759                         cn : [
9760                             {
9761                                 cls: 'fc-day-number',
9762                                 html: 'D'
9763                             },
9764                             {
9765                                 cls: 'fc-day-content',
9766                              
9767                                 cn : [
9768                                      {
9769                                         style: 'position: relative;' // height: 17px;
9770                                     }
9771                                 ]
9772                             }
9773                             
9774                             
9775                         ]
9776                     }
9777                 ]
9778                 
9779             }
9780         };
9781         var cal_rows = function() {
9782             
9783             var ret = []
9784             for (var r = 0; r < 6; r++) {
9785                 var row= {
9786                     tag : 'tr',
9787                     cls : 'fc-week',
9788                     cn : []
9789                 };
9790                 
9791                 for (var i =0; i < Date.dayNames.length; i++) {
9792                     var d = Date.dayNames[i];
9793                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9794
9795                 }
9796                 row.cn[0].cls+=' fc-first';
9797                 row.cn[0].cn[0].style = 'min-height:90px';
9798                 row.cn[6].cls+=' fc-last';
9799                 ret.push(row);
9800                 
9801             }
9802             ret[0].cls += ' fc-first';
9803             ret[4].cls += ' fc-prev-last';
9804             ret[5].cls += ' fc-last';
9805             return ret;
9806             
9807         };
9808         
9809         var cal_table = {
9810             tag: 'table',
9811             cls: 'fc-border-separate',
9812             style : 'width:100%',
9813             cellspacing  : 0,
9814             cn : [
9815                 { 
9816                     tag: 'thead',
9817                     cn : [
9818                         { 
9819                             tag: 'tr',
9820                             cls : 'fc-first fc-last',
9821                             cn : cal_heads()
9822                         }
9823                     ]
9824                 },
9825                 { 
9826                     tag: 'tbody',
9827                     cn : cal_rows()
9828                 }
9829                   
9830             ]
9831         };
9832          
9833          var cfg = {
9834             cls : 'fc fc-ltr',
9835             cn : [
9836                 header,
9837                 {
9838                     cls : 'fc-content',
9839                     style : "position: relative;",
9840                     cn : [
9841                         {
9842                             cls : 'fc-view fc-view-month fc-grid',
9843                             style : 'position: relative',
9844                             unselectable : 'on',
9845                             cn : [
9846                                 {
9847                                     cls : 'fc-event-container',
9848                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9849                                 },
9850                                 cal_table
9851                             ]
9852                         }
9853                     ]
9854     
9855                 }
9856            ] 
9857             
9858         };
9859         
9860          
9861         
9862         return cfg;
9863     },
9864     
9865     
9866     initEvents : function()
9867     {
9868         if(!this.store){
9869             throw "can not find store for calendar";
9870         }
9871         
9872         var mark = {
9873             tag: "div",
9874             cls:"x-dlg-mask",
9875             style: "text-align:center",
9876             cn: [
9877                 {
9878                     tag: "div",
9879                     style: "background-color:white;width:50%;margin:250 auto",
9880                     cn: [
9881                         {
9882                             tag: "img",
9883                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9884                         },
9885                         {
9886                             tag: "span",
9887                             html: "Loading"
9888                         }
9889                         
9890                     ]
9891                 }
9892             ]
9893         }
9894         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9895         
9896         var size = this.el.select('.fc-content', true).first().getSize();
9897         this.maskEl.setSize(size.width, size.height);
9898         this.maskEl.enableDisplayMode("block");
9899         if(!this.loadMask){
9900             this.maskEl.hide();
9901         }
9902         
9903         this.store = Roo.factory(this.store, Roo.data);
9904         this.store.on('load', this.onLoad, this);
9905         this.store.on('beforeload', this.onBeforeLoad, this);
9906         
9907         this.resize();
9908         
9909         this.cells = this.el.select('.fc-day',true);
9910         //Roo.log(this.cells);
9911         this.textNodes = this.el.query('.fc-day-number');
9912         this.cells.addClassOnOver('fc-state-hover');
9913         
9914         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
9915         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
9916         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
9917         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
9918         
9919         this.on('monthchange', this.onMonthChange, this);
9920         
9921         this.update(new Date().clearTime());
9922     },
9923     
9924     resize : function() {
9925         var sz  = this.el.getSize();
9926         
9927         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
9928         this.el.select('.fc-day-content div',true).setHeight(34);
9929     },
9930     
9931     
9932     // private
9933     showPrevMonth : function(e){
9934         this.update(this.activeDate.add("mo", -1));
9935     },
9936     showToday : function(e){
9937         this.update(new Date().clearTime());
9938     },
9939     // private
9940     showNextMonth : function(e){
9941         this.update(this.activeDate.add("mo", 1));
9942     },
9943
9944     // private
9945     showPrevYear : function(){
9946         this.update(this.activeDate.add("y", -1));
9947     },
9948
9949     // private
9950     showNextYear : function(){
9951         this.update(this.activeDate.add("y", 1));
9952     },
9953
9954     
9955    // private
9956     update : function(date)
9957     {
9958         var vd = this.activeDate;
9959         this.activeDate = date;
9960 //        if(vd && this.el){
9961 //            var t = date.getTime();
9962 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
9963 //                Roo.log('using add remove');
9964 //                
9965 //                this.fireEvent('monthchange', this, date);
9966 //                
9967 //                this.cells.removeClass("fc-state-highlight");
9968 //                this.cells.each(function(c){
9969 //                   if(c.dateValue == t){
9970 //                       c.addClass("fc-state-highlight");
9971 //                       setTimeout(function(){
9972 //                            try{c.dom.firstChild.focus();}catch(e){}
9973 //                       }, 50);
9974 //                       return false;
9975 //                   }
9976 //                   return true;
9977 //                });
9978 //                return;
9979 //            }
9980 //        }
9981         
9982         var days = date.getDaysInMonth();
9983         
9984         var firstOfMonth = date.getFirstDateOfMonth();
9985         var startingPos = firstOfMonth.getDay()-this.startDay;
9986         
9987         if(startingPos < this.startDay){
9988             startingPos += 7;
9989         }
9990         
9991         var pm = date.add(Date.MONTH, -1);
9992         var prevStart = pm.getDaysInMonth()-startingPos;
9993 //        
9994         this.cells = this.el.select('.fc-day',true);
9995         this.textNodes = this.el.query('.fc-day-number');
9996         this.cells.addClassOnOver('fc-state-hover');
9997         
9998         var cells = this.cells.elements;
9999         var textEls = this.textNodes;
10000         
10001         Roo.each(cells, function(cell){
10002             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10003         });
10004         
10005         days += startingPos;
10006
10007         // convert everything to numbers so it's fast
10008         var day = 86400000;
10009         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10010         //Roo.log(d);
10011         //Roo.log(pm);
10012         //Roo.log(prevStart);
10013         
10014         var today = new Date().clearTime().getTime();
10015         var sel = date.clearTime().getTime();
10016         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10017         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10018         var ddMatch = this.disabledDatesRE;
10019         var ddText = this.disabledDatesText;
10020         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10021         var ddaysText = this.disabledDaysText;
10022         var format = this.format;
10023         
10024         var setCellClass = function(cal, cell){
10025             
10026             //Roo.log('set Cell Class');
10027             cell.title = "";
10028             var t = d.getTime();
10029             
10030             //Roo.log(d);
10031             
10032             cell.dateValue = t;
10033             if(t == today){
10034                 cell.className += " fc-today";
10035                 cell.className += " fc-state-highlight";
10036                 cell.title = cal.todayText;
10037             }
10038             if(t == sel){
10039                 // disable highlight in other month..
10040                 //cell.className += " fc-state-highlight";
10041                 
10042             }
10043             // disabling
10044             if(t < min) {
10045                 cell.className = " fc-state-disabled";
10046                 cell.title = cal.minText;
10047                 return;
10048             }
10049             if(t > max) {
10050                 cell.className = " fc-state-disabled";
10051                 cell.title = cal.maxText;
10052                 return;
10053             }
10054             if(ddays){
10055                 if(ddays.indexOf(d.getDay()) != -1){
10056                     cell.title = ddaysText;
10057                     cell.className = " fc-state-disabled";
10058                 }
10059             }
10060             if(ddMatch && format){
10061                 var fvalue = d.dateFormat(format);
10062                 if(ddMatch.test(fvalue)){
10063                     cell.title = ddText.replace("%0", fvalue);
10064                     cell.className = " fc-state-disabled";
10065                 }
10066             }
10067             
10068             if (!cell.initialClassName) {
10069                 cell.initialClassName = cell.dom.className;
10070             }
10071             
10072             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10073         };
10074
10075         var i = 0;
10076         
10077         for(; i < startingPos; i++) {
10078             textEls[i].innerHTML = (++prevStart);
10079             d.setDate(d.getDate()+1);
10080             
10081             cells[i].className = "fc-past fc-other-month";
10082             setCellClass(this, cells[i]);
10083         }
10084         
10085         var intDay = 0;
10086         
10087         for(; i < days; i++){
10088             intDay = i - startingPos + 1;
10089             textEls[i].innerHTML = (intDay);
10090             d.setDate(d.getDate()+1);
10091             
10092             cells[i].className = ''; // "x-date-active";
10093             setCellClass(this, cells[i]);
10094         }
10095         var extraDays = 0;
10096         
10097         for(; i < 42; i++) {
10098             textEls[i].innerHTML = (++extraDays);
10099             d.setDate(d.getDate()+1);
10100             
10101             cells[i].className = "fc-future fc-other-month";
10102             setCellClass(this, cells[i]);
10103         }
10104         
10105         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10106         
10107         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10108         
10109         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10110         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10111         
10112         if(totalRows != 6){
10113             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10114             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10115         }
10116         
10117         this.fireEvent('monthchange', this, date);
10118         
10119         
10120         /*
10121         if(!this.internalRender){
10122             var main = this.el.dom.firstChild;
10123             var w = main.offsetWidth;
10124             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10125             Roo.fly(main).setWidth(w);
10126             this.internalRender = true;
10127             // opera does not respect the auto grow header center column
10128             // then, after it gets a width opera refuses to recalculate
10129             // without a second pass
10130             if(Roo.isOpera && !this.secondPass){
10131                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10132                 this.secondPass = true;
10133                 this.update.defer(10, this, [date]);
10134             }
10135         }
10136         */
10137         
10138     },
10139     
10140     findCell : function(dt) {
10141         dt = dt.clearTime().getTime();
10142         var ret = false;
10143         this.cells.each(function(c){
10144             //Roo.log("check " +c.dateValue + '?=' + dt);
10145             if(c.dateValue == dt){
10146                 ret = c;
10147                 return false;
10148             }
10149             return true;
10150         });
10151         
10152         return ret;
10153     },
10154     
10155     findCells : function(ev) {
10156         var s = ev.start.clone().clearTime().getTime();
10157        // Roo.log(s);
10158         var e= ev.end.clone().clearTime().getTime();
10159        // Roo.log(e);
10160         var ret = [];
10161         this.cells.each(function(c){
10162              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10163             
10164             if(c.dateValue > e){
10165                 return ;
10166             }
10167             if(c.dateValue < s){
10168                 return ;
10169             }
10170             ret.push(c);
10171         });
10172         
10173         return ret;    
10174     },
10175     
10176     findBestRow: function(cells)
10177     {
10178         var ret = 0;
10179         
10180         for (var i =0 ; i < cells.length;i++) {
10181             ret  = Math.max(cells[i].rows || 0,ret);
10182         }
10183         return ret;
10184         
10185     },
10186     
10187     
10188     addItem : function(ev)
10189     {
10190         // look for vertical location slot in
10191         var cells = this.findCells(ev);
10192         
10193         ev.row = this.findBestRow(cells);
10194         
10195         // work out the location.
10196         
10197         var crow = false;
10198         var rows = [];
10199         for(var i =0; i < cells.length; i++) {
10200             if (!crow) {
10201                 crow = {
10202                     start : cells[i],
10203                     end :  cells[i]
10204                 };
10205                 continue;
10206             }
10207             if (crow.start.getY() == cells[i].getY()) {
10208                 // on same row.
10209                 crow.end = cells[i];
10210                 continue;
10211             }
10212             // different row.
10213             rows.push(crow);
10214             crow = {
10215                 start: cells[i],
10216                 end : cells[i]
10217             };
10218             
10219         }
10220         
10221         rows.push(crow);
10222         ev.els = [];
10223         ev.rows = rows;
10224         ev.cells = cells;
10225         for (var i = 0; i < cells.length;i++) {
10226             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10227             
10228         }
10229         
10230         this.calevents.push(ev);
10231     },
10232     
10233     clearEvents: function() {
10234         
10235         if(!this.calevents){
10236             return;
10237         }
10238         
10239         Roo.each(this.cells.elements, function(c){
10240             c.rows = 0;
10241         });
10242         
10243         Roo.each(this.calevents, function(e) {
10244             Roo.each(e.els, function(el) {
10245                 el.un('mouseenter' ,this.onEventEnter, this);
10246                 el.un('mouseleave' ,this.onEventLeave, this);
10247                 el.remove();
10248             },this);
10249         },this);
10250         
10251     },
10252     
10253     renderEvents: function()
10254     {   
10255         // first make sure there is enough space..
10256         
10257         this.cells.each(function(c) {
10258         
10259             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10260         });
10261         
10262         for (var e = 0; e < this.calevents.length; e++) {
10263             var ev = this.calevents[e];
10264             var cells = ev.cells;
10265             var rows = ev.rows;
10266             
10267             for(var i =0; i < rows.length; i++) {
10268                 
10269                  
10270                 // how many rows should it span..
10271                 
10272                 var  cfg = {
10273                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10274                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10275                     
10276                     unselectable : "on",
10277                     cn : [
10278                         {
10279                             cls: 'fc-event-inner',
10280                             cn : [
10281 //                                {
10282 //                                  tag:'span',
10283 //                                  cls: 'fc-event-time',
10284 //                                  html : cells.length > 1 ? '' : ev.time
10285 //                                },
10286                                 {
10287                                   tag:'span',
10288                                   cls: 'fc-event-title',
10289                                   html : String.format('{0}', ev.title)
10290                                 }
10291                                 
10292                                 
10293                             ]
10294                         },
10295                         {
10296                             cls: 'ui-resizable-handle ui-resizable-e',
10297                             html : '&nbsp;&nbsp;&nbsp'
10298                         }
10299                         
10300                     ]
10301                 };
10302                 if (i == 0) {
10303                     cfg.cls += ' fc-event-start';
10304                 }
10305                 if ((i+1) == rows.length) {
10306                     cfg.cls += ' fc-event-end';
10307                 }
10308                 
10309                 var ctr = this.el.select('.fc-event-container',true).first();
10310                 var cg = ctr.createChild(cfg);
10311                 
10312                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10313                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10314                 cg.on('click', this.onEventClick, this, ev);
10315                 
10316                 ev.els.push(cg);
10317                 
10318                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10319                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10320                 //Roo.log(cg);
10321                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10322                 cg.setWidth(ebox.right - sbox.x -2);
10323             }
10324             
10325             
10326         }
10327         
10328     },
10329     
10330     onEventEnter: function (e, el,event,d) {
10331         this.fireEvent('evententer', this, el, event);
10332     },
10333     
10334     onEventLeave: function (e, el,event,d) {
10335         this.fireEvent('eventleave', this, el, event);
10336     },
10337     
10338     onEventClick: function (e, el,event,d) {
10339         this.fireEvent('eventclick', this, el, event);
10340     },
10341     
10342     onMonthChange: function () {
10343         this.store.load();
10344     },
10345     
10346     onLoad: function () 
10347     {   
10348         this.calevents = [];
10349         var cal = this;
10350         
10351         if(this.store.getCount() > 0){
10352             this.store.data.each(function(d){
10353                cal.addItem({
10354                     id : d.data.id,
10355                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10356                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10357                     time : d.data.start_time,
10358                     title : d.data.title,
10359                     description : d.data.description,
10360                     venue : d.data.venue
10361                 });
10362             });
10363         }
10364         
10365         this.renderEvents();
10366         
10367         if(this.loadMask){
10368             this.maskEl.hide();
10369         }
10370     },
10371     
10372     onBeforeLoad: function()
10373     {
10374         this.clearEvents();
10375         
10376         if(this.loadMask){
10377             this.maskEl.show();
10378         }
10379     }
10380 });
10381
10382  
10383  /*
10384  * - LGPL
10385  *
10386  * element
10387  * 
10388  */
10389
10390 /**
10391  * @class Roo.bootstrap.Popover
10392  * @extends Roo.bootstrap.Component
10393  * Bootstrap Popover class
10394  * @cfg {String} html contents of the popover   (or false to use children..)
10395  * @cfg {String} title of popover (or false to hide)
10396  * @cfg {String} placement how it is placed
10397  * @cfg {String} trigger click || hover (or false to trigger manually)
10398  * @cfg {String} over what (parent or false to trigger manually.)
10399  * 
10400  * @constructor
10401  * Create a new Popover
10402  * @param {Object} config The config object
10403  */
10404
10405 Roo.bootstrap.Popover = function(config){
10406     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10407 };
10408
10409 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10410     
10411     title: 'Fill in a title',
10412     html: false,
10413     
10414     placement : 'right',
10415     trigger : 'hover', // hover
10416     
10417     over: 'parent',
10418     
10419     can_build_overlaid : false,
10420     
10421     getChildContainer : function()
10422     {
10423         return this.el.select('.popover-content',true).first();
10424     },
10425     
10426     getAutoCreate : function(){
10427          Roo.log('make popover?');
10428         var cfg = {
10429            cls : 'popover roo-dynamic',
10430            style: 'display:block',
10431            cn : [
10432                 {
10433                     cls : 'arrow'
10434                 },
10435                 {
10436                     cls : 'popover-inner',
10437                     cn : [
10438                         {
10439                             tag: 'h3',
10440                             cls: 'popover-title',
10441                             html : this.title
10442                         },
10443                         {
10444                             cls : 'popover-content',
10445                             html : this.html
10446                         }
10447                     ]
10448                     
10449                 }
10450            ]
10451         };
10452         
10453         return cfg;
10454     },
10455     setTitle: function(str)
10456     {
10457         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10458     },
10459     setContent: function(str)
10460     {
10461         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10462     },
10463     // as it get's added to the bottom of the page.
10464     onRender : function(ct, position)
10465     {
10466         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10467         if(!this.el){
10468             var cfg = Roo.apply({},  this.getAutoCreate());
10469             cfg.id = Roo.id();
10470             
10471             if (this.cls) {
10472                 cfg.cls += ' ' + this.cls;
10473             }
10474             if (this.style) {
10475                 cfg.style = this.style;
10476             }
10477             Roo.log("adding to ")
10478             this.el = Roo.get(document.body).createChild(cfg, position);
10479             Roo.log(this.el);
10480         }
10481         this.initEvents();
10482     },
10483     
10484     initEvents : function()
10485     {
10486         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10487         this.el.enableDisplayMode('block');
10488         this.el.hide();
10489         if (this.over === false) {
10490             return; 
10491         }
10492         if (this.triggers === false) {
10493             return;
10494         }
10495         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10496         var triggers = this.trigger ? this.trigger.split(' ') : [];
10497         Roo.each(triggers, function(trigger) {
10498         
10499             if (trigger == 'click') {
10500                 on_el.on('click', this.toggle, this);
10501             } else if (trigger != 'manual') {
10502                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10503                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10504       
10505                 on_el.on(eventIn  ,this.enter, this);
10506                 on_el.on(eventOut, this.leave, this);
10507             }
10508         }, this);
10509         
10510     },
10511     
10512     
10513     // private
10514     timeout : null,
10515     hoverState : null,
10516     
10517     toggle : function () {
10518         this.hoverState == 'in' ? this.leave() : this.enter();
10519     },
10520     
10521     enter : function () {
10522        
10523     
10524         clearTimeout(this.timeout);
10525     
10526         this.hoverState = 'in'
10527     
10528         if (!this.delay || !this.delay.show) {
10529             this.show();
10530             return 
10531         }
10532         var _t = this;
10533         this.timeout = setTimeout(function () {
10534             if (_t.hoverState == 'in') {
10535                 _t.show();
10536             }
10537         }, this.delay.show)
10538     },
10539     leave : function() {
10540         clearTimeout(this.timeout);
10541     
10542         this.hoverState = 'out'
10543     
10544         if (!this.delay || !this.delay.hide) {
10545             this.hide();
10546             return 
10547         }
10548         var _t = this;
10549         this.timeout = setTimeout(function () {
10550             if (_t.hoverState == 'out') {
10551                 _t.hide();
10552             }
10553         }, this.delay.hide)
10554     },
10555     
10556     show : function (on_el)
10557     {
10558         if (!on_el) {
10559             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10560         }
10561         // set content.
10562         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10563         if (this.html !== false) {
10564             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10565         }
10566         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10567         if (!this.title.length) {
10568             this.el.select('.popover-title',true).hide();
10569         }
10570         
10571         var placement = typeof this.placement == 'function' ?
10572             this.placement.call(this, this.el, on_el) :
10573             this.placement;
10574             
10575         var autoToken = /\s?auto?\s?/i;
10576         var autoPlace = autoToken.test(placement);
10577         if (autoPlace) {
10578             placement = placement.replace(autoToken, '') || 'top';
10579         }
10580         
10581         //this.el.detach()
10582         //this.el.setXY([0,0]);
10583         this.el.show();
10584         this.el.dom.style.display='block';
10585         this.el.addClass(placement);
10586         
10587         //this.el.appendTo(on_el);
10588         
10589         var p = this.getPosition();
10590         var box = this.el.getBox();
10591         
10592         if (autoPlace) {
10593             // fixme..
10594         }
10595         var align = Roo.bootstrap.Popover.alignment[placement]
10596         this.el.alignTo(on_el, align[0],align[1]);
10597         //var arrow = this.el.select('.arrow',true).first();
10598         //arrow.set(align[2], 
10599         
10600         this.el.addClass('in');
10601         this.hoverState = null;
10602         
10603         if (this.el.hasClass('fade')) {
10604             // fade it?
10605         }
10606         
10607     },
10608     hide : function()
10609     {
10610         this.el.setXY([0,0]);
10611         this.el.removeClass('in');
10612         this.el.hide();
10613         
10614     }
10615     
10616 });
10617
10618 Roo.bootstrap.Popover.alignment = {
10619     'left' : ['r-l', [-10,0], 'right'],
10620     'right' : ['l-r', [10,0], 'left'],
10621     'bottom' : ['t-b', [0,10], 'top'],
10622     'top' : [ 'b-t', [0,-10], 'bottom']
10623 };
10624
10625  /*
10626  * - LGPL
10627  *
10628  * Progress
10629  * 
10630  */
10631
10632 /**
10633  * @class Roo.bootstrap.Progress
10634  * @extends Roo.bootstrap.Component
10635  * Bootstrap Progress class
10636  * @cfg {Boolean} striped striped of the progress bar
10637  * @cfg {Boolean} active animated of the progress bar
10638  * 
10639  * 
10640  * @constructor
10641  * Create a new Progress
10642  * @param {Object} config The config object
10643  */
10644
10645 Roo.bootstrap.Progress = function(config){
10646     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10647 };
10648
10649 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10650     
10651     striped : false,
10652     active: false,
10653     
10654     getAutoCreate : function(){
10655         var cfg = {
10656             tag: 'div',
10657             cls: 'progress'
10658         };
10659         
10660         
10661         if(this.striped){
10662             cfg.cls += ' progress-striped';
10663         }
10664       
10665         if(this.active){
10666             cfg.cls += ' active';
10667         }
10668         
10669         
10670         return cfg;
10671     }
10672    
10673 });
10674
10675  
10676
10677  /*
10678  * - LGPL
10679  *
10680  * ProgressBar
10681  * 
10682  */
10683
10684 /**
10685  * @class Roo.bootstrap.ProgressBar
10686  * @extends Roo.bootstrap.Component
10687  * Bootstrap ProgressBar class
10688  * @cfg {Number} aria_valuenow aria-value now
10689  * @cfg {Number} aria_valuemin aria-value min
10690  * @cfg {Number} aria_valuemax aria-value max
10691  * @cfg {String} label label for the progress bar
10692  * @cfg {String} panel (success | info | warning | danger )
10693  * @cfg {String} role role of the progress bar
10694  * @cfg {String} sr_only text
10695  * 
10696  * 
10697  * @constructor
10698  * Create a new ProgressBar
10699  * @param {Object} config The config object
10700  */
10701
10702 Roo.bootstrap.ProgressBar = function(config){
10703     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10704 };
10705
10706 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10707     
10708     aria_valuenow : 0,
10709     aria_valuemin : 0,
10710     aria_valuemax : 100,
10711     label : false,
10712     panel : false,
10713     role : false,
10714     sr_only: false,
10715     
10716     getAutoCreate : function()
10717     {
10718         
10719         var cfg = {
10720             tag: 'div',
10721             cls: 'progress-bar',
10722             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10723         };
10724         
10725         if(this.sr_only){
10726             cfg.cn = {
10727                 tag: 'span',
10728                 cls: 'sr-only',
10729                 html: this.sr_only
10730             }
10731         }
10732         
10733         if(this.role){
10734             cfg.role = this.role;
10735         }
10736         
10737         if(this.aria_valuenow){
10738             cfg['aria-valuenow'] = this.aria_valuenow;
10739         }
10740         
10741         if(this.aria_valuemin){
10742             cfg['aria-valuemin'] = this.aria_valuemin;
10743         }
10744         
10745         if(this.aria_valuemax){
10746             cfg['aria-valuemax'] = this.aria_valuemax;
10747         }
10748         
10749         if(this.label && !this.sr_only){
10750             cfg.html = this.label;
10751         }
10752         
10753         if(this.panel){
10754             cfg.cls += ' progress-bar-' + this.panel;
10755         }
10756         
10757         return cfg;
10758     },
10759     
10760     update : function(aria_valuenow)
10761     {
10762         this.aria_valuenow = aria_valuenow;
10763         
10764         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10765     }
10766    
10767 });
10768
10769  
10770
10771  /*
10772  * - LGPL
10773  *
10774  * TabPanel
10775  * 
10776  */
10777
10778 /**
10779  * @class Roo.bootstrap.TabPanel
10780  * @extends Roo.bootstrap.Component
10781  * Bootstrap TabPanel class
10782  * @cfg {Boolean} active panel active
10783  * @cfg {String} html panel content
10784  * @cfg {String} tabId tab relate id
10785  * 
10786  * 
10787  * @constructor
10788  * Create a new TabPanel
10789  * @param {Object} config The config object
10790  */
10791
10792 Roo.bootstrap.TabPanel = function(config){
10793     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10794 };
10795
10796 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10797     
10798     active: false,
10799     html: false,
10800     tabId: false,
10801     
10802     getAutoCreate : function(){
10803         var cfg = {
10804             tag: 'div',
10805             cls: 'tab-pane',
10806             html: this.html || ''
10807         };
10808         
10809         if(this.active){
10810             cfg.cls += ' active';
10811         }
10812         
10813         if(this.tabId){
10814             cfg.tabId = this.tabId;
10815         }
10816         
10817         return cfg;
10818     }
10819    
10820 });
10821
10822  
10823
10824  /*
10825  * - LGPL
10826  *
10827  * DateField
10828  * 
10829  */
10830
10831 /**
10832  * @class Roo.bootstrap.DateField
10833  * @extends Roo.bootstrap.Input
10834  * Bootstrap DateField class
10835  * @cfg {Number} weekStart default 0
10836  * @cfg {Number} weekStart default 0
10837  * @cfg {Number} viewMode default empty, (months|years)
10838  * @cfg {Number} minViewMode default empty, (months|years)
10839  * @cfg {Number} startDate default -Infinity
10840  * @cfg {Number} endDate default Infinity
10841  * @cfg {Boolean} todayHighlight default false
10842  * @cfg {Boolean} todayBtn default false
10843  * @cfg {Boolean} calendarWeeks default false
10844  * @cfg {Object} daysOfWeekDisabled default empty
10845  * 
10846  * @cfg {Boolean} keyboardNavigation default true
10847  * @cfg {String} language default en
10848  * 
10849  * @constructor
10850  * Create a new DateField
10851  * @param {Object} config The config object
10852  */
10853
10854 Roo.bootstrap.DateField = function(config){
10855     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10856 };
10857
10858 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10859     
10860     /**
10861      * @cfg {String} format
10862      * The default date format string which can be overriden for localization support.  The format must be
10863      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10864      */
10865     format : "m/d/y",
10866     /**
10867      * @cfg {String} altFormats
10868      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10869      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10870      */
10871     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10872     
10873     weekStart : 0,
10874     
10875     viewMode : '',
10876     
10877     minViewMode : '',
10878     
10879     todayHighlight : false,
10880     
10881     todayBtn: false,
10882     
10883     language: 'en',
10884     
10885     keyboardNavigation: true,
10886     
10887     calendarWeeks: false,
10888     
10889     startDate: -Infinity,
10890     
10891     endDate: Infinity,
10892     
10893     daysOfWeekDisabled: [],
10894     
10895     _events: [],
10896     
10897     UTCDate: function()
10898     {
10899         return new Date(Date.UTC.apply(Date, arguments));
10900     },
10901     
10902     UTCToday: function()
10903     {
10904         var today = new Date();
10905         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
10906     },
10907     
10908     getDate: function() {
10909             var d = this.getUTCDate();
10910             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
10911     },
10912     
10913     getUTCDate: function() {
10914             return this.date;
10915     },
10916     
10917     setDate: function(d) {
10918             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
10919     },
10920     
10921     setUTCDate: function(d) {
10922             this.date = d;
10923             this.setValue(this.formatDate(this.date));
10924     },
10925         
10926     onRender: function(ct, position)
10927     {
10928         
10929         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
10930         
10931         this.language = this.language || 'en';
10932         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
10933         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
10934         
10935         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
10936         this.format = this.format || 'm/d/y';
10937         this.isInline = false;
10938         this.isInput = true;
10939         this.component = this.el.select('.add-on', true).first() || false;
10940         this.component = (this.component && this.component.length === 0) ? false : this.component;
10941         this.hasInput = this.component && this.inputEL().length;
10942         
10943         if (typeof(this.minViewMode === 'string')) {
10944             switch (this.minViewMode) {
10945                 case 'months':
10946                     this.minViewMode = 1;
10947                     break;
10948                 case 'years':
10949                     this.minViewMode = 2;
10950                     break;
10951                 default:
10952                     this.minViewMode = 0;
10953                     break;
10954             }
10955         }
10956         
10957         if (typeof(this.viewMode === 'string')) {
10958             switch (this.viewMode) {
10959                 case 'months':
10960                     this.viewMode = 1;
10961                     break;
10962                 case 'years':
10963                     this.viewMode = 2;
10964                     break;
10965                 default:
10966                     this.viewMode = 0;
10967                     break;
10968             }
10969         }
10970                 
10971         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
10972         
10973         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10974         
10975         this.picker().on('mousedown', this.onMousedown, this);
10976         this.picker().on('click', this.onClick, this);
10977         
10978         this.picker().addClass('datepicker-dropdown');
10979         
10980         this.startViewMode = this.viewMode;
10981         
10982         
10983         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
10984             if(!this.calendarWeeks){
10985                 v.remove();
10986                 return;
10987             };
10988             
10989             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
10990             v.attr('colspan', function(i, val){
10991                 return parseInt(val) + 1;
10992             });
10993         })
10994                         
10995         
10996         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
10997         
10998         this.setStartDate(this.startDate);
10999         this.setEndDate(this.endDate);
11000         
11001         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11002         
11003         this.fillDow();
11004         this.fillMonths();
11005         this.update();
11006         this.showMode();
11007         
11008         if(this.isInline) {
11009             this.show();
11010         }
11011     },
11012     
11013     picker : function()
11014     {
11015         return this.el.select('.datepicker', true).first();
11016     },
11017     
11018     fillDow: function()
11019     {
11020         var dowCnt = this.weekStart;
11021         
11022         var dow = {
11023             tag: 'tr',
11024             cn: [
11025                 
11026             ]
11027         };
11028         
11029         if(this.calendarWeeks){
11030             dow.cn.push({
11031                 tag: 'th',
11032                 cls: 'cw',
11033                 html: '&nbsp;'
11034             })
11035         }
11036         
11037         while (dowCnt < this.weekStart + 7) {
11038             dow.cn.push({
11039                 tag: 'th',
11040                 cls: 'dow',
11041                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11042             });
11043         }
11044         
11045         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11046     },
11047     
11048     fillMonths: function()
11049     {    
11050         var i = 0
11051         var months = this.picker().select('>.datepicker-months td', true).first();
11052         
11053         months.dom.innerHTML = '';
11054         
11055         while (i < 12) {
11056             var month = {
11057                 tag: 'span',
11058                 cls: 'month',
11059                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11060             }
11061             
11062             months.createChild(month);
11063         }
11064         
11065     },
11066     
11067     update: function(){
11068         
11069         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11070         
11071         if (this.date < this.startDate) {
11072             this.viewDate = new Date(this.startDate);
11073         } else if (this.date > this.endDate) {
11074             this.viewDate = new Date(this.endDate);
11075         } else {
11076             this.viewDate = new Date(this.date);
11077         }
11078         
11079         this.fill();
11080     },
11081     
11082     fill: function() {
11083         var d = new Date(this.viewDate),
11084                 year = d.getUTCFullYear(),
11085                 month = d.getUTCMonth(),
11086                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11087                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11088                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11089                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11090                 currentDate = this.date && this.date.valueOf(),
11091                 today = this.UTCToday();
11092         
11093         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11094         
11095 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11096         
11097 //        this.picker.select('>tfoot th.today').
11098 //                                              .text(dates[this.language].today)
11099 //                                              .toggle(this.todayBtn !== false);
11100     
11101         this.updateNavArrows();
11102         this.fillMonths();
11103                                                 
11104         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11105         
11106         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11107          
11108         prevMonth.setUTCDate(day);
11109         
11110         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11111         
11112         var nextMonth = new Date(prevMonth);
11113         
11114         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11115         
11116         nextMonth = nextMonth.valueOf();
11117         
11118         var fillMonths = false;
11119         
11120         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11121         
11122         while(prevMonth.valueOf() < nextMonth) {
11123             var clsName = '';
11124             
11125             if (prevMonth.getUTCDay() === this.weekStart) {
11126                 if(fillMonths){
11127                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11128                 }
11129                     
11130                 fillMonths = {
11131                     tag: 'tr',
11132                     cn: []
11133                 };
11134                 
11135                 if(this.calendarWeeks){
11136                     // ISO 8601: First week contains first thursday.
11137                     // ISO also states week starts on Monday, but we can be more abstract here.
11138                     var
11139                     // Start of current week: based on weekstart/current date
11140                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11141                     // Thursday of this week
11142                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11143                     // First Thursday of year, year from thursday
11144                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11145                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11146                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11147                     
11148                     fillMonths.cn.push({
11149                         tag: 'td',
11150                         cls: 'cw',
11151                         html: calWeek
11152                     });
11153                 }
11154             }
11155             
11156             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11157                 clsName += ' old';
11158             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11159                 clsName += ' new';
11160             }
11161             if (this.todayHighlight &&
11162                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11163                 prevMonth.getUTCMonth() == today.getMonth() &&
11164                 prevMonth.getUTCDate() == today.getDate()) {
11165                 clsName += ' today';
11166             }
11167             
11168             if (currentDate && prevMonth.valueOf() === currentDate) {
11169                 clsName += ' active';
11170             }
11171             
11172             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11173                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11174                     clsName += ' disabled';
11175             }
11176             
11177             fillMonths.cn.push({
11178                 tag: 'td',
11179                 cls: 'day ' + clsName,
11180                 html: prevMonth.getDate()
11181             })
11182             
11183             prevMonth.setDate(prevMonth.getDate()+1);
11184         }
11185           
11186         var currentYear = this.date && this.date.getUTCFullYear();
11187         var currentMonth = this.date && this.date.getUTCMonth();
11188         
11189         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11190         
11191         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11192             v.removeClass('active');
11193             
11194             if(currentYear === year && k === currentMonth){
11195                 v.addClass('active');
11196             }
11197             
11198             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11199                 v.addClass('disabled');
11200             }
11201             
11202         });
11203         
11204         
11205         year = parseInt(year/10, 10) * 10;
11206         
11207         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11208         
11209         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11210         
11211         year -= 1;
11212         for (var i = -1; i < 11; i++) {
11213             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11214                 tag: 'span',
11215                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11216                 html: year
11217             })
11218             
11219             year += 1;
11220         }
11221     },
11222     
11223     showMode: function(dir) {
11224         if (dir) {
11225             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11226         }
11227         Roo.each(this.picker().select('>div',true).elements, function(v){
11228             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11229             v.hide();
11230         });
11231         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11232     },
11233     
11234     place: function()
11235     {
11236         if(this.isInline) return;
11237         
11238         this.picker().removeClass(['bottom', 'top']);
11239         
11240         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11241             /*
11242              * place to the top of element!
11243              *
11244              */
11245             
11246             this.picker().addClass('top');
11247             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11248             
11249             return;
11250         }
11251         
11252         this.picker().addClass('bottom');
11253         
11254         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11255     },
11256     
11257     parseDate : function(value){
11258         if(!value || value instanceof Date){
11259             return value;
11260         }
11261         var v = Date.parseDate(value, this.format);
11262         if (!v && this.useIso) {
11263             v = Date.parseDate(value, 'Y-m-d');
11264         }
11265         if(!v && this.altFormats){
11266             if(!this.altFormatsArray){
11267                 this.altFormatsArray = this.altFormats.split("|");
11268             }
11269             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11270                 v = Date.parseDate(value, this.altFormatsArray[i]);
11271             }
11272         }
11273         return v;
11274     },
11275     
11276     formatDate : function(date, fmt){
11277         return (!date || !(date instanceof Date)) ?
11278         date : date.dateFormat(fmt || this.format);
11279     },
11280     
11281     onFocus : function()
11282     {
11283         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11284         this.show();
11285     },
11286     
11287     onBlur : function()
11288     {
11289         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11290         this.hide();
11291     },
11292     
11293     show : function()
11294     {
11295         this.picker().show();
11296         this.update();
11297         this.place();
11298     },
11299     
11300     hide : function()
11301     {
11302         if(this.isInline) return;
11303         this.picker().hide();
11304         this.viewMode = this.startViewMode;
11305         this.showMode();
11306         
11307     },
11308     
11309     onMousedown: function(e){
11310         e.stopPropagation();
11311         e.preventDefault();
11312     },
11313     
11314     keyup: function(e){
11315         Roo.bootstrap.DateField.superclass.keyup.call(this);
11316         this.update();
11317         
11318     },
11319     
11320     fireKey: function(e){
11321         if (!this.picker().isVisible()){
11322             if (e.keyCode == 27) // allow escape to hide and re-show picker
11323                 this.show();
11324             return;
11325         }
11326         var dateChanged = false,
11327         dir, day, month,
11328         newDate, newViewDate;
11329         switch(e.keyCode){
11330             case 27: // escape
11331                 this.hide();
11332                 e.preventDefault();
11333                 break;
11334             case 37: // left
11335             case 39: // right
11336                 if (!this.keyboardNavigation) break;
11337                 dir = e.keyCode == 37 ? -1 : 1;
11338                 
11339                 if (e.ctrlKey){
11340                     newDate = this.moveYear(this.date, dir);
11341                     newViewDate = this.moveYear(this.viewDate, dir);
11342                 } else if (e.shiftKey){
11343                     newDate = this.moveMonth(this.date, dir);
11344                     newViewDate = this.moveMonth(this.viewDate, dir);
11345                 } else {
11346                     newDate = new Date(this.date);
11347                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11348                     newViewDate = new Date(this.viewDate);
11349                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11350                 }
11351                 if (this.dateWithinRange(newDate)){
11352                     this.date = newDate;
11353                     this.viewDate = newViewDate;
11354                     this.setValue(this.formatDate(this.date));
11355                     this.update();
11356                     e.preventDefault();
11357                     dateChanged = true;
11358                 }
11359                 break;
11360             case 38: // up
11361             case 40: // down
11362                 if (!this.keyboardNavigation) break;
11363                 dir = e.keyCode == 38 ? -1 : 1;
11364                 if (e.ctrlKey){
11365                     newDate = this.moveYear(this.date, dir);
11366                     newViewDate = this.moveYear(this.viewDate, dir);
11367                 } else if (e.shiftKey){
11368                     newDate = this.moveMonth(this.date, dir);
11369                     newViewDate = this.moveMonth(this.viewDate, dir);
11370                 } else {
11371                     newDate = new Date(this.date);
11372                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11373                     newViewDate = new Date(this.viewDate);
11374                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11375                 }
11376                 if (this.dateWithinRange(newDate)){
11377                     this.date = newDate;
11378                     this.viewDate = newViewDate;
11379                     this.setValue(this.formatDate(this.date));
11380                     this.update();
11381                     e.preventDefault();
11382                     dateChanged = true;
11383                 }
11384                 break;
11385             case 13: // enter
11386                 this.setValue(this.formatDate(this.date));
11387                 this.hide();
11388                 e.preventDefault();
11389                 break;
11390             case 9: // tab
11391                 this.setValue(this.formatDate(this.date));
11392                 this.hide();
11393                 break;
11394         }
11395     },
11396     
11397     
11398     onClick: function(e) {
11399         e.stopPropagation();
11400         e.preventDefault();
11401         
11402         var target = e.getTarget();
11403         
11404         if(target.nodeName.toLowerCase() === 'i'){
11405             target = Roo.get(target).dom.parentNode;
11406         }
11407         
11408         var nodeName = target.nodeName;
11409         var className = target.className;
11410         var html = target.innerHTML;
11411         
11412         switch(nodeName.toLowerCase()) {
11413             case 'th':
11414                 switch(className) {
11415                     case 'switch':
11416                         this.showMode(1);
11417                         break;
11418                     case 'prev':
11419                     case 'next':
11420                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11421                         switch(this.viewMode){
11422                                 case 0:
11423                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11424                                         break;
11425                                 case 1:
11426                                 case 2:
11427                                         this.viewDate = this.moveYear(this.viewDate, dir);
11428                                         break;
11429                         }
11430                         this.fill();
11431                         break;
11432                     case 'today':
11433                         var date = new Date();
11434                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11435                         this.fill()
11436                         this.setValue(this.formatDate(this.date));
11437                         this.hide();
11438                         break;
11439                 }
11440                 break;
11441             case 'span':
11442                 if (className.indexOf('disabled') === -1) {
11443                     this.viewDate.setUTCDate(1);
11444                     if (className.indexOf('month') !== -1) {
11445                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11446                     } else {
11447                         var year = parseInt(html, 10) || 0;
11448                         this.viewDate.setUTCFullYear(year);
11449                         
11450                     }
11451                     this.showMode(-1);
11452                     this.fill();
11453                 }
11454                 break;
11455                 
11456             case 'td':
11457                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11458                     var day = parseInt(html, 10) || 1;
11459                     var year = this.viewDate.getUTCFullYear(),
11460                         month = this.viewDate.getUTCMonth();
11461
11462                     if (className.indexOf('old') !== -1) {
11463                         if(month === 0 ){
11464                             month = 11;
11465                             year -= 1;
11466                         }else{
11467                             month -= 1;
11468                         }
11469                     } else if (className.indexOf('new') !== -1) {
11470                         if (month == 11) {
11471                             month = 0;
11472                             year += 1;
11473                         } else {
11474                             month += 1;
11475                         }
11476                     }
11477                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11478                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11479                     this.fill();
11480                     this.setValue(this.formatDate(this.date));
11481                     this.hide();
11482                 }
11483                 break;
11484         }
11485     },
11486     
11487     setStartDate: function(startDate){
11488         this.startDate = startDate || -Infinity;
11489         if (this.startDate !== -Infinity) {
11490             this.startDate = this.parseDate(this.startDate);
11491         }
11492         this.update();
11493         this.updateNavArrows();
11494     },
11495
11496     setEndDate: function(endDate){
11497         this.endDate = endDate || Infinity;
11498         if (this.endDate !== Infinity) {
11499             this.endDate = this.parseDate(this.endDate);
11500         }
11501         this.update();
11502         this.updateNavArrows();
11503     },
11504     
11505     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11506         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11507         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11508             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11509         }
11510         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11511             return parseInt(d, 10);
11512         });
11513         this.update();
11514         this.updateNavArrows();
11515     },
11516     
11517     updateNavArrows: function() {
11518         var d = new Date(this.viewDate),
11519         year = d.getUTCFullYear(),
11520         month = d.getUTCMonth();
11521         
11522         Roo.each(this.picker().select('.prev', true).elements, function(v){
11523             v.show();
11524             switch (this.viewMode) {
11525                 case 0:
11526
11527                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11528                         v.hide();
11529                     }
11530                     break;
11531                 case 1:
11532                 case 2:
11533                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11534                         v.hide();
11535                     }
11536                     break;
11537             }
11538         });
11539         
11540         Roo.each(this.picker().select('.next', true).elements, function(v){
11541             v.show();
11542             switch (this.viewMode) {
11543                 case 0:
11544
11545                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11546                         v.hide();
11547                     }
11548                     break;
11549                 case 1:
11550                 case 2:
11551                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11552                         v.hide();
11553                     }
11554                     break;
11555             }
11556         })
11557     },
11558     
11559     moveMonth: function(date, dir){
11560         if (!dir) return date;
11561         var new_date = new Date(date.valueOf()),
11562         day = new_date.getUTCDate(),
11563         month = new_date.getUTCMonth(),
11564         mag = Math.abs(dir),
11565         new_month, test;
11566         dir = dir > 0 ? 1 : -1;
11567         if (mag == 1){
11568             test = dir == -1
11569             // If going back one month, make sure month is not current month
11570             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11571             ? function(){
11572                 return new_date.getUTCMonth() == month;
11573             }
11574             // If going forward one month, make sure month is as expected
11575             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11576             : function(){
11577                 return new_date.getUTCMonth() != new_month;
11578             };
11579             new_month = month + dir;
11580             new_date.setUTCMonth(new_month);
11581             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11582             if (new_month < 0 || new_month > 11)
11583                 new_month = (new_month + 12) % 12;
11584         } else {
11585             // For magnitudes >1, move one month at a time...
11586             for (var i=0; i<mag; i++)
11587                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11588                 new_date = this.moveMonth(new_date, dir);
11589             // ...then reset the day, keeping it in the new month
11590             new_month = new_date.getUTCMonth();
11591             new_date.setUTCDate(day);
11592             test = function(){
11593                 return new_month != new_date.getUTCMonth();
11594             };
11595         }
11596         // Common date-resetting loop -- if date is beyond end of month, make it
11597         // end of month
11598         while (test()){
11599             new_date.setUTCDate(--day);
11600             new_date.setUTCMonth(new_month);
11601         }
11602         return new_date;
11603     },
11604
11605     moveYear: function(date, dir){
11606         return this.moveMonth(date, dir*12);
11607     },
11608
11609     dateWithinRange: function(date){
11610         return date >= this.startDate && date <= this.endDate;
11611     },
11612
11613     
11614     remove: function() {
11615         this.picker().remove();
11616     }
11617    
11618 });
11619
11620 Roo.apply(Roo.bootstrap.DateField,  {
11621     
11622     head : {
11623         tag: 'thead',
11624         cn: [
11625         {
11626             tag: 'tr',
11627             cn: [
11628             {
11629                 tag: 'th',
11630                 cls: 'prev',
11631                 html: '<i class="icon-arrow-left"/>'
11632             },
11633             {
11634                 tag: 'th',
11635                 cls: 'switch',
11636                 colspan: '5'
11637             },
11638             {
11639                 tag: 'th',
11640                 cls: 'next',
11641                 html: '<i class="icon-arrow-right"/>'
11642             }
11643
11644             ]
11645         }
11646         ]
11647     },
11648     
11649     content : {
11650         tag: 'tbody',
11651         cn: [
11652         {
11653             tag: 'tr',
11654             cn: [
11655             {
11656                 tag: 'td',
11657                 colspan: '7'
11658             }
11659             ]
11660         }
11661         ]
11662     },
11663     
11664     footer : {
11665         tag: 'tfoot',
11666         cn: [
11667         {
11668             tag: 'tr',
11669             cn: [
11670             {
11671                 tag: 'th',
11672                 colspan: '7',
11673                 cls: 'today'
11674             }
11675                     
11676             ]
11677         }
11678         ]
11679     },
11680     
11681     dates:{
11682         en: {
11683             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11684             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11685             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11686             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11687             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11688             today: "Today"
11689         }
11690     },
11691     
11692     modes: [
11693     {
11694         clsName: 'days',
11695         navFnc: 'Month',
11696         navStep: 1
11697     },
11698     {
11699         clsName: 'months',
11700         navFnc: 'FullYear',
11701         navStep: 1
11702     },
11703     {
11704         clsName: 'years',
11705         navFnc: 'FullYear',
11706         navStep: 10
11707     }]
11708 });
11709
11710 Roo.apply(Roo.bootstrap.DateField,  {
11711   
11712     template : {
11713         tag: 'div',
11714         cls: 'datepicker dropdown-menu',
11715         cn: [
11716         {
11717             tag: 'div',
11718             cls: 'datepicker-days',
11719             cn: [
11720             {
11721                 tag: 'table',
11722                 cls: 'table-condensed',
11723                 cn:[
11724                 Roo.bootstrap.DateField.head,
11725                 {
11726                     tag: 'tbody'
11727                 },
11728                 Roo.bootstrap.DateField.footer
11729                 ]
11730             }
11731             ]
11732         },
11733         {
11734             tag: 'div',
11735             cls: 'datepicker-months',
11736             cn: [
11737             {
11738                 tag: 'table',
11739                 cls: 'table-condensed',
11740                 cn:[
11741                 Roo.bootstrap.DateField.head,
11742                 Roo.bootstrap.DateField.content,
11743                 Roo.bootstrap.DateField.footer
11744                 ]
11745             }
11746             ]
11747         },
11748         {
11749             tag: 'div',
11750             cls: 'datepicker-years',
11751             cn: [
11752             {
11753                 tag: 'table',
11754                 cls: 'table-condensed',
11755                 cn:[
11756                 Roo.bootstrap.DateField.head,
11757                 Roo.bootstrap.DateField.content,
11758                 Roo.bootstrap.DateField.footer
11759                 ]
11760             }
11761             ]
11762         }
11763         ]
11764     }
11765 });
11766
11767  
11768
11769  /*
11770  * - LGPL
11771  *
11772  * TimeField
11773  * 
11774  */
11775
11776 /**
11777  * @class Roo.bootstrap.TimeField
11778  * @extends Roo.bootstrap.Input
11779  * Bootstrap DateField class
11780  * 
11781  * 
11782  * @constructor
11783  * Create a new TimeField
11784  * @param {Object} config The config object
11785  */
11786
11787 Roo.bootstrap.TimeField = function(config){
11788     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11789 };
11790
11791 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11792     
11793     /**
11794      * @cfg {String} format
11795      * The default time format string which can be overriden for localization support.  The format must be
11796      * valid according to {@link Date#parseDate} (defaults to 'H:i:s').
11797      */
11798     format : "H:i:s",
11799     
11800     
11801     UTCDate: function()
11802     {
11803         return new Date(Date.UTC.apply(Date, arguments));
11804     },
11805     
11806     UTCTime: function()
11807     {
11808         return new Date(Date.UTC.apply(Date, arguments));
11809     },
11810     
11811     UTCToday: function()
11812     {
11813         var today = new Date();
11814         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11815     },
11816     
11817     UTCTodayTime: function()
11818     {
11819         var today = new Date();
11820         return this.UTCTime(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate(), today.getUTCHours(), today.getUTCMinutes());
11821     },
11822     
11823     getDate: function() {
11824             var d = this.getUTCDate();
11825             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11826     },
11827     
11828     getUTCDate: function() {
11829             return this.date;
11830     },
11831     
11832     setDate: function(d) {
11833             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11834     },
11835     
11836     setUTCDate: function(d) {
11837             this.date = d;
11838             this.setValue(this.formatDate(this.date));
11839     },
11840         
11841     onRender: function(ct, position)
11842     {
11843         
11844         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11845 //        
11846 //        this.language = this.language || 'en';
11847 //        this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11848 //        this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11849 //        
11850 //        this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11851 //        this.format = this.format || 'm/d/y';
11852 //        this.isInline = false;
11853 //        this.isInput = true;
11854 //        this.component = this.el.select('.add-on', true).first() || false;
11855 //        this.component = (this.component && this.component.length === 0) ? false : this.component;
11856 //        this.hasInput = this.component && this.inputEL().length;
11857         
11858 //        if (typeof(this.minViewMode === 'string')) {
11859 //            switch (this.minViewMode) {
11860 //                case 'months':
11861 //                    this.minViewMode = 2;
11862 //                    break;
11863 //                case 'years':
11864 //                    this.minViewMode = 3;
11865 //                    break;
11866 //                case 'day':
11867 //                    this.minViewMode = 1;
11868 //                    break;
11869 //                default:
11870 //                    this.minViewMode = 0;
11871 //                    break;
11872 //            }
11873 //        }
11874         
11875 //        if (typeof(this.viewMode === 'string')) {
11876 //            switch (this.viewMode) {
11877 //                case 'months':
11878 //                    this.viewMode = 2;
11879 //                    break;
11880 //                case 'years':
11881 //                    this.viewMode = 2;
11882 //                    break;
11883 //                case 'day':
11884 //                    this.viewMode = 1;
11885 //                    break;
11886 //                default:
11887 //                    this.viewMode = 0;
11888 //                    break;
11889 //            }
11890 //        }
11891                 
11892         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11893         
11894         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11895         
11896         this.picker().on('mousedown', this.onMousedown, this);
11897         this.picker().on('click', this.onClick, this);
11898         
11899         this.picker().addClass('datepicker-dropdown');
11900         
11901 //        this.startViewMode = this.viewMode;
11902         
11903 //        this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11904         
11905 //        this.setStartDate(this.startDate);
11906 //        this.setEndDate(this.endDate);
11907         
11908 //        this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11909         
11910 //        this.fillDow();
11911 //        this.fillMonths();
11912         this.fillTime();
11913         this.update();
11914 //        this.showMode();
11915         
11916 //        if(this.showTime){
11917             
11918 //            var dayFoot = this.picker().select('>.datepicker-days tfoot th', true).first();
11919 //            var timeFoot = this.picker().select('>.datepicker-time tfoot th', true).first();
11920 //
11921 //            var dayFootIcon = this.picker().select('>.datepicker-days tfoot span.picker-switch-icon', true).first();
11922 //            var timeFootIcon = this.picker().select('>.datepicker-time tfoot span.picker-switch-icon', true).first();
11923 //            
11924 //            timeFoot.addClass('switch-calendar');
11925 //            dayFoot.addClass('switch-time');
11926 //            
11927 //            timeFootIcon.addClass('switch-calendar');
11928 //            timeFootIcon.addClass('glyphicon-calendar');
11929 //            
11930 //            dayFootIcon.addClass('switch-time');
11931 //            dayFootIcon.addClass('glyphicon-time');
11932             
11933         var hours_up = this.picker().select('>.datepicker-time span.hours-up', true).first();
11934         var hours_down = this.picker().select('>.datepicker-time span.hours-down', true).first();
11935         var minutes_up = this.picker().select('>.datepicker-time span.minutes-up', true).first();
11936         var minutes_down = this.picker().select('>.datepicker-time span.minutes-down', true).first();
11937
11938         var period = this.picker().select('>.datepicker-time button', true).first();
11939
11940         hours_up.on('click', this.onIncrementHours, hours_up);
11941         hours_down.on('click', this.onDecrementHours, hours_down);
11942         minutes_up.on('click', this.onIncrementMinutes, minutes_up);
11943         minutes_down.on('click', this.onDecrementMinutes, minutes_down);
11944
11945         period.on('click', this.onTogglePeriod, period);
11946             
11947 //        }else{
11948 //            Roo.each(this.picker().select('tfoot th', true).elements, function(v){
11949 //                v.remove();
11950 //            });
11951 //        }
11952 //        
11953 //        if(this.isInline) {
11954 //            this.show();
11955 //        }
11956     },
11957     
11958     picker : function()
11959     {
11960         return this.el.select('.datepicker', true).first();
11961     },
11962     
11963     fillTime: function()
11964     {    
11965         var time = this.picker().select('>.datepicker-time tbody', true).first();
11966         
11967         time.dom.innerHTML = '';
11968         
11969         time.createChild({
11970             tag: 'tr',
11971             cn: [
11972                 {
11973                     tag: 'td',
11974                     cn: [
11975                         {
11976                             tag: 'a',
11977                             href: '#',
11978                             cls: 'btn',
11979                             cn: [
11980                                 {
11981                                     tag: 'span',
11982                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
11983                                 }
11984                             ]
11985                         } 
11986                     ]
11987                 },
11988                 {
11989                     tag: 'td',
11990                     cls: 'separator'
11991                 },
11992                 {
11993                     tag: 'td',
11994                     cn: [
11995                         {
11996                             tag: 'a',
11997                             href: '#',
11998                             cls: 'btn',
11999                             cn: [
12000                                 {
12001                                     tag: 'span',
12002                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12003                                 }
12004                             ]
12005                         }
12006                     ]
12007                 },
12008                 {
12009                     tag: 'td',
12010                     cls: 'separator'
12011                 }
12012             ]
12013         });
12014         
12015         time.createChild({
12016             tag: 'tr',
12017             cn: [
12018                 {
12019                     tag: 'td',
12020                     cn: [
12021                         {
12022                             tag: 'span',
12023                             cls: 'timepicker-hour',
12024                             html: '00'
12025                         }  
12026                     ]
12027                 },
12028                 {
12029                     tag: 'td',
12030                     cls: 'separator',
12031                     html: ':'
12032                 },
12033                 {
12034                     tag: 'td',
12035                     cn: [
12036                         {
12037                             tag: 'span',
12038                             cls: 'timepicker-minute',
12039                             html: '00'
12040                         }  
12041                     ]
12042                 },
12043                 {
12044                     tag: 'td',
12045                     cls: 'separator'
12046                 },
12047                 {
12048                     tag: 'td',
12049                     cn: [
12050                         {
12051                             tag: 'button',
12052                             type: 'button',
12053                             cls: 'btn btn-primary',
12054                             html: 'AM'
12055                             
12056                         }
12057                     ]
12058                 }
12059             ]
12060         });
12061         
12062         time.createChild({
12063             tag: 'tr',
12064             cn: [
12065                 {
12066                     tag: 'td',
12067                     cn: [
12068                         {
12069                             tag: 'a',
12070                             href: '#',
12071                             cls: 'btn',
12072                             cn: [
12073                                 {
12074                                     tag: 'span',
12075                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12076                                 }
12077                             ]
12078                         }
12079                     ]
12080                 },
12081                 {
12082                     tag: 'td',
12083                     cls: 'separator'
12084                 },
12085                 {
12086                     tag: 'td',
12087                     cn: [
12088                         {
12089                             tag: 'a',
12090                             href: '#',
12091                             cls: 'btn',
12092                             cn: [
12093                                 {
12094                                     tag: 'span',
12095                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12096                                 }
12097                             ]
12098                         }
12099                     ]
12100                 },
12101                 {
12102                     tag: 'td',
12103                     cls: 'separator'
12104                 }
12105             ]
12106         });
12107         
12108     },
12109     
12110     update: function()
12111     {
12112         
12113         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12114         
12115         this.fill();
12116     },
12117     
12118     fill: function() 
12119     {
12120         
12121         Roo.log(this.time.getHours());
12122 //        Roo.log(this.time.get());
12123         
12124     },
12125 //    
12126 //    showMode: function(dir) {
12127 //        if (dir) {
12128 //            this.viewMode = Math.max(this.minViewMode, Math.min(3, this.viewMode + dir));
12129 //        }
12130 //        
12131 //        Roo.each(this.picker().select('>div',true).elements, function(v){
12132 //            v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12133 //            v.hide();
12134 //        });
12135 //        this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12136 //        
12137 //    },
12138     
12139     place: function()
12140     {
12141 //        if(this.isInline) return;
12142         
12143         this.picker().removeClass(['bottom', 'top']);
12144         
12145         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12146             /*
12147              * place to the top of element!
12148              *
12149              */
12150             
12151             this.picker().addClass('top');
12152             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12153             
12154             return;
12155         }
12156         
12157         this.picker().addClass('bottom');
12158         
12159         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12160     },
12161     
12162 //    parseDate : function(value){
12163 //        if(!value || value instanceof Date){
12164 //            return value;
12165 //        }
12166 //        var v = Date.parseDate(value, this.format);
12167 //        if (!v && this.useIso) {
12168 //            v = Date.parseDate(value, 'Y-m-d');
12169 //        }
12170 //        if(!v && this.altFormats){
12171 //            if(!this.altFormatsArray){
12172 //                this.altFormatsArray = this.altFormats.split("|");
12173 //            }
12174 //            for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12175 //                v = Date.parseDate(value, this.altFormatsArray[i]);
12176 //            }
12177 //        }
12178 //        return v;
12179 //    },
12180     
12181 //    formatDate : function(date, fmt){
12182 //        return (!date || !(date instanceof Date)) ?
12183 //        date : date.dateFormat(fmt || this.format);
12184 //    },
12185     
12186     onFocus : function()
12187     {
12188         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12189         this.show();
12190     },
12191     
12192     onBlur : function()
12193     {
12194         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12195         this.hide();
12196     },
12197     
12198     show : function()
12199     {
12200         this.picker().show();
12201         this.update();
12202         this.place();
12203     },
12204     
12205     hide : function()
12206     {
12207         this.picker().hide();
12208         
12209     },
12210     
12211     onMousedown: function(e){
12212         e.stopPropagation();
12213         e.preventDefault();
12214     },
12215 //    
12216 //    keyup: function(e){
12217 //        Roo.bootstrap.DateField.superclass.keyup.call(this);
12218 //        this.update();
12219 //        
12220 //    },
12221     
12222 //    fireKey: function(e){
12223 //        if (!this.picker().isVisible()){
12224 //            if (e.keyCode == 27) // allow escape to hide and re-show picker
12225 //                this.show();
12226 //            return;
12227 //        }
12228 //        var dateChanged = false,
12229 //        dir, day, month,
12230 //        newDate, newViewDate;
12231 //        switch(e.keyCode){
12232 //            case 27: // escape
12233 //                this.hide();
12234 //                e.preventDefault();
12235 //                break;
12236 //            case 37: // left
12237 //            case 39: // right
12238 //                if (!this.keyboardNavigation) break;
12239 //                dir = e.keyCode == 37 ? -1 : 1;
12240 //                
12241 //                if (e.ctrlKey){
12242 //                    newDate = this.moveYear(this.date, dir);
12243 //                    newViewDate = this.moveYear(this.viewDate, dir);
12244 //                } else if (e.shiftKey){
12245 //                    newDate = this.moveMonth(this.date, dir);
12246 //                    newViewDate = this.moveMonth(this.viewDate, dir);
12247 //                } else {
12248 //                    newDate = new Date(this.date);
12249 //                    newDate.setUTCDate(this.date.getUTCDate() + dir);
12250 //                    newViewDate = new Date(this.viewDate);
12251 //                    newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12252 //                }
12253 //                if (this.dateWithinRange(newDate)){
12254 //                    this.date = newDate;
12255 //                    this.viewDate = newViewDate;
12256 //                    this.setValue(this.formatDate(this.date));
12257 //                    this.update();
12258 //                    e.preventDefault();
12259 //                    dateChanged = true;
12260 //                }
12261 //                break;
12262 //            case 38: // up
12263 //            case 40: // down
12264 //                if (!this.keyboardNavigation) break;
12265 //                dir = e.keyCode == 38 ? -1 : 1;
12266 //                if (e.ctrlKey){
12267 //                    newDate = this.moveYear(this.date, dir);
12268 //                    newViewDate = this.moveYear(this.viewDate, dir);
12269 //                } else if (e.shiftKey){
12270 //                    newDate = this.moveMonth(this.date, dir);
12271 //                    newViewDate = this.moveMonth(this.viewDate, dir);
12272 //                } else {
12273 //                    newDate = new Date(this.date);
12274 //                    newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12275 //                    newViewDate = new Date(this.viewDate);
12276 //                    newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12277 //                }
12278 //                if (this.dateWithinRange(newDate)){
12279 //                    this.date = newDate;
12280 //                    this.viewDate = newViewDate;
12281 //                    this.setValue(this.formatDate(this.date));
12282 //                    this.update();
12283 //                    e.preventDefault();
12284 //                    dateChanged = true;
12285 //                }
12286 //                break;
12287 //            case 13: // enter
12288 //                this.setValue(this.formatDate(this.date));
12289 //                this.hide();
12290 //                e.preventDefault();
12291 //                break;
12292 //            case 9: // tab
12293 //                this.setValue(this.formatDate(this.date));
12294 //                this.hide();
12295 //                break;
12296 //        }
12297 //    },
12298     
12299     
12300     onClick: function(e) {
12301         e.stopPropagation();
12302         e.preventDefault();
12303         
12304         var target = e.getTarget();
12305         
12306 //        if(target.nodeName.toLowerCase() === 'i'){
12307 //            target = Roo.get(target).dom.parentNode;
12308 //        }
12309         
12310         var nodeName = target.nodeName.trim();
12311         var className = target.className.trim();
12312         var html = target.innerHTML;
12313         
12314         Roo.log('nodeName');
12315         Roo.log(nodeName);
12316         
12317         Roo.log('className');
12318         Roo.log(className);
12319         
12320         Roo.log('html');
12321         Roo.log(html);
12322         
12323         
12324 //        switch(nodeName.toLowerCase()) {
12325 //            case 'th':
12326 //                switch(className) {
12327 //                    case 'switch':
12328 //                        this.showMode(1);
12329 //                        break;
12330 //                    case 'prev':
12331 //                    case 'next':
12332 //                        var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12333 //                        switch(this.viewMode){
12334 //                                case 0:
12335 //                                        this.viewDate = this.moveMonth(this.viewDate, dir);
12336 //                                        break;
12337 //                                case 1:
12338 //                                case 2:
12339 //                                        this.viewDate = this.moveYear(this.viewDate, dir);
12340 //                                        break;
12341 //                        }
12342 //                        this.fill();
12343 //                        break;
12344 //                    case 'today':
12345 //                        var date = new Date();
12346 //                        this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12347 //                        this.fill()
12348 //                        this.setValue(this.formatDate(this.date));
12349 //                        this.hide();
12350 //                        break;
12351 //                     case 'switch-time':
12352 //                        this.showMode(-1);
12353 //                        this.fill();
12354 //                        break;
12355 //                     case 'switch-calendar':
12356 //                         this.showMode(1);
12357 //                         this.fill();
12358 //                         break;
12359 //                }
12360 //                break;
12361 //            case 'span':
12362 //                if (className.indexOf('disabled') === -1) {
12363 //                    this.viewDate.setUTCDate(1);
12364 //                    if (className.indexOf('month') !== -1) {
12365 //                        this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12366 //                    } else if(className.indexOf('picker-switch-icon') !== -1){
12367 //                        if(className.indexOf('switch-time') !== -1){
12368 //                            this.showMode(-1);
12369 //                            this.fill();
12370 //                        }else{
12371 //                            this.showMode(1);
12372 //                            this.fill();
12373 //                        }
12374 //                        
12375 //                        break;
12376 //                        
12377 //                    } else {
12378 //                        var year = parseInt(html, 10) || 0;
12379 //                        this.viewDate.setUTCFullYear(year);
12380 //                        
12381 //                    }
12382 //                    this.showMode(-1);
12383 //                    this.fill();
12384 //                }
12385 //                break;
12386 //                
12387 //            case 'td':
12388 //                if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12389 //                    var day = parseInt(html, 10) || 1;
12390 //                    var year = this.viewDate.getUTCFullYear(),
12391 //                        month = this.viewDate.getUTCMonth();
12392 //
12393 //                    if (className.indexOf('old') !== -1) {
12394 //                        if(month === 0 ){
12395 //                            month = 11;
12396 //                            year -= 1;
12397 //                        }else{
12398 //                            month -= 1;
12399 //                        }
12400 //                    } else if (className.indexOf('new') !== -1) {
12401 //                        if (month == 11) {
12402 //                            month = 0;
12403 //                            year += 1;
12404 //                        } else {
12405 //                            month += 1;
12406 //                        }
12407 //                    }
12408 //                    this.date = this.UTCDate(year, month, day,0,0,0,0);
12409 //                    this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12410 //                    this.fill();
12411 //                    this.setValue(this.formatDate(this.date));
12412 //                    this.hide();
12413 //                }
12414 //                break;
12415 //        }
12416     },
12417     
12418 //    setStartDate: function(startDate){
12419 //        this.startDate = startDate || -Infinity;
12420 //        if (this.startDate !== -Infinity) {
12421 //            this.startDate = this.parseDate(this.startDate);
12422 //        }
12423 //        this.update();
12424 //        this.updateNavArrows();
12425 //    },
12426 //
12427 //    setEndDate: function(endDate){
12428 //        this.endDate = endDate || Infinity;
12429 //        if (this.endDate !== Infinity) {
12430 //            this.endDate = this.parseDate(this.endDate);
12431 //        }
12432 //        this.update();
12433 //        this.updateNavArrows();
12434 //    },
12435 //    
12436 //    setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12437 //        this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12438 //        if (typeof(this.daysOfWeekDisabled) !== 'object') {
12439 //            this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12440 //        }
12441 //        this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12442 //            return parseInt(d, 10);
12443 //        });
12444 //        this.update();
12445 //        this.updateNavArrows();
12446 //    },
12447 //    
12448 //    updateNavArrows: function() {
12449 //        var d = new Date(this.viewDate),
12450 //        year = d.getUTCFullYear(),
12451 //        month = d.getUTCMonth();
12452 //        
12453 //        Roo.each(this.picker().select('.prev', true).elements, function(v){
12454 //            v.show();
12455 //            switch (this.viewMode) {
12456 //                case 0:
12457 //
12458 //                    if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12459 //                        v.hide();
12460 //                    }
12461 //                    break;
12462 //                case 1:
12463 //                case 2:
12464 //                    if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12465 //                        v.hide();
12466 //                    }
12467 //                    break;
12468 //            }
12469 //        });
12470 //        
12471 //        Roo.each(this.picker().select('.next', true).elements, function(v){
12472 //            v.show();
12473 //            switch (this.viewMode) {
12474 //                case 0:
12475 //
12476 //                    if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12477 //                        v.hide();
12478 //                    }
12479 //                    break;
12480 //                case 1:
12481 //                case 2:
12482 //                    if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12483 //                        v.hide();
12484 //                    }
12485 //                    break;
12486 //            }
12487 //        })
12488 //    },
12489 //    
12490 //    moveMonth: function(date, dir){
12491 //        if (!dir) return date;
12492 //        var new_date = new Date(date.valueOf()),
12493 //        day = new_date.getUTCDate(),
12494 //        month = new_date.getUTCMonth(),
12495 //        mag = Math.abs(dir),
12496 //        new_month, test;
12497 //        dir = dir > 0 ? 1 : -1;
12498 //        if (mag == 1){
12499 //            test = dir == -1
12500 //            // If going back one month, make sure month is not current month
12501 //            // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12502 //            ? function(){
12503 //                return new_date.getUTCMonth() == month;
12504 //            }
12505 //            // If going forward one month, make sure month is as expected
12506 //            // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12507 //            : function(){
12508 //                return new_date.getUTCMonth() != new_month;
12509 //            };
12510 //            new_month = month + dir;
12511 //            new_date.setUTCMonth(new_month);
12512 //            // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12513 //            if (new_month < 0 || new_month > 11)
12514 //                new_month = (new_month + 12) % 12;
12515 //        } else {
12516 //            // For magnitudes >1, move one month at a time...
12517 //            for (var i=0; i<mag; i++)
12518 //                // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12519 //                new_date = this.moveMonth(new_date, dir);
12520 //            // ...then reset the day, keeping it in the new month
12521 //            new_month = new_date.getUTCMonth();
12522 //            new_date.setUTCDate(day);
12523 //            test = function(){
12524 //                return new_month != new_date.getUTCMonth();
12525 //            };
12526 //        }
12527 //        // Common date-resetting loop -- if date is beyond end of month, make it
12528 //        // end of month
12529 //        while (test()){
12530 //            new_date.setUTCDate(--day);
12531 //            new_date.setUTCMonth(new_month);
12532 //        }
12533 //        return new_date;
12534 //    },
12535 //
12536 //    moveYear: function(date, dir){
12537 //        return this.moveMonth(date, dir*12);
12538 //    },
12539 //
12540 //    dateWithinRange: function(date){
12541 //        return date >= this.startDate && date <= this.endDate;
12542 //    },
12543
12544 //    remove: function() {
12545 //        this.picker().remove();
12546 //    },
12547     
12548     onIncrementHours: function()
12549     {
12550         Roo.log('onIncrementHours');
12551     },
12552     
12553     onDecrementHours: function()
12554     {
12555         Roo.log('onDecrementHours');
12556     },
12557     
12558     onIncrementMinutes: function()
12559     {
12560         Roo.log('onIncrementMinutes');
12561     },
12562     
12563     onDecrementMinutes: function()
12564     {
12565         Roo.log('onDecrementMinutes');
12566     },
12567     
12568     onTogglePeriod: function()
12569     {
12570         Roo.log('onTogglePeriod');
12571     }
12572     
12573    
12574 });
12575
12576 Roo.apply(Roo.bootstrap.TimeField,  {
12577     
12578     content : {
12579         tag: 'tbody',
12580         cn: [
12581         {
12582             tag: 'tr',
12583             cn: [
12584             {
12585                 tag: 'td',
12586                 colspan: '7'
12587             }
12588             ]
12589         }
12590         ]
12591     }
12592 });
12593
12594 Roo.apply(Roo.bootstrap.TimeField,  {
12595   
12596     template : {
12597         tag: 'div',
12598         cls: 'datepicker dropdown-menu',
12599         cn: [
12600             {
12601                 tag: 'div',
12602                 cls: 'datepicker-time',
12603                 cn: [
12604                 {
12605                     tag: 'table',
12606                     cls: 'table-condensed',
12607                     cn:[
12608                     Roo.bootstrap.DateField.content
12609                     ]
12610                 }
12611                 ]
12612             }
12613         ]
12614     }
12615 });
12616
12617  
12618
12619  /*
12620  * - LGPL
12621  *
12622  * CheckBox
12623  * 
12624  */
12625
12626 /**
12627  * @class Roo.bootstrap.CheckBox
12628  * @extends Roo.bootstrap.Input
12629  * Bootstrap CheckBox class
12630  * 
12631  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12632  * @cfg {String} boxLabel The text that appears beside the checkbox
12633  * @cfg {Boolean} checked initnal the element
12634  * 
12635  * @constructor
12636  * Create a new CheckBox
12637  * @param {Object} config The config object
12638  */
12639
12640 Roo.bootstrap.CheckBox = function(config){
12641     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12642    
12643         this.addEvents({
12644             /**
12645             * @event check
12646             * Fires when the element is checked or unchecked.
12647             * @param {Roo.bootstrap.CheckBox} this This input
12648             * @param {Boolean} checked The new checked value
12649             */
12650            check : true
12651         });
12652 };
12653
12654 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12655     
12656     inputType: 'checkbox',
12657     value: 1,
12658     valueOff: 0,
12659     boxLabel: false,
12660     checked: false,
12661     
12662     getAutoCreate : function()
12663     {
12664         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12665         
12666         var id = Roo.id();
12667         
12668         var cfg = {};
12669         
12670         cfg.cls = 'form-group' //input-group
12671         
12672         var input =  {
12673             tag: 'input',
12674             id : id,
12675             type : this.inputType,
12676             value : (!this.checked) ? this.valueOff : this.value,
12677             cls : 'form-box',
12678             placeholder : this.placeholder || ''
12679             
12680         };
12681         
12682         if (this.disabled) {
12683             input.disabled=true;
12684         }
12685         
12686         if(this.checked){
12687             input.checked = this.checked;
12688         }
12689         
12690         if (this.name) {
12691             input.name = this.name;
12692         }
12693         
12694         if (this.size) {
12695             input.cls += ' input-' + this.size;
12696         }
12697         
12698         var settings=this;
12699         ['xs','sm','md','lg'].map(function(size){
12700             if (settings[size]) {
12701                 cfg.cls += ' col-' + size + '-' + settings[size];
12702             }
12703         });
12704         
12705         var inputblock = input;
12706         
12707         if (this.before || this.after) {
12708             
12709             inputblock = {
12710                 cls : 'input-group',
12711                 cn :  [] 
12712             };
12713             if (this.before) {
12714                 inputblock.cn.push({
12715                     tag :'span',
12716                     cls : 'input-group-addon',
12717                     html : this.before
12718                 });
12719             }
12720             inputblock.cn.push(input);
12721             if (this.after) {
12722                 inputblock.cn.push({
12723                     tag :'span',
12724                     cls : 'input-group-addon',
12725                     html : this.after
12726                 });
12727             }
12728             
12729         };
12730         
12731         if (align ==='left' && this.fieldLabel.length) {
12732                 Roo.log("left and has label");
12733                 cfg.cn = [
12734                     
12735                     {
12736                         tag: 'label',
12737                         'for' :  id,
12738                         cls : 'control-label col-md-' + this.labelWidth,
12739                         html : this.fieldLabel
12740                         
12741                     },
12742                     {
12743                         cls : "col-md-" + (12 - this.labelWidth), 
12744                         cn: [
12745                             inputblock
12746                         ]
12747                     }
12748                     
12749                 ];
12750         } else if ( this.fieldLabel.length) {
12751                 Roo.log(" label");
12752                  cfg.cn = [
12753                    
12754                     {
12755                         tag: 'label',
12756                         'for': id,
12757                         cls: 'control-label box-input-label',
12758                         //cls : 'input-group-addon',
12759                         html : this.fieldLabel
12760                         
12761                     },
12762                     
12763                     inputblock
12764                     
12765                 ];
12766
12767         } else {
12768             
12769                    Roo.log(" no label && no align");
12770                 cfg.cn = [
12771                     
12772                         inputblock
12773                     
12774                 ];
12775                 
12776                 
12777         };
12778         
12779         if(this.boxLabel){
12780             cfg.cn.push({
12781                 tag: 'span',
12782                 'for': id,
12783                 cls: 'box-label',
12784                 html: this.boxLabel
12785             })
12786         }
12787         
12788         return cfg;
12789         
12790     },
12791     
12792     /**
12793      * return the real input element.
12794      */
12795     inputEl: function ()
12796     {
12797         return this.el.select('input.form-box',true).first();
12798     },
12799     
12800     initEvents : function()
12801     {
12802         Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12803         
12804         this.inputEl().on('click', this.onClick,  this);
12805         
12806     },
12807     
12808     onClick : function()
12809     {   
12810         this.setChecked(!this.checked);
12811     },
12812     
12813     setChecked : function(state,suppressEvent)
12814     {
12815         this.checked = state;
12816         
12817         if(suppressEvent !== true){
12818             this.fireEvent('check', this, state);
12819         }
12820         
12821         this.inputEl().dom.value = state ? this.value : this.valueOff;
12822         
12823     }
12824 });
12825
12826  
12827 /*
12828  * - LGPL
12829  *
12830  * Radio
12831  * 
12832  */
12833
12834 /**
12835  * @class Roo.bootstrap.Radio
12836  * @extends Roo.bootstrap.CheckBox
12837  * Bootstrap Radio class
12838
12839  * @constructor
12840  * Create a new Radio
12841  * @param {Object} config The config object
12842  */
12843
12844 Roo.bootstrap.Radio = function(config){
12845     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12846    
12847 };
12848
12849 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12850     
12851     inputType: 'radio',
12852     
12853     getAutoCreate : function()
12854     {
12855         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12856         
12857         var id = Roo.id();
12858         
12859         var cfg = {};
12860         
12861         cfg.cls = 'form-group' //input-group
12862         
12863         var input =  {
12864             tag: 'input',
12865             id : id,
12866             type : this.inputType,
12867             value : (!this.checked) ? this.valueOff : this.value,
12868             cls : 'form-box',
12869             placeholder : this.placeholder || ''
12870             
12871         };
12872         
12873         if (this.disabled) {
12874             input.disabled=true;
12875         }
12876         
12877         if(this.checked){
12878             input.checked = this.checked;
12879         }
12880         
12881         if (this.name) {
12882             input.name = this.name;
12883         }
12884         
12885         if (this.size) {
12886             input.cls += ' input-' + this.size;
12887         }
12888         
12889         var settings=this;
12890         ['xs','sm','md','lg'].map(function(size){
12891             if (settings[size]) {
12892                 cfg.cls += ' col-' + size + '-' + settings[size];
12893             }
12894         });
12895         
12896         var inputblock = input;
12897         
12898         if (this.before || this.after) {
12899             
12900             inputblock = {
12901                 cls : 'input-group',
12902                 cn :  [] 
12903             };
12904             if (this.before) {
12905                 inputblock.cn.push({
12906                     tag :'span',
12907                     cls : 'input-group-addon',
12908                     html : this.before
12909                 });
12910             }
12911             inputblock.cn.push(input);
12912             if (this.after) {
12913                 inputblock.cn.push({
12914                     tag :'span',
12915                     cls : 'input-group-addon',
12916                     html : this.after
12917                 });
12918             }
12919             
12920         };
12921         
12922         if (align ==='left' && this.fieldLabel.length) {
12923                 Roo.log("left and has label");
12924                 cfg.cn = [
12925                     
12926                     {
12927                         tag: 'label',
12928                         'for' :  id,
12929                         cls : 'control-label col-md-' + this.labelWidth,
12930                         html : this.fieldLabel
12931                         
12932                     },
12933                     {
12934                         cls : "col-md-" + (12 - this.labelWidth), 
12935                         cn: [
12936                             inputblock
12937                         ]
12938                     }
12939                     
12940                 ];
12941         } else if ( this.fieldLabel.length) {
12942                 Roo.log(" label");
12943                  cfg.cn = [
12944                    
12945                     {
12946                         tag: 'label',
12947                         'for': id,
12948                         cls: 'control-label box-input-label',
12949                         //cls : 'input-group-addon',
12950                         html : this.fieldLabel
12951                         
12952                     },
12953                     
12954                     inputblock
12955                     
12956                 ];
12957
12958         } else {
12959             
12960                    Roo.log(" no label && no align");
12961                 cfg.cn = [
12962                     
12963                         inputblock
12964                     
12965                 ];
12966                 
12967                 
12968         };
12969         
12970         if(this.boxLabel){
12971             cfg.cn.push({
12972                 tag: 'span',
12973                 'for': id,
12974                 cls: 'box-label',
12975                 html: this.boxLabel
12976             })
12977         }
12978         
12979         return cfg;
12980         
12981     },
12982     
12983     onClick : function()
12984     {   
12985         this.setChecked(true);
12986     },
12987     
12988     setChecked : function(state,suppressEvent)
12989     {
12990         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12991             v.checked = false;
12992         });
12993         
12994         this.checked = state;
12995         
12996         if(suppressEvent !== true){
12997             this.fireEvent('check', this, state);
12998         }
12999         
13000         this.inputEl().dom.value = state ? this.value : this.valueOff;
13001         
13002     },
13003     
13004     getGroupValue : function()
13005     {
13006         if(typeof(this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true)) == 'undefined'){
13007             return '';
13008         }
13009         
13010         return this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true).value;
13011     },
13012     
13013     /**
13014      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13015      * @return {Mixed} value The field value
13016      */
13017     getValue : function(){
13018         return this.getGroupValue();
13019     }
13020     
13021 });
13022
13023  
13024 /*
13025  * - LGPL
13026  *
13027  * HtmlEditor
13028  * 
13029  */
13030
13031 /**
13032  * @class Roo.bootstrap.HtmlEditor
13033  * @extends Roo.bootstrap.Component
13034  * Bootstrap HtmlEditor class
13035
13036  * @constructor
13037  * Create a new HtmlEditor
13038  * @param {Object} config The config object
13039  */
13040
13041 Roo.bootstrap.HtmlEditor = function(config){
13042     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
13043     if (!this.toolbars) {
13044         this.toolbars = [];
13045     }
13046     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
13047     this.addEvents({
13048             /**
13049              * @event initialize
13050              * Fires when the editor is fully initialized (including the iframe)
13051              * @param {HtmlEditor} this
13052              */
13053             initialize: true,
13054             /**
13055              * @event activate
13056              * Fires when the editor is first receives the focus. Any insertion must wait
13057              * until after this event.
13058              * @param {HtmlEditor} this
13059              */
13060             activate: true,
13061              /**
13062              * @event beforesync
13063              * Fires before the textarea is updated with content from the editor iframe. Return false
13064              * to cancel the sync.
13065              * @param {HtmlEditor} this
13066              * @param {String} html
13067              */
13068             beforesync: true,
13069              /**
13070              * @event beforepush
13071              * Fires before the iframe editor is updated with content from the textarea. Return false
13072              * to cancel the push.
13073              * @param {HtmlEditor} this
13074              * @param {String} html
13075              */
13076             beforepush: true,
13077              /**
13078              * @event sync
13079              * Fires when the textarea is updated with content from the editor iframe.
13080              * @param {HtmlEditor} this
13081              * @param {String} html
13082              */
13083             sync: true,
13084              /**
13085              * @event push
13086              * Fires when the iframe editor is updated with content from the textarea.
13087              * @param {HtmlEditor} this
13088              * @param {String} html
13089              */
13090             push: true,
13091              /**
13092              * @event editmodechange
13093              * Fires when the editor switches edit modes
13094              * @param {HtmlEditor} this
13095              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
13096              */
13097             editmodechange: true,
13098             /**
13099              * @event editorevent
13100              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13101              * @param {HtmlEditor} this
13102              */
13103             editorevent: true,
13104             /**
13105              * @event firstfocus
13106              * Fires when on first focus - needed by toolbars..
13107              * @param {HtmlEditor} this
13108              */
13109             firstfocus: true,
13110             /**
13111              * @event autosave
13112              * Auto save the htmlEditor value as a file into Events
13113              * @param {HtmlEditor} this
13114              */
13115             autosave: true,
13116             /**
13117              * @event savedpreview
13118              * preview the saved version of htmlEditor
13119              * @param {HtmlEditor} this
13120              */
13121             savedpreview: true
13122         });
13123 };
13124
13125
13126 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
13127     
13128     
13129       /**
13130      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
13131      */
13132     toolbars : false,
13133    
13134      /**
13135      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13136      *                        Roo.resizable.
13137      */
13138     resizable : false,
13139      /**
13140      * @cfg {Number} height (in pixels)
13141      */   
13142     height: 300,
13143    /**
13144      * @cfg {Number} width (in pixels)
13145      */   
13146     width: 500,
13147     
13148     /**
13149      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13150      * 
13151      */
13152     stylesheets: false,
13153     
13154     // id of frame..
13155     frameId: false,
13156     
13157     // private properties
13158     validationEvent : false,
13159     deferHeight: true,
13160     initialized : false,
13161     activated : false,
13162     
13163     onFocus : Roo.emptyFn,
13164     iframePad:3,
13165     hideMode:'offsets',
13166     
13167     
13168     tbContainer : false,
13169     
13170     toolbarContainer :function() {
13171         return this.wrap.select('.x-html-editor-tb',true).first();
13172     },
13173
13174     /**
13175      * Protected method that will not generally be called directly. It
13176      * is called when the editor creates its toolbar. Override this method if you need to
13177      * add custom toolbar buttons.
13178      * @param {HtmlEditor} editor
13179      */
13180     createToolbar : function(){
13181         
13182         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
13183         this.toolbars[0].render(this.toolbarContainer());
13184          
13185         Roo.log("create toolbars");
13186         return;
13187         if (!editor.toolbars || !editor.toolbars.length) {
13188             editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
13189         }
13190         
13191         for (var i =0 ; i < editor.toolbars.length;i++) {
13192             editor.toolbars[i] = Roo.factory(
13193                     typeof(editor.toolbars[i]) == 'string' ?
13194                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
13195                 Roo.bootstrap.HtmlEditor);
13196             editor.toolbars[i].init(editor);
13197         }
13198          
13199         
13200     },
13201
13202      
13203     // private
13204     onRender : function(ct, position)
13205     {
13206        // Roo.log("Call onRender: " + this.xtype);
13207         
13208         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
13209       
13210         this.wrap = this.inputEl().wrap({
13211             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
13212         });
13213         
13214         this.editorcore.onRender(ct, position);
13215          
13216         if (this.resizable) {
13217             this.resizeEl = new Roo.Resizable(this.wrap, {
13218                 pinned : true,
13219                 wrap: true,
13220                 dynamic : true,
13221                 minHeight : this.height,
13222                 height: this.height,
13223                 handles : this.resizable,
13224                 width: this.width,
13225                 listeners : {
13226                     resize : function(r, w, h) {
13227                         _t.onResize(w,h); // -something
13228                     }
13229                 }
13230             });
13231             
13232         }
13233         this.createToolbar(this);
13234        
13235         
13236         if(!this.width){
13237             this.setSize(this.wrap.getSize());
13238         }
13239         if (this.resizeEl) {
13240             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
13241             // should trigger onReize..
13242         }
13243         
13244 //        if(this.autosave && this.w){
13245 //            this.autoSaveFn = setInterval(this.autosave, 1000);
13246 //        }
13247     },
13248
13249     // private
13250     onResize : function(w, h)
13251     {
13252         Roo.log('resize: ' +w + ',' + h );
13253         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
13254         var ew = false;
13255         var eh = false;
13256         
13257         if(this.inputEl() ){
13258             if(typeof w == 'number'){
13259                 var aw = w - this.wrap.getFrameWidth('lr');
13260                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
13261                 ew = aw;
13262             }
13263             if(typeof h == 'number'){
13264                  var tbh = -11;  // fixme it needs to tool bar size!
13265                 for (var i =0; i < this.toolbars.length;i++) {
13266                     // fixme - ask toolbars for heights?
13267                     tbh += this.toolbars[i].el.getHeight();
13268                     //if (this.toolbars[i].footer) {
13269                     //    tbh += this.toolbars[i].footer.el.getHeight();
13270                     //}
13271                 }
13272               
13273                 
13274                 
13275                 
13276                 
13277                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
13278                 ah -= 5; // knock a few pixes off for look..
13279                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
13280                 var eh = ah;
13281             }
13282         }
13283         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
13284         this.editorcore.onResize(ew,eh);
13285         
13286     },
13287
13288     /**
13289      * Toggles the editor between standard and source edit mode.
13290      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13291      */
13292     toggleSourceEdit : function(sourceEditMode)
13293     {
13294         this.editorcore.toggleSourceEdit(sourceEditMode);
13295         
13296         if(this.editorcore.sourceEditMode){
13297             Roo.log('editor - showing textarea');
13298             
13299 //            Roo.log('in');
13300 //            Roo.log(this.syncValue());
13301             this.editorcore.syncValue();
13302             this.inputEl().removeClass('hide');
13303             this.inputEl().dom.removeAttribute('tabIndex');
13304             this.inputEl().focus();
13305         }else{
13306             Roo.log('editor - hiding textarea');
13307 //            Roo.log('out')
13308 //            Roo.log(this.pushValue()); 
13309             this.editorcore.pushValue();
13310             
13311             this.inputEl().addClass('hide');
13312             this.inputEl().dom.setAttribute('tabIndex', -1);
13313             //this.deferFocus();
13314         }
13315          
13316         this.setSize(this.wrap.getSize());
13317         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
13318     },
13319  
13320     // private (for BoxComponent)
13321     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13322
13323     // private (for BoxComponent)
13324     getResizeEl : function(){
13325         return this.wrap;
13326     },
13327
13328     // private (for BoxComponent)
13329     getPositionEl : function(){
13330         return this.wrap;
13331     },
13332
13333     // private
13334     initEvents : function(){
13335         this.originalValue = this.getValue();
13336     },
13337
13338     /**
13339      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
13340      * @method
13341      */
13342     markInvalid : Roo.emptyFn,
13343     /**
13344      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
13345      * @method
13346      */
13347     clearInvalid : Roo.emptyFn,
13348
13349     setValue : function(v){
13350         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
13351         this.editorcore.pushValue();
13352     },
13353
13354      
13355     // private
13356     deferFocus : function(){
13357         this.focus.defer(10, this);
13358     },
13359
13360     // doc'ed in Field
13361     focus : function(){
13362         this.editorcore.focus();
13363         
13364     },
13365       
13366
13367     // private
13368     onDestroy : function(){
13369         
13370         
13371         
13372         if(this.rendered){
13373             
13374             for (var i =0; i < this.toolbars.length;i++) {
13375                 // fixme - ask toolbars for heights?
13376                 this.toolbars[i].onDestroy();
13377             }
13378             
13379             this.wrap.dom.innerHTML = '';
13380             this.wrap.remove();
13381         }
13382     },
13383
13384     // private
13385     onFirstFocus : function(){
13386         //Roo.log("onFirstFocus");
13387         this.editorcore.onFirstFocus();
13388          for (var i =0; i < this.toolbars.length;i++) {
13389             this.toolbars[i].onFirstFocus();
13390         }
13391         
13392     },
13393     
13394     // private
13395     syncValue : function()
13396     {
13397         this.editorcore.syncValue();
13398     }
13399      
13400     
13401     // hide stuff that is not compatible
13402     /**
13403      * @event blur
13404      * @hide
13405      */
13406     /**
13407      * @event change
13408      * @hide
13409      */
13410     /**
13411      * @event focus
13412      * @hide
13413      */
13414     /**
13415      * @event specialkey
13416      * @hide
13417      */
13418     /**
13419      * @cfg {String} fieldClass @hide
13420      */
13421     /**
13422      * @cfg {String} focusClass @hide
13423      */
13424     /**
13425      * @cfg {String} autoCreate @hide
13426      */
13427     /**
13428      * @cfg {String} inputType @hide
13429      */
13430     /**
13431      * @cfg {String} invalidClass @hide
13432      */
13433     /**
13434      * @cfg {String} invalidText @hide
13435      */
13436     /**
13437      * @cfg {String} msgFx @hide
13438      */
13439     /**
13440      * @cfg {String} validateOnBlur @hide
13441      */
13442 });
13443  
13444     
13445    
13446    
13447    
13448     
13449 /**
13450  * @class Roo.bootstrap.Table.AbstractSelectionModel
13451  * @extends Roo.util.Observable
13452  * Abstract base class for grid SelectionModels.  It provides the interface that should be
13453  * implemented by descendant classes.  This class should not be directly instantiated.
13454  * @constructor
13455  */
13456 Roo.bootstrap.Table.AbstractSelectionModel = function(){
13457     this.locked = false;
13458     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
13459 };
13460
13461
13462 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
13463     /** @ignore Called by the grid automatically. Do not call directly. */
13464     init : function(grid){
13465         this.grid = grid;
13466         this.initEvents();
13467     },
13468
13469     /**
13470      * Locks the selections.
13471      */
13472     lock : function(){
13473         this.locked = true;
13474     },
13475
13476     /**
13477      * Unlocks the selections.
13478      */
13479     unlock : function(){
13480         this.locked = false;
13481     },
13482
13483     /**
13484      * Returns true if the selections are locked.
13485      * @return {Boolean}
13486      */
13487     isLocked : function(){
13488         return this.locked;
13489     }
13490 });
13491 /**
13492  * @class Roo.bootstrap.Table.ColumnModel
13493  * @extends Roo.util.Observable
13494  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
13495  * the columns in the table.
13496  
13497  * @constructor
13498  * @param {Object} config An Array of column config objects. See this class's
13499  * config objects for details.
13500 */
13501 Roo.bootstrap.Table.ColumnModel = function(config){
13502         /**
13503      * The config passed into the constructor
13504      */
13505     this.config = config;
13506     this.lookup = {};
13507
13508     // if no id, create one
13509     // if the column does not have a dataIndex mapping,
13510     // map it to the order it is in the config
13511     for(var i = 0, len = config.length; i < len; i++){
13512         var c = config[i];
13513         if(typeof c.dataIndex == "undefined"){
13514             c.dataIndex = i;
13515         }
13516         if(typeof c.renderer == "string"){
13517             c.renderer = Roo.util.Format[c.renderer];
13518         }
13519         if(typeof c.id == "undefined"){
13520             c.id = Roo.id();
13521         }
13522 //        if(c.editor && c.editor.xtype){
13523 //            c.editor  = Roo.factory(c.editor, Roo.grid);
13524 //        }
13525 //        if(c.editor && c.editor.isFormField){
13526 //            c.editor = new Roo.grid.GridEditor(c.editor);
13527 //        }
13528
13529         this.lookup[c.id] = c;
13530     }
13531
13532     /**
13533      * The width of columns which have no width specified (defaults to 100)
13534      * @type Number
13535      */
13536     this.defaultWidth = 100;
13537
13538     /**
13539      * Default sortable of columns which have no sortable specified (defaults to false)
13540      * @type Boolean
13541      */
13542     this.defaultSortable = false;
13543
13544     this.addEvents({
13545         /**
13546              * @event widthchange
13547              * Fires when the width of a column changes.
13548              * @param {ColumnModel} this
13549              * @param {Number} columnIndex The column index
13550              * @param {Number} newWidth The new width
13551              */
13552             "widthchange": true,
13553         /**
13554              * @event headerchange
13555              * Fires when the text of a header changes.
13556              * @param {ColumnModel} this
13557              * @param {Number} columnIndex The column index
13558              * @param {Number} newText The new header text
13559              */
13560             "headerchange": true,
13561         /**
13562              * @event hiddenchange
13563              * Fires when a column is hidden or "unhidden".
13564              * @param {ColumnModel} this
13565              * @param {Number} columnIndex The column index
13566              * @param {Boolean} hidden true if hidden, false otherwise
13567              */
13568             "hiddenchange": true,
13569             /**
13570          * @event columnmoved
13571          * Fires when a column is moved.
13572          * @param {ColumnModel} this
13573          * @param {Number} oldIndex
13574          * @param {Number} newIndex
13575          */
13576         "columnmoved" : true,
13577         /**
13578          * @event columlockchange
13579          * Fires when a column's locked state is changed
13580          * @param {ColumnModel} this
13581          * @param {Number} colIndex
13582          * @param {Boolean} locked true if locked
13583          */
13584         "columnlockchange" : true
13585     });
13586     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
13587 };
13588 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
13589     /**
13590      * @cfg {String} header The header text to display in the Grid view.
13591      */
13592     /**
13593      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
13594      * {@link Roo.data.Record} definition from which to draw the column's value. If not
13595      * specified, the column's index is used as an index into the Record's data Array.
13596      */
13597     /**
13598      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
13599      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
13600      */
13601     /**
13602      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
13603      * Defaults to the value of the {@link #defaultSortable} property.
13604      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
13605      */
13606     /**
13607      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
13608      */
13609     /**
13610      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
13611      */
13612     /**
13613      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
13614      */
13615     /**
13616      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
13617      */
13618     /**
13619      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
13620      * given the cell's data value. See {@link #setRenderer}. If not specified, the
13621      * default renderer uses the raw data value.
13622      */
13623     /**
13624      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
13625      */
13626
13627     /**
13628      * Returns the id of the column at the specified index.
13629      * @param {Number} index The column index
13630      * @return {String} the id
13631      */
13632     getColumnId : function(index){
13633         return this.config[index].id;
13634     },
13635
13636     /**
13637      * Returns the column for a specified id.
13638      * @param {String} id The column id
13639      * @return {Object} the column
13640      */
13641     getColumnById : function(id){
13642         return this.lookup[id];
13643     },
13644
13645     
13646     /**
13647      * Returns the column for a specified dataIndex.
13648      * @param {String} dataIndex The column dataIndex
13649      * @return {Object|Boolean} the column or false if not found
13650      */
13651     getColumnByDataIndex: function(dataIndex){
13652         var index = this.findColumnIndex(dataIndex);
13653         return index > -1 ? this.config[index] : false;
13654     },
13655     
13656     /**
13657      * Returns the index for a specified column id.
13658      * @param {String} id The column id
13659      * @return {Number} the index, or -1 if not found
13660      */
13661     getIndexById : function(id){
13662         for(var i = 0, len = this.config.length; i < len; i++){
13663             if(this.config[i].id == id){
13664                 return i;
13665             }
13666         }
13667         return -1;
13668     },
13669     
13670     /**
13671      * Returns the index for a specified column dataIndex.
13672      * @param {String} dataIndex The column dataIndex
13673      * @return {Number} the index, or -1 if not found
13674      */
13675     
13676     findColumnIndex : function(dataIndex){
13677         for(var i = 0, len = this.config.length; i < len; i++){
13678             if(this.config[i].dataIndex == dataIndex){
13679                 return i;
13680             }
13681         }
13682         return -1;
13683     },
13684     
13685     
13686     moveColumn : function(oldIndex, newIndex){
13687         var c = this.config[oldIndex];
13688         this.config.splice(oldIndex, 1);
13689         this.config.splice(newIndex, 0, c);
13690         this.dataMap = null;
13691         this.fireEvent("columnmoved", this, oldIndex, newIndex);
13692     },
13693
13694     isLocked : function(colIndex){
13695         return this.config[colIndex].locked === true;
13696     },
13697
13698     setLocked : function(colIndex, value, suppressEvent){
13699         if(this.isLocked(colIndex) == value){
13700             return;
13701         }
13702         this.config[colIndex].locked = value;
13703         if(!suppressEvent){
13704             this.fireEvent("columnlockchange", this, colIndex, value);
13705         }
13706     },
13707
13708     getTotalLockedWidth : function(){
13709         var totalWidth = 0;
13710         for(var i = 0; i < this.config.length; i++){
13711             if(this.isLocked(i) && !this.isHidden(i)){
13712                 this.totalWidth += this.getColumnWidth(i);
13713             }
13714         }
13715         return totalWidth;
13716     },
13717
13718     getLockedCount : function(){
13719         for(var i = 0, len = this.config.length; i < len; i++){
13720             if(!this.isLocked(i)){
13721                 return i;
13722             }
13723         }
13724     },
13725
13726     /**
13727      * Returns the number of columns.
13728      * @return {Number}
13729      */
13730     getColumnCount : function(visibleOnly){
13731         if(visibleOnly === true){
13732             var c = 0;
13733             for(var i = 0, len = this.config.length; i < len; i++){
13734                 if(!this.isHidden(i)){
13735                     c++;
13736                 }
13737             }
13738             return c;
13739         }
13740         return this.config.length;
13741     },
13742
13743     /**
13744      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
13745      * @param {Function} fn
13746      * @param {Object} scope (optional)
13747      * @return {Array} result
13748      */
13749     getColumnsBy : function(fn, scope){
13750         var r = [];
13751         for(var i = 0, len = this.config.length; i < len; i++){
13752             var c = this.config[i];
13753             if(fn.call(scope||this, c, i) === true){
13754                 r[r.length] = c;
13755             }
13756         }
13757         return r;
13758     },
13759
13760     /**
13761      * Returns true if the specified column is sortable.
13762      * @param {Number} col The column index
13763      * @return {Boolean}
13764      */
13765     isSortable : function(col){
13766         if(typeof this.config[col].sortable == "undefined"){
13767             return this.defaultSortable;
13768         }
13769         return this.config[col].sortable;
13770     },
13771
13772     /**
13773      * Returns the rendering (formatting) function defined for the column.
13774      * @param {Number} col The column index.
13775      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
13776      */
13777     getRenderer : function(col){
13778         if(!this.config[col].renderer){
13779             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
13780         }
13781         return this.config[col].renderer;
13782     },
13783
13784     /**
13785      * Sets the rendering (formatting) function for a column.
13786      * @param {Number} col The column index
13787      * @param {Function} fn The function to use to process the cell's raw data
13788      * to return HTML markup for the grid view. The render function is called with
13789      * the following parameters:<ul>
13790      * <li>Data value.</li>
13791      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
13792      * <li>css A CSS style string to apply to the table cell.</li>
13793      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
13794      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
13795      * <li>Row index</li>
13796      * <li>Column index</li>
13797      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
13798      */
13799     setRenderer : function(col, fn){
13800         this.config[col].renderer = fn;
13801     },
13802
13803     /**
13804      * Returns the width for the specified column.
13805      * @param {Number} col The column index
13806      * @return {Number}
13807      */
13808     getColumnWidth : function(col){
13809         return this.config[col].width * 1 || this.defaultWidth;
13810     },
13811
13812     /**
13813      * Sets the width for a column.
13814      * @param {Number} col The column index
13815      * @param {Number} width The new width
13816      */
13817     setColumnWidth : function(col, width, suppressEvent){
13818         this.config[col].width = width;
13819         this.totalWidth = null;
13820         if(!suppressEvent){
13821              this.fireEvent("widthchange", this, col, width);
13822         }
13823     },
13824
13825     /**
13826      * Returns the total width of all columns.
13827      * @param {Boolean} includeHidden True to include hidden column widths
13828      * @return {Number}
13829      */
13830     getTotalWidth : function(includeHidden){
13831         if(!this.totalWidth){
13832             this.totalWidth = 0;
13833             for(var i = 0, len = this.config.length; i < len; i++){
13834                 if(includeHidden || !this.isHidden(i)){
13835                     this.totalWidth += this.getColumnWidth(i);
13836                 }
13837             }
13838         }
13839         return this.totalWidth;
13840     },
13841
13842     /**
13843      * Returns the header for the specified column.
13844      * @param {Number} col The column index
13845      * @return {String}
13846      */
13847     getColumnHeader : function(col){
13848         return this.config[col].header;
13849     },
13850
13851     /**
13852      * Sets the header for a column.
13853      * @param {Number} col The column index
13854      * @param {String} header The new header
13855      */
13856     setColumnHeader : function(col, header){
13857         this.config[col].header = header;
13858         this.fireEvent("headerchange", this, col, header);
13859     },
13860
13861     /**
13862      * Returns the tooltip for the specified column.
13863      * @param {Number} col The column index
13864      * @return {String}
13865      */
13866     getColumnTooltip : function(col){
13867             return this.config[col].tooltip;
13868     },
13869     /**
13870      * Sets the tooltip for a column.
13871      * @param {Number} col The column index
13872      * @param {String} tooltip The new tooltip
13873      */
13874     setColumnTooltip : function(col, tooltip){
13875             this.config[col].tooltip = tooltip;
13876     },
13877
13878     /**
13879      * Returns the dataIndex for the specified column.
13880      * @param {Number} col The column index
13881      * @return {Number}
13882      */
13883     getDataIndex : function(col){
13884         return this.config[col].dataIndex;
13885     },
13886
13887     /**
13888      * Sets the dataIndex for a column.
13889      * @param {Number} col The column index
13890      * @param {Number} dataIndex The new dataIndex
13891      */
13892     setDataIndex : function(col, dataIndex){
13893         this.config[col].dataIndex = dataIndex;
13894     },
13895
13896     
13897     
13898     /**
13899      * Returns true if the cell is editable.
13900      * @param {Number} colIndex The column index
13901      * @param {Number} rowIndex The row index
13902      * @return {Boolean}
13903      */
13904     isCellEditable : function(colIndex, rowIndex){
13905         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
13906     },
13907
13908     /**
13909      * Returns the editor defined for the cell/column.
13910      * return false or null to disable editing.
13911      * @param {Number} colIndex The column index
13912      * @param {Number} rowIndex The row index
13913      * @return {Object}
13914      */
13915     getCellEditor : function(colIndex, rowIndex){
13916         return this.config[colIndex].editor;
13917     },
13918
13919     /**
13920      * Sets if a column is editable.
13921      * @param {Number} col The column index
13922      * @param {Boolean} editable True if the column is editable
13923      */
13924     setEditable : function(col, editable){
13925         this.config[col].editable = editable;
13926     },
13927
13928
13929     /**
13930      * Returns true if the column is hidden.
13931      * @param {Number} colIndex The column index
13932      * @return {Boolean}
13933      */
13934     isHidden : function(colIndex){
13935         return this.config[colIndex].hidden;
13936     },
13937
13938
13939     /**
13940      * Returns true if the column width cannot be changed
13941      */
13942     isFixed : function(colIndex){
13943         return this.config[colIndex].fixed;
13944     },
13945
13946     /**
13947      * Returns true if the column can be resized
13948      * @return {Boolean}
13949      */
13950     isResizable : function(colIndex){
13951         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
13952     },
13953     /**
13954      * Sets if a column is hidden.
13955      * @param {Number} colIndex The column index
13956      * @param {Boolean} hidden True if the column is hidden
13957      */
13958     setHidden : function(colIndex, hidden){
13959         this.config[colIndex].hidden = hidden;
13960         this.totalWidth = null;
13961         this.fireEvent("hiddenchange", this, colIndex, hidden);
13962     },
13963
13964     /**
13965      * Sets the editor for a column.
13966      * @param {Number} col The column index
13967      * @param {Object} editor The editor object
13968      */
13969     setEditor : function(col, editor){
13970         this.config[col].editor = editor;
13971     }
13972 });
13973
13974 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
13975         if(typeof value == "string" && value.length < 1){
13976             return "&#160;";
13977         }
13978         return value;
13979 };
13980
13981 // Alias for backwards compatibility
13982 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
13983
13984 /**
13985  * @extends Roo.bootstrap.Table.AbstractSelectionModel
13986  * @class Roo.bootstrap.Table.RowSelectionModel
13987  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
13988  * It supports multiple selections and keyboard selection/navigation. 
13989  * @constructor
13990  * @param {Object} config
13991  */
13992
13993 Roo.bootstrap.Table.RowSelectionModel = function(config){
13994     Roo.apply(this, config);
13995     this.selections = new Roo.util.MixedCollection(false, function(o){
13996         return o.id;
13997     });
13998
13999     this.last = false;
14000     this.lastActive = false;
14001
14002     this.addEvents({
14003         /**
14004              * @event selectionchange
14005              * Fires when the selection changes
14006              * @param {SelectionModel} this
14007              */
14008             "selectionchange" : true,
14009         /**
14010              * @event afterselectionchange
14011              * Fires after the selection changes (eg. by key press or clicking)
14012              * @param {SelectionModel} this
14013              */
14014             "afterselectionchange" : true,
14015         /**
14016              * @event beforerowselect
14017              * Fires when a row is selected being selected, return false to cancel.
14018              * @param {SelectionModel} this
14019              * @param {Number} rowIndex The selected index
14020              * @param {Boolean} keepExisting False if other selections will be cleared
14021              */
14022             "beforerowselect" : true,
14023         /**
14024              * @event rowselect
14025              * Fires when a row is selected.
14026              * @param {SelectionModel} this
14027              * @param {Number} rowIndex The selected index
14028              * @param {Roo.data.Record} r The record
14029              */
14030             "rowselect" : true,
14031         /**
14032              * @event rowdeselect
14033              * Fires when a row is deselected.
14034              * @param {SelectionModel} this
14035              * @param {Number} rowIndex The selected index
14036              */
14037         "rowdeselect" : true
14038     });
14039     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
14040     this.locked = false;
14041 };
14042
14043 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
14044     /**
14045      * @cfg {Boolean} singleSelect
14046      * True to allow selection of only one row at a time (defaults to false)
14047      */
14048     singleSelect : false,
14049
14050     // private
14051     initEvents : function(){
14052
14053         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
14054             this.grid.on("mousedown", this.handleMouseDown, this);
14055         }else{ // allow click to work like normal
14056             this.grid.on("rowclick", this.handleDragableRowClick, this);
14057         }
14058
14059         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
14060             "up" : function(e){
14061                 if(!e.shiftKey){
14062                     this.selectPrevious(e.shiftKey);
14063                 }else if(this.last !== false && this.lastActive !== false){
14064                     var last = this.last;
14065                     this.selectRange(this.last,  this.lastActive-1);
14066                     this.grid.getView().focusRow(this.lastActive);
14067                     if(last !== false){
14068                         this.last = last;
14069                     }
14070                 }else{
14071                     this.selectFirstRow();
14072                 }
14073                 this.fireEvent("afterselectionchange", this);
14074             },
14075             "down" : function(e){
14076                 if(!e.shiftKey){
14077                     this.selectNext(e.shiftKey);
14078                 }else if(this.last !== false && this.lastActive !== false){
14079                     var last = this.last;
14080                     this.selectRange(this.last,  this.lastActive+1);
14081                     this.grid.getView().focusRow(this.lastActive);
14082                     if(last !== false){
14083                         this.last = last;
14084                     }
14085                 }else{
14086                     this.selectFirstRow();
14087                 }
14088                 this.fireEvent("afterselectionchange", this);
14089             },
14090             scope: this
14091         });
14092
14093         var view = this.grid.view;
14094         view.on("refresh", this.onRefresh, this);
14095         view.on("rowupdated", this.onRowUpdated, this);
14096         view.on("rowremoved", this.onRemove, this);
14097     },
14098
14099     // private
14100     onRefresh : function(){
14101         var ds = this.grid.dataSource, i, v = this.grid.view;
14102         var s = this.selections;
14103         s.each(function(r){
14104             if((i = ds.indexOfId(r.id)) != -1){
14105                 v.onRowSelect(i);
14106             }else{
14107                 s.remove(r);
14108             }
14109         });
14110     },
14111
14112     // private
14113     onRemove : function(v, index, r){
14114         this.selections.remove(r);
14115     },
14116
14117     // private
14118     onRowUpdated : function(v, index, r){
14119         if(this.isSelected(r)){
14120             v.onRowSelect(index);
14121         }
14122     },
14123
14124     /**
14125      * Select records.
14126      * @param {Array} records The records to select
14127      * @param {Boolean} keepExisting (optional) True to keep existing selections
14128      */
14129     selectRecords : function(records, keepExisting){
14130         if(!keepExisting){
14131             this.clearSelections();
14132         }
14133         var ds = this.grid.dataSource;
14134         for(var i = 0, len = records.length; i < len; i++){
14135             this.selectRow(ds.indexOf(records[i]), true);
14136         }
14137     },
14138
14139     /**
14140      * Gets the number of selected rows.
14141      * @return {Number}
14142      */
14143     getCount : function(){
14144         return this.selections.length;
14145     },
14146
14147     /**
14148      * Selects the first row in the grid.
14149      */
14150     selectFirstRow : function(){
14151         this.selectRow(0);
14152     },
14153
14154     /**
14155      * Select the last row.
14156      * @param {Boolean} keepExisting (optional) True to keep existing selections
14157      */
14158     selectLastRow : function(keepExisting){
14159         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
14160     },
14161
14162     /**
14163      * Selects the row immediately following the last selected row.
14164      * @param {Boolean} keepExisting (optional) True to keep existing selections
14165      */
14166     selectNext : function(keepExisting){
14167         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
14168             this.selectRow(this.last+1, keepExisting);
14169             this.grid.getView().focusRow(this.last);
14170         }
14171     },
14172
14173     /**
14174      * Selects the row that precedes the last selected row.
14175      * @param {Boolean} keepExisting (optional) True to keep existing selections
14176      */
14177     selectPrevious : function(keepExisting){
14178         if(this.last){
14179             this.selectRow(this.last-1, keepExisting);
14180             this.grid.getView().focusRow(this.last);
14181         }
14182     },
14183
14184     /**
14185      * Returns the selected records
14186      * @return {Array} Array of selected records
14187      */
14188     getSelections : function(){
14189         return [].concat(this.selections.items);
14190     },
14191
14192     /**
14193      * Returns the first selected record.
14194      * @return {Record}
14195      */
14196     getSelected : function(){
14197         return this.selections.itemAt(0);
14198     },
14199
14200
14201     /**
14202      * Clears all selections.
14203      */
14204     clearSelections : function(fast){
14205         if(this.locked) return;
14206         if(fast !== true){
14207             var ds = this.grid.dataSource;
14208             var s = this.selections;
14209             s.each(function(r){
14210                 this.deselectRow(ds.indexOfId(r.id));
14211             }, this);
14212             s.clear();
14213         }else{
14214             this.selections.clear();
14215         }
14216         this.last = false;
14217     },
14218
14219
14220     /**
14221      * Selects all rows.
14222      */
14223     selectAll : function(){
14224         if(this.locked) return;
14225         this.selections.clear();
14226         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
14227             this.selectRow(i, true);
14228         }
14229     },
14230
14231     /**
14232      * Returns True if there is a selection.
14233      * @return {Boolean}
14234      */
14235     hasSelection : function(){
14236         return this.selections.length > 0;
14237     },
14238
14239     /**
14240      * Returns True if the specified row is selected.
14241      * @param {Number/Record} record The record or index of the record to check
14242      * @return {Boolean}
14243      */
14244     isSelected : function(index){
14245         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
14246         return (r && this.selections.key(r.id) ? true : false);
14247     },
14248
14249     /**
14250      * Returns True if the specified record id is selected.
14251      * @param {String} id The id of record to check
14252      * @return {Boolean}
14253      */
14254     isIdSelected : function(id){
14255         return (this.selections.key(id) ? true : false);
14256     },
14257
14258     // private
14259     handleMouseDown : function(e, t){
14260         var view = this.grid.getView(), rowIndex;
14261         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
14262             return;
14263         };
14264         if(e.shiftKey && this.last !== false){
14265             var last = this.last;
14266             this.selectRange(last, rowIndex, e.ctrlKey);
14267             this.last = last; // reset the last
14268             view.focusRow(rowIndex);
14269         }else{
14270             var isSelected = this.isSelected(rowIndex);
14271             if(e.button !== 0 && isSelected){
14272                 view.focusRow(rowIndex);
14273             }else if(e.ctrlKey && isSelected){
14274                 this.deselectRow(rowIndex);
14275             }else if(!isSelected){
14276                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
14277                 view.focusRow(rowIndex);
14278             }
14279         }
14280         this.fireEvent("afterselectionchange", this);
14281     },
14282     // private
14283     handleDragableRowClick :  function(grid, rowIndex, e) 
14284     {
14285         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
14286             this.selectRow(rowIndex, false);
14287             grid.view.focusRow(rowIndex);
14288              this.fireEvent("afterselectionchange", this);
14289         }
14290     },
14291     
14292     /**
14293      * Selects multiple rows.
14294      * @param {Array} rows Array of the indexes of the row to select
14295      * @param {Boolean} keepExisting (optional) True to keep existing selections
14296      */
14297     selectRows : function(rows, keepExisting){
14298         if(!keepExisting){
14299             this.clearSelections();
14300         }
14301         for(var i = 0, len = rows.length; i < len; i++){
14302             this.selectRow(rows[i], true);
14303         }
14304     },
14305
14306     /**
14307      * Selects a range of rows. All rows in between startRow and endRow are also selected.
14308      * @param {Number} startRow The index of the first row in the range
14309      * @param {Number} endRow The index of the last row in the range
14310      * @param {Boolean} keepExisting (optional) True to retain existing selections
14311      */
14312     selectRange : function(startRow, endRow, keepExisting){
14313         if(this.locked) return;
14314         if(!keepExisting){
14315             this.clearSelections();
14316         }
14317         if(startRow <= endRow){
14318             for(var i = startRow; i <= endRow; i++){
14319                 this.selectRow(i, true);
14320             }
14321         }else{
14322             for(var i = startRow; i >= endRow; i--){
14323                 this.selectRow(i, true);
14324             }
14325         }
14326     },
14327
14328     /**
14329      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
14330      * @param {Number} startRow The index of the first row in the range
14331      * @param {Number} endRow The index of the last row in the range
14332      */
14333     deselectRange : function(startRow, endRow, preventViewNotify){
14334         if(this.locked) return;
14335         for(var i = startRow; i <= endRow; i++){
14336             this.deselectRow(i, preventViewNotify);
14337         }
14338     },
14339
14340     /**
14341      * Selects a row.
14342      * @param {Number} row The index of the row to select
14343      * @param {Boolean} keepExisting (optional) True to keep existing selections
14344      */
14345     selectRow : function(index, keepExisting, preventViewNotify){
14346         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
14347         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
14348             if(!keepExisting || this.singleSelect){
14349                 this.clearSelections();
14350             }
14351             var r = this.grid.dataSource.getAt(index);
14352             this.selections.add(r);
14353             this.last = this.lastActive = index;
14354             if(!preventViewNotify){
14355                 this.grid.getView().onRowSelect(index);
14356             }
14357             this.fireEvent("rowselect", this, index, r);
14358             this.fireEvent("selectionchange", this);
14359         }
14360     },
14361
14362     /**
14363      * Deselects a row.
14364      * @param {Number} row The index of the row to deselect
14365      */
14366     deselectRow : function(index, preventViewNotify){
14367         if(this.locked) return;
14368         if(this.last == index){
14369             this.last = false;
14370         }
14371         if(this.lastActive == index){
14372             this.lastActive = false;
14373         }
14374         var r = this.grid.dataSource.getAt(index);
14375         this.selections.remove(r);
14376         if(!preventViewNotify){
14377             this.grid.getView().onRowDeselect(index);
14378         }
14379         this.fireEvent("rowdeselect", this, index);
14380         this.fireEvent("selectionchange", this);
14381     },
14382
14383     // private
14384     restoreLast : function(){
14385         if(this._last){
14386             this.last = this._last;
14387         }
14388     },
14389
14390     // private
14391     acceptsNav : function(row, col, cm){
14392         return !cm.isHidden(col) && cm.isCellEditable(col, row);
14393     },
14394
14395     // private
14396     onEditorKey : function(field, e){
14397         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
14398         if(k == e.TAB){
14399             e.stopEvent();
14400             ed.completeEdit();
14401             if(e.shiftKey){
14402                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
14403             }else{
14404                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
14405             }
14406         }else if(k == e.ENTER && !e.ctrlKey){
14407             e.stopEvent();
14408             ed.completeEdit();
14409             if(e.shiftKey){
14410                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
14411             }else{
14412                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
14413             }
14414         }else if(k == e.ESC){
14415             ed.cancelEdit();
14416         }
14417         if(newCell){
14418             g.startEditing(newCell[0], newCell[1]);
14419         }
14420     }
14421 });