Roo/bootstrap/DateField.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             
5403             
5404                 Roo.log("left and has label");
5405                 cfg.cn = [
5406                     
5407                     {
5408                         tag: 'label',
5409                         'for' :  id,
5410                         cls : 'col-sm-2 control-label',
5411                         html : this.fieldLabel
5412                         
5413                     },
5414                     {
5415                         cls : "col-sm-10", 
5416                         cn: [
5417                             combobox
5418                         ]
5419                     }
5420                     
5421                 ];
5422         } else if ( this.fieldLabel.length) {
5423                 Roo.log(" label");
5424                  cfg.cn = [
5425                    
5426                     {
5427                         tag: 'label',
5428                         //cls : 'input-group-addon',
5429                         html : this.fieldLabel
5430                         
5431                     },
5432                     
5433                     combobox
5434                     
5435                 ];
5436
5437         } else {
5438             
5439                 Roo.log(" no label && no align");
5440                 cfg = combobox
5441                      
5442                 
5443         }
5444          
5445         var settings=this;
5446         ['xs','sm','md','lg'].map(function(size){
5447             if (settings[size]) {
5448                 cfg.cls += ' col-' + size + '-' + settings[size];
5449             }
5450         });
5451         
5452         return cfg;
5453         
5454     },
5455     
5456     
5457     
5458     // private
5459     onResize : function(w, h){
5460 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5461 //        if(typeof w == 'number'){
5462 //            var x = w - this.trigger.getWidth();
5463 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5464 //            this.trigger.setStyle('left', x+'px');
5465 //        }
5466     },
5467
5468     // private
5469     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5470
5471     // private
5472     getResizeEl : function(){
5473         return this.inputEl();
5474     },
5475
5476     // private
5477     getPositionEl : function(){
5478         return this.inputEl();
5479     },
5480
5481     // private
5482     alignErrorIcon : function(){
5483         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5484     },
5485
5486     // private
5487     initEvents : function(){
5488         
5489         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5490         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5491         if(!this.multiple){
5492             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5493             if(this.hideTrigger){
5494                 this.trigger.setDisplayed(false);
5495             }
5496             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5497         }
5498         
5499         if(this.multiple){
5500             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5501         }
5502         
5503         //this.trigger.addClassOnOver('x-form-trigger-over');
5504         //this.trigger.addClassOnClick('x-form-trigger-click');
5505         
5506         //if(!this.width){
5507         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5508         //}
5509     },
5510
5511     // private
5512     initTrigger : function(){
5513        
5514     },
5515
5516     // private
5517     onDestroy : function(){
5518         if(this.trigger){
5519             this.trigger.removeAllListeners();
5520           //  this.trigger.remove();
5521         }
5522         //if(this.wrap){
5523         //    this.wrap.remove();
5524         //}
5525         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5526     },
5527
5528     // private
5529     onFocus : function(){
5530         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5531         /*
5532         if(!this.mimicing){
5533             this.wrap.addClass('x-trigger-wrap-focus');
5534             this.mimicing = true;
5535             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5536             if(this.monitorTab){
5537                 this.el.on("keydown", this.checkTab, this);
5538             }
5539         }
5540         */
5541     },
5542
5543     // private
5544     checkTab : function(e){
5545         if(e.getKey() == e.TAB){
5546             this.triggerBlur();
5547         }
5548     },
5549
5550     // private
5551     onBlur : function(){
5552         // do nothing
5553     },
5554
5555     // private
5556     mimicBlur : function(e, t){
5557         /*
5558         if(!this.wrap.contains(t) && this.validateBlur()){
5559             this.triggerBlur();
5560         }
5561         */
5562     },
5563
5564     // private
5565     triggerBlur : function(){
5566         this.mimicing = false;
5567         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5568         if(this.monitorTab){
5569             this.el.un("keydown", this.checkTab, this);
5570         }
5571         //this.wrap.removeClass('x-trigger-wrap-focus');
5572         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5573     },
5574
5575     // private
5576     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5577     validateBlur : function(e, t){
5578         return true;
5579     },
5580
5581     // private
5582     onDisable : function(){
5583         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5584         //if(this.wrap){
5585         //    this.wrap.addClass('x-item-disabled');
5586         //}
5587     },
5588
5589     // private
5590     onEnable : function(){
5591         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5592         //if(this.wrap){
5593         //    this.el.removeClass('x-item-disabled');
5594         //}
5595     },
5596
5597     // private
5598     onShow : function(){
5599         var ae = this.getActionEl();
5600         
5601         if(ae){
5602             ae.dom.style.display = '';
5603             ae.dom.style.visibility = 'visible';
5604         }
5605     },
5606
5607     // private
5608     
5609     onHide : function(){
5610         var ae = this.getActionEl();
5611         ae.dom.style.display = 'none';
5612     },
5613
5614     /**
5615      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5616      * by an implementing function.
5617      * @method
5618      * @param {EventObject} e
5619      */
5620     onTriggerClick : Roo.emptyFn
5621 });
5622  /*
5623  * Based on:
5624  * Ext JS Library 1.1.1
5625  * Copyright(c) 2006-2007, Ext JS, LLC.
5626  *
5627  * Originally Released Under LGPL - original licence link has changed is not relivant.
5628  *
5629  * Fork - LGPL
5630  * <script type="text/javascript">
5631  */
5632
5633
5634 /**
5635  * @class Roo.data.SortTypes
5636  * @singleton
5637  * Defines the default sorting (casting?) comparison functions used when sorting data.
5638  */
5639 Roo.data.SortTypes = {
5640     /**
5641      * Default sort that does nothing
5642      * @param {Mixed} s The value being converted
5643      * @return {Mixed} The comparison value
5644      */
5645     none : function(s){
5646         return s;
5647     },
5648     
5649     /**
5650      * The regular expression used to strip tags
5651      * @type {RegExp}
5652      * @property
5653      */
5654     stripTagsRE : /<\/?[^>]+>/gi,
5655     
5656     /**
5657      * Strips all HTML tags to sort on text only
5658      * @param {Mixed} s The value being converted
5659      * @return {String} The comparison value
5660      */
5661     asText : function(s){
5662         return String(s).replace(this.stripTagsRE, "");
5663     },
5664     
5665     /**
5666      * Strips all HTML tags to sort on text only - Case insensitive
5667      * @param {Mixed} s The value being converted
5668      * @return {String} The comparison value
5669      */
5670     asUCText : function(s){
5671         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5672     },
5673     
5674     /**
5675      * Case insensitive string
5676      * @param {Mixed} s The value being converted
5677      * @return {String} The comparison value
5678      */
5679     asUCString : function(s) {
5680         return String(s).toUpperCase();
5681     },
5682     
5683     /**
5684      * Date sorting
5685      * @param {Mixed} s The value being converted
5686      * @return {Number} The comparison value
5687      */
5688     asDate : function(s) {
5689         if(!s){
5690             return 0;
5691         }
5692         if(s instanceof Date){
5693             return s.getTime();
5694         }
5695         return Date.parse(String(s));
5696     },
5697     
5698     /**
5699      * Float sorting
5700      * @param {Mixed} s The value being converted
5701      * @return {Float} The comparison value
5702      */
5703     asFloat : function(s) {
5704         var val = parseFloat(String(s).replace(/,/g, ""));
5705         if(isNaN(val)) val = 0;
5706         return val;
5707     },
5708     
5709     /**
5710      * Integer sorting
5711      * @param {Mixed} s The value being converted
5712      * @return {Number} The comparison value
5713      */
5714     asInt : function(s) {
5715         var val = parseInt(String(s).replace(/,/g, ""));
5716         if(isNaN(val)) val = 0;
5717         return val;
5718     }
5719 };/*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731 * @class Roo.data.Record
5732  * Instances of this class encapsulate both record <em>definition</em> information, and record
5733  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5734  * to access Records cached in an {@link Roo.data.Store} object.<br>
5735  * <p>
5736  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5737  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5738  * objects.<br>
5739  * <p>
5740  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5741  * @constructor
5742  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5743  * {@link #create}. The parameters are the same.
5744  * @param {Array} data An associative Array of data values keyed by the field name.
5745  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5746  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5747  * not specified an integer id is generated.
5748  */
5749 Roo.data.Record = function(data, id){
5750     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5751     this.data = data;
5752 };
5753
5754 /**
5755  * Generate a constructor for a specific record layout.
5756  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5757  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5758  * Each field definition object may contain the following properties: <ul>
5759  * <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,
5760  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5761  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5762  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5763  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5764  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5765  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5766  * this may be omitted.</p></li>
5767  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5768  * <ul><li>auto (Default, implies no conversion)</li>
5769  * <li>string</li>
5770  * <li>int</li>
5771  * <li>float</li>
5772  * <li>boolean</li>
5773  * <li>date</li></ul></p></li>
5774  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5775  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5776  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5777  * by the Reader into an object that will be stored in the Record. It is passed the
5778  * following parameters:<ul>
5779  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5780  * </ul></p></li>
5781  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5782  * </ul>
5783  * <br>usage:<br><pre><code>
5784 var TopicRecord = Roo.data.Record.create(
5785     {name: 'title', mapping: 'topic_title'},
5786     {name: 'author', mapping: 'username'},
5787     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5788     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5789     {name: 'lastPoster', mapping: 'user2'},
5790     {name: 'excerpt', mapping: 'post_text'}
5791 );
5792
5793 var myNewRecord = new TopicRecord({
5794     title: 'Do my job please',
5795     author: 'noobie',
5796     totalPosts: 1,
5797     lastPost: new Date(),
5798     lastPoster: 'Animal',
5799     excerpt: 'No way dude!'
5800 });
5801 myStore.add(myNewRecord);
5802 </code></pre>
5803  * @method create
5804  * @static
5805  */
5806 Roo.data.Record.create = function(o){
5807     var f = function(){
5808         f.superclass.constructor.apply(this, arguments);
5809     };
5810     Roo.extend(f, Roo.data.Record);
5811     var p = f.prototype;
5812     p.fields = new Roo.util.MixedCollection(false, function(field){
5813         return field.name;
5814     });
5815     for(var i = 0, len = o.length; i < len; i++){
5816         p.fields.add(new Roo.data.Field(o[i]));
5817     }
5818     f.getField = function(name){
5819         return p.fields.get(name);  
5820     };
5821     return f;
5822 };
5823
5824 Roo.data.Record.AUTO_ID = 1000;
5825 Roo.data.Record.EDIT = 'edit';
5826 Roo.data.Record.REJECT = 'reject';
5827 Roo.data.Record.COMMIT = 'commit';
5828
5829 Roo.data.Record.prototype = {
5830     /**
5831      * Readonly flag - true if this record has been modified.
5832      * @type Boolean
5833      */
5834     dirty : false,
5835     editing : false,
5836     error: null,
5837     modified: null,
5838
5839     // private
5840     join : function(store){
5841         this.store = store;
5842     },
5843
5844     /**
5845      * Set the named field to the specified value.
5846      * @param {String} name The name of the field to set.
5847      * @param {Object} value The value to set the field to.
5848      */
5849     set : function(name, value){
5850         if(this.data[name] == value){
5851             return;
5852         }
5853         this.dirty = true;
5854         if(!this.modified){
5855             this.modified = {};
5856         }
5857         if(typeof this.modified[name] == 'undefined'){
5858             this.modified[name] = this.data[name];
5859         }
5860         this.data[name] = value;
5861         if(!this.editing && this.store){
5862             this.store.afterEdit(this);
5863         }       
5864     },
5865
5866     /**
5867      * Get the value of the named field.
5868      * @param {String} name The name of the field to get the value of.
5869      * @return {Object} The value of the field.
5870      */
5871     get : function(name){
5872         return this.data[name]; 
5873     },
5874
5875     // private
5876     beginEdit : function(){
5877         this.editing = true;
5878         this.modified = {}; 
5879     },
5880
5881     // private
5882     cancelEdit : function(){
5883         this.editing = false;
5884         delete this.modified;
5885     },
5886
5887     // private
5888     endEdit : function(){
5889         this.editing = false;
5890         if(this.dirty && this.store){
5891             this.store.afterEdit(this);
5892         }
5893     },
5894
5895     /**
5896      * Usually called by the {@link Roo.data.Store} which owns the Record.
5897      * Rejects all changes made to the Record since either creation, or the last commit operation.
5898      * Modified fields are reverted to their original values.
5899      * <p>
5900      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5901      * of reject operations.
5902      */
5903     reject : function(){
5904         var m = this.modified;
5905         for(var n in m){
5906             if(typeof m[n] != "function"){
5907                 this.data[n] = m[n];
5908             }
5909         }
5910         this.dirty = false;
5911         delete this.modified;
5912         this.editing = false;
5913         if(this.store){
5914             this.store.afterReject(this);
5915         }
5916     },
5917
5918     /**
5919      * Usually called by the {@link Roo.data.Store} which owns the Record.
5920      * Commits all changes made to the Record since either creation, or the last commit operation.
5921      * <p>
5922      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5923      * of commit operations.
5924      */
5925     commit : function(){
5926         this.dirty = false;
5927         delete this.modified;
5928         this.editing = false;
5929         if(this.store){
5930             this.store.afterCommit(this);
5931         }
5932     },
5933
5934     // private
5935     hasError : function(){
5936         return this.error != null;
5937     },
5938
5939     // private
5940     clearError : function(){
5941         this.error = null;
5942     },
5943
5944     /**
5945      * Creates a copy of this record.
5946      * @param {String} id (optional) A new record id if you don't want to use this record's id
5947      * @return {Record}
5948      */
5949     copy : function(newId) {
5950         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
5951     }
5952 };/*
5953  * Based on:
5954  * Ext JS Library 1.1.1
5955  * Copyright(c) 2006-2007, Ext JS, LLC.
5956  *
5957  * Originally Released Under LGPL - original licence link has changed is not relivant.
5958  *
5959  * Fork - LGPL
5960  * <script type="text/javascript">
5961  */
5962
5963
5964
5965 /**
5966  * @class Roo.data.Store
5967  * @extends Roo.util.Observable
5968  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
5969  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
5970  * <p>
5971  * 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
5972  * has no knowledge of the format of the data returned by the Proxy.<br>
5973  * <p>
5974  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
5975  * instances from the data object. These records are cached and made available through accessor functions.
5976  * @constructor
5977  * Creates a new Store.
5978  * @param {Object} config A config object containing the objects needed for the Store to access data,
5979  * and read the data into Records.
5980  */
5981 Roo.data.Store = function(config){
5982     this.data = new Roo.util.MixedCollection(false);
5983     this.data.getKey = function(o){
5984         return o.id;
5985     };
5986     this.baseParams = {};
5987     // private
5988     this.paramNames = {
5989         "start" : "start",
5990         "limit" : "limit",
5991         "sort" : "sort",
5992         "dir" : "dir",
5993         "multisort" : "_multisort"
5994     };
5995
5996     if(config && config.data){
5997         this.inlineData = config.data;
5998         delete config.data;
5999     }
6000
6001     Roo.apply(this, config);
6002     
6003     if(this.reader){ // reader passed
6004         this.reader = Roo.factory(this.reader, Roo.data);
6005         this.reader.xmodule = this.xmodule || false;
6006         if(!this.recordType){
6007             this.recordType = this.reader.recordType;
6008         }
6009         if(this.reader.onMetaChange){
6010             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6011         }
6012     }
6013
6014     if(this.recordType){
6015         this.fields = this.recordType.prototype.fields;
6016     }
6017     this.modified = [];
6018
6019     this.addEvents({
6020         /**
6021          * @event datachanged
6022          * Fires when the data cache has changed, and a widget which is using this Store
6023          * as a Record cache should refresh its view.
6024          * @param {Store} this
6025          */
6026         datachanged : true,
6027         /**
6028          * @event metachange
6029          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6030          * @param {Store} this
6031          * @param {Object} meta The JSON metadata
6032          */
6033         metachange : true,
6034         /**
6035          * @event add
6036          * Fires when Records have been added to the Store
6037          * @param {Store} this
6038          * @param {Roo.data.Record[]} records The array of Records added
6039          * @param {Number} index The index at which the record(s) were added
6040          */
6041         add : true,
6042         /**
6043          * @event remove
6044          * Fires when a Record has been removed from the Store
6045          * @param {Store} this
6046          * @param {Roo.data.Record} record The Record that was removed
6047          * @param {Number} index The index at which the record was removed
6048          */
6049         remove : true,
6050         /**
6051          * @event update
6052          * Fires when a Record has been updated
6053          * @param {Store} this
6054          * @param {Roo.data.Record} record The Record that was updated
6055          * @param {String} operation The update operation being performed.  Value may be one of:
6056          * <pre><code>
6057  Roo.data.Record.EDIT
6058  Roo.data.Record.REJECT
6059  Roo.data.Record.COMMIT
6060          * </code></pre>
6061          */
6062         update : true,
6063         /**
6064          * @event clear
6065          * Fires when the data cache has been cleared.
6066          * @param {Store} this
6067          */
6068         clear : true,
6069         /**
6070          * @event beforeload
6071          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6072          * the load action will be canceled.
6073          * @param {Store} this
6074          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6075          */
6076         beforeload : true,
6077         /**
6078          * @event beforeloadadd
6079          * Fires after a new set of Records has been loaded.
6080          * @param {Store} this
6081          * @param {Roo.data.Record[]} records The Records that were loaded
6082          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6083          */
6084         beforeloadadd : true,
6085         /**
6086          * @event load
6087          * Fires after a new set of Records has been loaded, before they are added to the store.
6088          * @param {Store} this
6089          * @param {Roo.data.Record[]} records The Records that were loaded
6090          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6091          * @params {Object} return from reader
6092          */
6093         load : true,
6094         /**
6095          * @event loadexception
6096          * Fires if an exception occurs in the Proxy during loading.
6097          * Called with the signature of the Proxy's "loadexception" event.
6098          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6099          * 
6100          * @param {Proxy} 
6101          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6102          * @param {Object} load options 
6103          * @param {Object} jsonData from your request (normally this contains the Exception)
6104          */
6105         loadexception : true
6106     });
6107     
6108     if(this.proxy){
6109         this.proxy = Roo.factory(this.proxy, Roo.data);
6110         this.proxy.xmodule = this.xmodule || false;
6111         this.relayEvents(this.proxy,  ["loadexception"]);
6112     }
6113     this.sortToggle = {};
6114     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6115
6116     Roo.data.Store.superclass.constructor.call(this);
6117
6118     if(this.inlineData){
6119         this.loadData(this.inlineData);
6120         delete this.inlineData;
6121     }
6122 };
6123
6124 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6125      /**
6126     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6127     * without a remote query - used by combo/forms at present.
6128     */
6129     
6130     /**
6131     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6132     */
6133     /**
6134     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6135     */
6136     /**
6137     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6138     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6139     */
6140     /**
6141     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6142     * on any HTTP request
6143     */
6144     /**
6145     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6146     */
6147     /**
6148     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6149     */
6150     multiSort: false,
6151     /**
6152     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6153     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6154     */
6155     remoteSort : false,
6156
6157     /**
6158     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6159      * loaded or when a record is removed. (defaults to false).
6160     */
6161     pruneModifiedRecords : false,
6162
6163     // private
6164     lastOptions : null,
6165
6166     /**
6167      * Add Records to the Store and fires the add event.
6168      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6169      */
6170     add : function(records){
6171         records = [].concat(records);
6172         for(var i = 0, len = records.length; i < len; i++){
6173             records[i].join(this);
6174         }
6175         var index = this.data.length;
6176         this.data.addAll(records);
6177         this.fireEvent("add", this, records, index);
6178     },
6179
6180     /**
6181      * Remove a Record from the Store and fires the remove event.
6182      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6183      */
6184     remove : function(record){
6185         var index = this.data.indexOf(record);
6186         this.data.removeAt(index);
6187         if(this.pruneModifiedRecords){
6188             this.modified.remove(record);
6189         }
6190         this.fireEvent("remove", this, record, index);
6191     },
6192
6193     /**
6194      * Remove all Records from the Store and fires the clear event.
6195      */
6196     removeAll : function(){
6197         this.data.clear();
6198         if(this.pruneModifiedRecords){
6199             this.modified = [];
6200         }
6201         this.fireEvent("clear", this);
6202     },
6203
6204     /**
6205      * Inserts Records to the Store at the given index and fires the add event.
6206      * @param {Number} index The start index at which to insert the passed Records.
6207      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6208      */
6209     insert : function(index, records){
6210         records = [].concat(records);
6211         for(var i = 0, len = records.length; i < len; i++){
6212             this.data.insert(index, records[i]);
6213             records[i].join(this);
6214         }
6215         this.fireEvent("add", this, records, index);
6216     },
6217
6218     /**
6219      * Get the index within the cache of the passed Record.
6220      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6221      * @return {Number} The index of the passed Record. Returns -1 if not found.
6222      */
6223     indexOf : function(record){
6224         return this.data.indexOf(record);
6225     },
6226
6227     /**
6228      * Get the index within the cache of the Record with the passed id.
6229      * @param {String} id The id of the Record to find.
6230      * @return {Number} The index of the Record. Returns -1 if not found.
6231      */
6232     indexOfId : function(id){
6233         return this.data.indexOfKey(id);
6234     },
6235
6236     /**
6237      * Get the Record with the specified id.
6238      * @param {String} id The id of the Record to find.
6239      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6240      */
6241     getById : function(id){
6242         return this.data.key(id);
6243     },
6244
6245     /**
6246      * Get the Record at the specified index.
6247      * @param {Number} index The index of the Record to find.
6248      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6249      */
6250     getAt : function(index){
6251         return this.data.itemAt(index);
6252     },
6253
6254     /**
6255      * Returns a range of Records between specified indices.
6256      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6257      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6258      * @return {Roo.data.Record[]} An array of Records
6259      */
6260     getRange : function(start, end){
6261         return this.data.getRange(start, end);
6262     },
6263
6264     // private
6265     storeOptions : function(o){
6266         o = Roo.apply({}, o);
6267         delete o.callback;
6268         delete o.scope;
6269         this.lastOptions = o;
6270     },
6271
6272     /**
6273      * Loads the Record cache from the configured Proxy using the configured Reader.
6274      * <p>
6275      * If using remote paging, then the first load call must specify the <em>start</em>
6276      * and <em>limit</em> properties in the options.params property to establish the initial
6277      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6278      * <p>
6279      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6280      * and this call will return before the new data has been loaded. Perform any post-processing
6281      * in a callback function, or in a "load" event handler.</strong>
6282      * <p>
6283      * @param {Object} options An object containing properties which control loading options:<ul>
6284      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6285      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6286      * passed the following arguments:<ul>
6287      * <li>r : Roo.data.Record[]</li>
6288      * <li>options: Options object from the load call</li>
6289      * <li>success: Boolean success indicator</li></ul></li>
6290      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6291      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6292      * </ul>
6293      */
6294     load : function(options){
6295         options = options || {};
6296         if(this.fireEvent("beforeload", this, options) !== false){
6297             this.storeOptions(options);
6298             var p = Roo.apply(options.params || {}, this.baseParams);
6299             // if meta was not loaded from remote source.. try requesting it.
6300             if (!this.reader.metaFromRemote) {
6301                 p._requestMeta = 1;
6302             }
6303             if(this.sortInfo && this.remoteSort){
6304                 var pn = this.paramNames;
6305                 p[pn["sort"]] = this.sortInfo.field;
6306                 p[pn["dir"]] = this.sortInfo.direction;
6307             }
6308             if (this.multiSort) {
6309                 var pn = this.paramNames;
6310                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6311             }
6312             
6313             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6314         }
6315     },
6316
6317     /**
6318      * Reloads the Record cache from the configured Proxy using the configured Reader and
6319      * the options from the last load operation performed.
6320      * @param {Object} options (optional) An object containing properties which may override the options
6321      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6322      * the most recently used options are reused).
6323      */
6324     reload : function(options){
6325         this.load(Roo.applyIf(options||{}, this.lastOptions));
6326     },
6327
6328     // private
6329     // Called as a callback by the Reader during a load operation.
6330     loadRecords : function(o, options, success){
6331         if(!o || success === false){
6332             if(success !== false){
6333                 this.fireEvent("load", this, [], options, o);
6334             }
6335             if(options.callback){
6336                 options.callback.call(options.scope || this, [], options, false);
6337             }
6338             return;
6339         }
6340         // if data returned failure - throw an exception.
6341         if (o.success === false) {
6342             // show a message if no listener is registered.
6343             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6344                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6345             }
6346             // loadmask wil be hooked into this..
6347             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6348             return;
6349         }
6350         var r = o.records, t = o.totalRecords || r.length;
6351         
6352         this.fireEvent("beforeloadadd", this, r, options, o);
6353         
6354         if(!options || options.add !== true){
6355             if(this.pruneModifiedRecords){
6356                 this.modified = [];
6357             }
6358             for(var i = 0, len = r.length; i < len; i++){
6359                 r[i].join(this);
6360             }
6361             if(this.snapshot){
6362                 this.data = this.snapshot;
6363                 delete this.snapshot;
6364             }
6365             this.data.clear();
6366             this.data.addAll(r);
6367             this.totalLength = t;
6368             this.applySort();
6369             this.fireEvent("datachanged", this);
6370         }else{
6371             this.totalLength = Math.max(t, this.data.length+r.length);
6372             this.add(r);
6373         }
6374         this.fireEvent("load", this, r, options, o);
6375         if(options.callback){
6376             options.callback.call(options.scope || this, r, options, true);
6377         }
6378     },
6379
6380
6381     /**
6382      * Loads data from a passed data block. A Reader which understands the format of the data
6383      * must have been configured in the constructor.
6384      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6385      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6386      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6387      */
6388     loadData : function(o, append){
6389         var r = this.reader.readRecords(o);
6390         this.loadRecords(r, {add: append}, true);
6391     },
6392
6393     /**
6394      * Gets the number of cached records.
6395      * <p>
6396      * <em>If using paging, this may not be the total size of the dataset. If the data object
6397      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6398      * the data set size</em>
6399      */
6400     getCount : function(){
6401         return this.data.length || 0;
6402     },
6403
6404     /**
6405      * Gets the total number of records in the dataset as returned by the server.
6406      * <p>
6407      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6408      * the dataset size</em>
6409      */
6410     getTotalCount : function(){
6411         return this.totalLength || 0;
6412     },
6413
6414     /**
6415      * Returns the sort state of the Store as an object with two properties:
6416      * <pre><code>
6417  field {String} The name of the field by which the Records are sorted
6418  direction {String} The sort order, "ASC" or "DESC"
6419      * </code></pre>
6420      */
6421     getSortState : function(){
6422         return this.sortInfo;
6423     },
6424
6425     // private
6426     applySort : function(){
6427         if(this.sortInfo && !this.remoteSort){
6428             var s = this.sortInfo, f = s.field;
6429             var st = this.fields.get(f).sortType;
6430             var fn = function(r1, r2){
6431                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6432                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6433             };
6434             this.data.sort(s.direction, fn);
6435             if(this.snapshot && this.snapshot != this.data){
6436                 this.snapshot.sort(s.direction, fn);
6437             }
6438         }
6439     },
6440
6441     /**
6442      * Sets the default sort column and order to be used by the next load operation.
6443      * @param {String} fieldName The name of the field to sort by.
6444      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6445      */
6446     setDefaultSort : function(field, dir){
6447         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6448     },
6449
6450     /**
6451      * Sort the Records.
6452      * If remote sorting is used, the sort is performed on the server, and the cache is
6453      * reloaded. If local sorting is used, the cache is sorted internally.
6454      * @param {String} fieldName The name of the field to sort by.
6455      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6456      */
6457     sort : function(fieldName, dir){
6458         var f = this.fields.get(fieldName);
6459         if(!dir){
6460             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6461             
6462             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6463                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6464             }else{
6465                 dir = f.sortDir;
6466             }
6467         }
6468         this.sortToggle[f.name] = dir;
6469         this.sortInfo = {field: f.name, direction: dir};
6470         if(!this.remoteSort){
6471             this.applySort();
6472             this.fireEvent("datachanged", this);
6473         }else{
6474             this.load(this.lastOptions);
6475         }
6476     },
6477
6478     /**
6479      * Calls the specified function for each of the Records in the cache.
6480      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6481      * Returning <em>false</em> aborts and exits the iteration.
6482      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6483      */
6484     each : function(fn, scope){
6485         this.data.each(fn, scope);
6486     },
6487
6488     /**
6489      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6490      * (e.g., during paging).
6491      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6492      */
6493     getModifiedRecords : function(){
6494         return this.modified;
6495     },
6496
6497     // private
6498     createFilterFn : function(property, value, anyMatch){
6499         if(!value.exec){ // not a regex
6500             value = String(value);
6501             if(value.length == 0){
6502                 return false;
6503             }
6504             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6505         }
6506         return function(r){
6507             return value.test(r.data[property]);
6508         };
6509     },
6510
6511     /**
6512      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6513      * @param {String} property A field on your records
6514      * @param {Number} start The record index to start at (defaults to 0)
6515      * @param {Number} end The last record index to include (defaults to length - 1)
6516      * @return {Number} The sum
6517      */
6518     sum : function(property, start, end){
6519         var rs = this.data.items, v = 0;
6520         start = start || 0;
6521         end = (end || end === 0) ? end : rs.length-1;
6522
6523         for(var i = start; i <= end; i++){
6524             v += (rs[i].data[property] || 0);
6525         }
6526         return v;
6527     },
6528
6529     /**
6530      * Filter the records by a specified property.
6531      * @param {String} field A field on your records
6532      * @param {String/RegExp} value Either a string that the field
6533      * should start with or a RegExp to test against the field
6534      * @param {Boolean} anyMatch True to match any part not just the beginning
6535      */
6536     filter : function(property, value, anyMatch){
6537         var fn = this.createFilterFn(property, value, anyMatch);
6538         return fn ? this.filterBy(fn) : this.clearFilter();
6539     },
6540
6541     /**
6542      * Filter by a function. The specified function will be called with each
6543      * record in this data source. If the function returns true the record is included,
6544      * otherwise it is filtered.
6545      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6546      * @param {Object} scope (optional) The scope of the function (defaults to this)
6547      */
6548     filterBy : function(fn, scope){
6549         this.snapshot = this.snapshot || this.data;
6550         this.data = this.queryBy(fn, scope||this);
6551         this.fireEvent("datachanged", this);
6552     },
6553
6554     /**
6555      * Query the records by a specified property.
6556      * @param {String} field A field on your records
6557      * @param {String/RegExp} value Either a string that the field
6558      * should start with or a RegExp to test against the field
6559      * @param {Boolean} anyMatch True to match any part not just the beginning
6560      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6561      */
6562     query : function(property, value, anyMatch){
6563         var fn = this.createFilterFn(property, value, anyMatch);
6564         return fn ? this.queryBy(fn) : this.data.clone();
6565     },
6566
6567     /**
6568      * Query by a function. The specified function will be called with each
6569      * record in this data source. If the function returns true the record is included
6570      * in the results.
6571      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6572      * @param {Object} scope (optional) The scope of the function (defaults to this)
6573       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6574      **/
6575     queryBy : function(fn, scope){
6576         var data = this.snapshot || this.data;
6577         return data.filterBy(fn, scope||this);
6578     },
6579
6580     /**
6581      * Collects unique values for a particular dataIndex from this store.
6582      * @param {String} dataIndex The property to collect
6583      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6584      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6585      * @return {Array} An array of the unique values
6586      **/
6587     collect : function(dataIndex, allowNull, bypassFilter){
6588         var d = (bypassFilter === true && this.snapshot) ?
6589                 this.snapshot.items : this.data.items;
6590         var v, sv, r = [], l = {};
6591         for(var i = 0, len = d.length; i < len; i++){
6592             v = d[i].data[dataIndex];
6593             sv = String(v);
6594             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6595                 l[sv] = true;
6596                 r[r.length] = v;
6597             }
6598         }
6599         return r;
6600     },
6601
6602     /**
6603      * Revert to a view of the Record cache with no filtering applied.
6604      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6605      */
6606     clearFilter : function(suppressEvent){
6607         if(this.snapshot && this.snapshot != this.data){
6608             this.data = this.snapshot;
6609             delete this.snapshot;
6610             if(suppressEvent !== true){
6611                 this.fireEvent("datachanged", this);
6612             }
6613         }
6614     },
6615
6616     // private
6617     afterEdit : function(record){
6618         if(this.modified.indexOf(record) == -1){
6619             this.modified.push(record);
6620         }
6621         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6622     },
6623     
6624     // private
6625     afterReject : function(record){
6626         this.modified.remove(record);
6627         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6628     },
6629
6630     // private
6631     afterCommit : function(record){
6632         this.modified.remove(record);
6633         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6634     },
6635
6636     /**
6637      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6638      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6639      */
6640     commitChanges : function(){
6641         var m = this.modified.slice(0);
6642         this.modified = [];
6643         for(var i = 0, len = m.length; i < len; i++){
6644             m[i].commit();
6645         }
6646     },
6647
6648     /**
6649      * Cancel outstanding changes on all changed records.
6650      */
6651     rejectChanges : function(){
6652         var m = this.modified.slice(0);
6653         this.modified = [];
6654         for(var i = 0, len = m.length; i < len; i++){
6655             m[i].reject();
6656         }
6657     },
6658
6659     onMetaChange : function(meta, rtype, o){
6660         this.recordType = rtype;
6661         this.fields = rtype.prototype.fields;
6662         delete this.snapshot;
6663         this.sortInfo = meta.sortInfo || this.sortInfo;
6664         this.modified = [];
6665         this.fireEvent('metachange', this, this.reader.meta);
6666     }
6667 });/*
6668  * Based on:
6669  * Ext JS Library 1.1.1
6670  * Copyright(c) 2006-2007, Ext JS, LLC.
6671  *
6672  * Originally Released Under LGPL - original licence link has changed is not relivant.
6673  *
6674  * Fork - LGPL
6675  * <script type="text/javascript">
6676  */
6677
6678 /**
6679  * @class Roo.data.SimpleStore
6680  * @extends Roo.data.Store
6681  * Small helper class to make creating Stores from Array data easier.
6682  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6683  * @cfg {Array} fields An array of field definition objects, or field name strings.
6684  * @cfg {Array} data The multi-dimensional array of data
6685  * @constructor
6686  * @param {Object} config
6687  */
6688 Roo.data.SimpleStore = function(config){
6689     Roo.data.SimpleStore.superclass.constructor.call(this, {
6690         isLocal : true,
6691         reader: new Roo.data.ArrayReader({
6692                 id: config.id
6693             },
6694             Roo.data.Record.create(config.fields)
6695         ),
6696         proxy : new Roo.data.MemoryProxy(config.data)
6697     });
6698     this.load();
6699 };
6700 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6701  * Based on:
6702  * Ext JS Library 1.1.1
6703  * Copyright(c) 2006-2007, Ext JS, LLC.
6704  *
6705  * Originally Released Under LGPL - original licence link has changed is not relivant.
6706  *
6707  * Fork - LGPL
6708  * <script type="text/javascript">
6709  */
6710
6711 /**
6712 /**
6713  * @extends Roo.data.Store
6714  * @class Roo.data.JsonStore
6715  * Small helper class to make creating Stores for JSON data easier. <br/>
6716 <pre><code>
6717 var store = new Roo.data.JsonStore({
6718     url: 'get-images.php',
6719     root: 'images',
6720     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6721 });
6722 </code></pre>
6723  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6724  * JsonReader and HttpProxy (unless inline data is provided).</b>
6725  * @cfg {Array} fields An array of field definition objects, or field name strings.
6726  * @constructor
6727  * @param {Object} config
6728  */
6729 Roo.data.JsonStore = function(c){
6730     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6731         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6732         reader: new Roo.data.JsonReader(c, c.fields)
6733     }));
6734 };
6735 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6736  * Based on:
6737  * Ext JS Library 1.1.1
6738  * Copyright(c) 2006-2007, Ext JS, LLC.
6739  *
6740  * Originally Released Under LGPL - original licence link has changed is not relivant.
6741  *
6742  * Fork - LGPL
6743  * <script type="text/javascript">
6744  */
6745
6746  
6747 Roo.data.Field = function(config){
6748     if(typeof config == "string"){
6749         config = {name: config};
6750     }
6751     Roo.apply(this, config);
6752     
6753     if(!this.type){
6754         this.type = "auto";
6755     }
6756     
6757     var st = Roo.data.SortTypes;
6758     // named sortTypes are supported, here we look them up
6759     if(typeof this.sortType == "string"){
6760         this.sortType = st[this.sortType];
6761     }
6762     
6763     // set default sortType for strings and dates
6764     if(!this.sortType){
6765         switch(this.type){
6766             case "string":
6767                 this.sortType = st.asUCString;
6768                 break;
6769             case "date":
6770                 this.sortType = st.asDate;
6771                 break;
6772             default:
6773                 this.sortType = st.none;
6774         }
6775     }
6776
6777     // define once
6778     var stripRe = /[\$,%]/g;
6779
6780     // prebuilt conversion function for this field, instead of
6781     // switching every time we're reading a value
6782     if(!this.convert){
6783         var cv, dateFormat = this.dateFormat;
6784         switch(this.type){
6785             case "":
6786             case "auto":
6787             case undefined:
6788                 cv = function(v){ return v; };
6789                 break;
6790             case "string":
6791                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6792                 break;
6793             case "int":
6794                 cv = function(v){
6795                     return v !== undefined && v !== null && v !== '' ?
6796                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6797                     };
6798                 break;
6799             case "float":
6800                 cv = function(v){
6801                     return v !== undefined && v !== null && v !== '' ?
6802                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6803                     };
6804                 break;
6805             case "bool":
6806             case "boolean":
6807                 cv = function(v){ return v === true || v === "true" || v == 1; };
6808                 break;
6809             case "date":
6810                 cv = function(v){
6811                     if(!v){
6812                         return '';
6813                     }
6814                     if(v instanceof Date){
6815                         return v;
6816                     }
6817                     if(dateFormat){
6818                         if(dateFormat == "timestamp"){
6819                             return new Date(v*1000);
6820                         }
6821                         return Date.parseDate(v, dateFormat);
6822                     }
6823                     var parsed = Date.parse(v);
6824                     return parsed ? new Date(parsed) : null;
6825                 };
6826              break;
6827             
6828         }
6829         this.convert = cv;
6830     }
6831 };
6832
6833 Roo.data.Field.prototype = {
6834     dateFormat: null,
6835     defaultValue: "",
6836     mapping: null,
6837     sortType : null,
6838     sortDir : "ASC"
6839 };/*
6840  * Based on:
6841  * Ext JS Library 1.1.1
6842  * Copyright(c) 2006-2007, Ext JS, LLC.
6843  *
6844  * Originally Released Under LGPL - original licence link has changed is not relivant.
6845  *
6846  * Fork - LGPL
6847  * <script type="text/javascript">
6848  */
6849  
6850 // Base class for reading structured data from a data source.  This class is intended to be
6851 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6852
6853 /**
6854  * @class Roo.data.DataReader
6855  * Base class for reading structured data from a data source.  This class is intended to be
6856  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6857  */
6858
6859 Roo.data.DataReader = function(meta, recordType){
6860     
6861     this.meta = meta;
6862     
6863     this.recordType = recordType instanceof Array ? 
6864         Roo.data.Record.create(recordType) : recordType;
6865 };
6866
6867 Roo.data.DataReader.prototype = {
6868      /**
6869      * Create an empty record
6870      * @param {Object} data (optional) - overlay some values
6871      * @return {Roo.data.Record} record created.
6872      */
6873     newRow :  function(d) {
6874         var da =  {};
6875         this.recordType.prototype.fields.each(function(c) {
6876             switch( c.type) {
6877                 case 'int' : da[c.name] = 0; break;
6878                 case 'date' : da[c.name] = new Date(); break;
6879                 case 'float' : da[c.name] = 0.0; break;
6880                 case 'boolean' : da[c.name] = false; break;
6881                 default : da[c.name] = ""; break;
6882             }
6883             
6884         });
6885         return new this.recordType(Roo.apply(da, d));
6886     }
6887     
6888 };/*
6889  * Based on:
6890  * Ext JS Library 1.1.1
6891  * Copyright(c) 2006-2007, Ext JS, LLC.
6892  *
6893  * Originally Released Under LGPL - original licence link has changed is not relivant.
6894  *
6895  * Fork - LGPL
6896  * <script type="text/javascript">
6897  */
6898
6899 /**
6900  * @class Roo.data.DataProxy
6901  * @extends Roo.data.Observable
6902  * This class is an abstract base class for implementations which provide retrieval of
6903  * unformatted data objects.<br>
6904  * <p>
6905  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6906  * (of the appropriate type which knows how to parse the data object) to provide a block of
6907  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6908  * <p>
6909  * Custom implementations must implement the load method as described in
6910  * {@link Roo.data.HttpProxy#load}.
6911  */
6912 Roo.data.DataProxy = function(){
6913     this.addEvents({
6914         /**
6915          * @event beforeload
6916          * Fires before a network request is made to retrieve a data object.
6917          * @param {Object} This DataProxy object.
6918          * @param {Object} params The params parameter to the load function.
6919          */
6920         beforeload : true,
6921         /**
6922          * @event load
6923          * Fires before the load method's callback is called.
6924          * @param {Object} This DataProxy object.
6925          * @param {Object} o The data object.
6926          * @param {Object} arg The callback argument object passed to the load function.
6927          */
6928         load : true,
6929         /**
6930          * @event loadexception
6931          * Fires if an Exception occurs during data retrieval.
6932          * @param {Object} This DataProxy object.
6933          * @param {Object} o The data object.
6934          * @param {Object} arg The callback argument object passed to the load function.
6935          * @param {Object} e The Exception.
6936          */
6937         loadexception : true
6938     });
6939     Roo.data.DataProxy.superclass.constructor.call(this);
6940 };
6941
6942 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
6943
6944     /**
6945      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
6946      */
6947 /*
6948  * Based on:
6949  * Ext JS Library 1.1.1
6950  * Copyright(c) 2006-2007, Ext JS, LLC.
6951  *
6952  * Originally Released Under LGPL - original licence link has changed is not relivant.
6953  *
6954  * Fork - LGPL
6955  * <script type="text/javascript">
6956  */
6957 /**
6958  * @class Roo.data.MemoryProxy
6959  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
6960  * to the Reader when its load method is called.
6961  * @constructor
6962  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
6963  */
6964 Roo.data.MemoryProxy = function(data){
6965     if (data.data) {
6966         data = data.data;
6967     }
6968     Roo.data.MemoryProxy.superclass.constructor.call(this);
6969     this.data = data;
6970 };
6971
6972 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
6973     /**
6974      * Load data from the requested source (in this case an in-memory
6975      * data object passed to the constructor), read the data object into
6976      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6977      * process that block using the passed callback.
6978      * @param {Object} params This parameter is not used by the MemoryProxy class.
6979      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6980      * object into a block of Roo.data.Records.
6981      * @param {Function} callback The function into which to pass the block of Roo.data.records.
6982      * The function must be passed <ul>
6983      * <li>The Record block object</li>
6984      * <li>The "arg" argument from the load function</li>
6985      * <li>A boolean success indicator</li>
6986      * </ul>
6987      * @param {Object} scope The scope in which to call the callback
6988      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6989      */
6990     load : function(params, reader, callback, scope, arg){
6991         params = params || {};
6992         var result;
6993         try {
6994             result = reader.readRecords(this.data);
6995         }catch(e){
6996             this.fireEvent("loadexception", this, arg, null, e);
6997             callback.call(scope, null, arg, false);
6998             return;
6999         }
7000         callback.call(scope, result, arg, true);
7001     },
7002     
7003     // private
7004     update : function(params, records){
7005         
7006     }
7007 });/*
7008  * Based on:
7009  * Ext JS Library 1.1.1
7010  * Copyright(c) 2006-2007, Ext JS, LLC.
7011  *
7012  * Originally Released Under LGPL - original licence link has changed is not relivant.
7013  *
7014  * Fork - LGPL
7015  * <script type="text/javascript">
7016  */
7017 /**
7018  * @class Roo.data.HttpProxy
7019  * @extends Roo.data.DataProxy
7020  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7021  * configured to reference a certain URL.<br><br>
7022  * <p>
7023  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7024  * from which the running page was served.<br><br>
7025  * <p>
7026  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7027  * <p>
7028  * Be aware that to enable the browser to parse an XML document, the server must set
7029  * the Content-Type header in the HTTP response to "text/xml".
7030  * @constructor
7031  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7032  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7033  * will be used to make the request.
7034  */
7035 Roo.data.HttpProxy = function(conn){
7036     Roo.data.HttpProxy.superclass.constructor.call(this);
7037     // is conn a conn config or a real conn?
7038     this.conn = conn;
7039     this.useAjax = !conn || !conn.events;
7040   
7041 };
7042
7043 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7044     // thse are take from connection...
7045     
7046     /**
7047      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7048      */
7049     /**
7050      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7051      * extra parameters to each request made by this object. (defaults to undefined)
7052      */
7053     /**
7054      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7055      *  to each request made by this object. (defaults to undefined)
7056      */
7057     /**
7058      * @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)
7059      */
7060     /**
7061      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7062      */
7063      /**
7064      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7065      * @type Boolean
7066      */
7067   
7068
7069     /**
7070      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7071      * @type Boolean
7072      */
7073     /**
7074      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7075      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7076      * a finer-grained basis than the DataProxy events.
7077      */
7078     getConnection : function(){
7079         return this.useAjax ? Roo.Ajax : this.conn;
7080     },
7081
7082     /**
7083      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7084      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7085      * process that block using the passed callback.
7086      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7087      * for the request to the remote server.
7088      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7089      * object into a block of Roo.data.Records.
7090      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7091      * The function must be passed <ul>
7092      * <li>The Record block object</li>
7093      * <li>The "arg" argument from the load function</li>
7094      * <li>A boolean success indicator</li>
7095      * </ul>
7096      * @param {Object} scope The scope in which to call the callback
7097      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7098      */
7099     load : function(params, reader, callback, scope, arg){
7100         if(this.fireEvent("beforeload", this, params) !== false){
7101             var  o = {
7102                 params : params || {},
7103                 request: {
7104                     callback : callback,
7105                     scope : scope,
7106                     arg : arg
7107                 },
7108                 reader: reader,
7109                 callback : this.loadResponse,
7110                 scope: this
7111             };
7112             if(this.useAjax){
7113                 Roo.applyIf(o, this.conn);
7114                 if(this.activeRequest){
7115                     Roo.Ajax.abort(this.activeRequest);
7116                 }
7117                 this.activeRequest = Roo.Ajax.request(o);
7118             }else{
7119                 this.conn.request(o);
7120             }
7121         }else{
7122             callback.call(scope||this, null, arg, false);
7123         }
7124     },
7125
7126     // private
7127     loadResponse : function(o, success, response){
7128         delete this.activeRequest;
7129         if(!success){
7130             this.fireEvent("loadexception", this, o, response);
7131             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7132             return;
7133         }
7134         var result;
7135         try {
7136             result = o.reader.read(response);
7137         }catch(e){
7138             this.fireEvent("loadexception", this, o, response, e);
7139             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7140             return;
7141         }
7142         
7143         this.fireEvent("load", this, o, o.request.arg);
7144         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7145     },
7146
7147     // private
7148     update : function(dataSet){
7149
7150     },
7151
7152     // private
7153     updateResponse : function(dataSet){
7154
7155     }
7156 });/*
7157  * Based on:
7158  * Ext JS Library 1.1.1
7159  * Copyright(c) 2006-2007, Ext JS, LLC.
7160  *
7161  * Originally Released Under LGPL - original licence link has changed is not relivant.
7162  *
7163  * Fork - LGPL
7164  * <script type="text/javascript">
7165  */
7166
7167 /**
7168  * @class Roo.data.ScriptTagProxy
7169  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7170  * other than the originating domain of the running page.<br><br>
7171  * <p>
7172  * <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
7173  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7174  * <p>
7175  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7176  * source code that is used as the source inside a &lt;script> tag.<br><br>
7177  * <p>
7178  * In order for the browser to process the returned data, the server must wrap the data object
7179  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7180  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7181  * depending on whether the callback name was passed:
7182  * <p>
7183  * <pre><code>
7184 boolean scriptTag = false;
7185 String cb = request.getParameter("callback");
7186 if (cb != null) {
7187     scriptTag = true;
7188     response.setContentType("text/javascript");
7189 } else {
7190     response.setContentType("application/x-json");
7191 }
7192 Writer out = response.getWriter();
7193 if (scriptTag) {
7194     out.write(cb + "(");
7195 }
7196 out.print(dataBlock.toJsonString());
7197 if (scriptTag) {
7198     out.write(");");
7199 }
7200 </pre></code>
7201  *
7202  * @constructor
7203  * @param {Object} config A configuration object.
7204  */
7205 Roo.data.ScriptTagProxy = function(config){
7206     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7207     Roo.apply(this, config);
7208     this.head = document.getElementsByTagName("head")[0];
7209 };
7210
7211 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7212
7213 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7214     /**
7215      * @cfg {String} url The URL from which to request the data object.
7216      */
7217     /**
7218      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7219      */
7220     timeout : 30000,
7221     /**
7222      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7223      * the server the name of the callback function set up by the load call to process the returned data object.
7224      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7225      * javascript output which calls this named function passing the data object as its only parameter.
7226      */
7227     callbackParam : "callback",
7228     /**
7229      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7230      * name to the request.
7231      */
7232     nocache : true,
7233
7234     /**
7235      * Load data from the configured URL, read the data object into
7236      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7237      * process that block using the passed callback.
7238      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7239      * for the request to the remote server.
7240      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7241      * object into a block of Roo.data.Records.
7242      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7243      * The function must be passed <ul>
7244      * <li>The Record block object</li>
7245      * <li>The "arg" argument from the load function</li>
7246      * <li>A boolean success indicator</li>
7247      * </ul>
7248      * @param {Object} scope The scope in which to call the callback
7249      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7250      */
7251     load : function(params, reader, callback, scope, arg){
7252         if(this.fireEvent("beforeload", this, params) !== false){
7253
7254             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7255
7256             var url = this.url;
7257             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7258             if(this.nocache){
7259                 url += "&_dc=" + (new Date().getTime());
7260             }
7261             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7262             var trans = {
7263                 id : transId,
7264                 cb : "stcCallback"+transId,
7265                 scriptId : "stcScript"+transId,
7266                 params : params,
7267                 arg : arg,
7268                 url : url,
7269                 callback : callback,
7270                 scope : scope,
7271                 reader : reader
7272             };
7273             var conn = this;
7274
7275             window[trans.cb] = function(o){
7276                 conn.handleResponse(o, trans);
7277             };
7278
7279             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7280
7281             if(this.autoAbort !== false){
7282                 this.abort();
7283             }
7284
7285             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7286
7287             var script = document.createElement("script");
7288             script.setAttribute("src", url);
7289             script.setAttribute("type", "text/javascript");
7290             script.setAttribute("id", trans.scriptId);
7291             this.head.appendChild(script);
7292
7293             this.trans = trans;
7294         }else{
7295             callback.call(scope||this, null, arg, false);
7296         }
7297     },
7298
7299     // private
7300     isLoading : function(){
7301         return this.trans ? true : false;
7302     },
7303
7304     /**
7305      * Abort the current server request.
7306      */
7307     abort : function(){
7308         if(this.isLoading()){
7309             this.destroyTrans(this.trans);
7310         }
7311     },
7312
7313     // private
7314     destroyTrans : function(trans, isLoaded){
7315         this.head.removeChild(document.getElementById(trans.scriptId));
7316         clearTimeout(trans.timeoutId);
7317         if(isLoaded){
7318             window[trans.cb] = undefined;
7319             try{
7320                 delete window[trans.cb];
7321             }catch(e){}
7322         }else{
7323             // if hasn't been loaded, wait for load to remove it to prevent script error
7324             window[trans.cb] = function(){
7325                 window[trans.cb] = undefined;
7326                 try{
7327                     delete window[trans.cb];
7328                 }catch(e){}
7329             };
7330         }
7331     },
7332
7333     // private
7334     handleResponse : function(o, trans){
7335         this.trans = false;
7336         this.destroyTrans(trans, true);
7337         var result;
7338         try {
7339             result = trans.reader.readRecords(o);
7340         }catch(e){
7341             this.fireEvent("loadexception", this, o, trans.arg, e);
7342             trans.callback.call(trans.scope||window, null, trans.arg, false);
7343             return;
7344         }
7345         this.fireEvent("load", this, o, trans.arg);
7346         trans.callback.call(trans.scope||window, result, trans.arg, true);
7347     },
7348
7349     // private
7350     handleFailure : function(trans){
7351         this.trans = false;
7352         this.destroyTrans(trans, false);
7353         this.fireEvent("loadexception", this, null, trans.arg);
7354         trans.callback.call(trans.scope||window, null, trans.arg, false);
7355     }
7356 });/*
7357  * Based on:
7358  * Ext JS Library 1.1.1
7359  * Copyright(c) 2006-2007, Ext JS, LLC.
7360  *
7361  * Originally Released Under LGPL - original licence link has changed is not relivant.
7362  *
7363  * Fork - LGPL
7364  * <script type="text/javascript">
7365  */
7366
7367 /**
7368  * @class Roo.data.JsonReader
7369  * @extends Roo.data.DataReader
7370  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7371  * based on mappings in a provided Roo.data.Record constructor.
7372  * 
7373  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7374  * in the reply previously. 
7375  * 
7376  * <p>
7377  * Example code:
7378  * <pre><code>
7379 var RecordDef = Roo.data.Record.create([
7380     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7381     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7382 ]);
7383 var myReader = new Roo.data.JsonReader({
7384     totalProperty: "results",    // The property which contains the total dataset size (optional)
7385     root: "rows",                // The property which contains an Array of row objects
7386     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7387 }, RecordDef);
7388 </code></pre>
7389  * <p>
7390  * This would consume a JSON file like this:
7391  * <pre><code>
7392 { 'results': 2, 'rows': [
7393     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7394     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7395 }
7396 </code></pre>
7397  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7398  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7399  * paged from the remote server.
7400  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7401  * @cfg {String} root name of the property which contains the Array of row objects.
7402  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7403  * @constructor
7404  * Create a new JsonReader
7405  * @param {Object} meta Metadata configuration options
7406  * @param {Object} recordType Either an Array of field definition objects,
7407  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7408  */
7409 Roo.data.JsonReader = function(meta, recordType){
7410     
7411     meta = meta || {};
7412     // set some defaults:
7413     Roo.applyIf(meta, {
7414         totalProperty: 'total',
7415         successProperty : 'success',
7416         root : 'data',
7417         id : 'id'
7418     });
7419     
7420     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7421 };
7422 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7423     
7424     /**
7425      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7426      * Used by Store query builder to append _requestMeta to params.
7427      * 
7428      */
7429     metaFromRemote : false,
7430     /**
7431      * This method is only used by a DataProxy which has retrieved data from a remote server.
7432      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7433      * @return {Object} data A data block which is used by an Roo.data.Store object as
7434      * a cache of Roo.data.Records.
7435      */
7436     read : function(response){
7437         var json = response.responseText;
7438        
7439         var o = /* eval:var:o */ eval("("+json+")");
7440         if(!o) {
7441             throw {message: "JsonReader.read: Json object not found"};
7442         }
7443         
7444         if(o.metaData){
7445             
7446             delete this.ef;
7447             this.metaFromRemote = true;
7448             this.meta = o.metaData;
7449             this.recordType = Roo.data.Record.create(o.metaData.fields);
7450             this.onMetaChange(this.meta, this.recordType, o);
7451         }
7452         return this.readRecords(o);
7453     },
7454
7455     // private function a store will implement
7456     onMetaChange : function(meta, recordType, o){
7457
7458     },
7459
7460     /**
7461          * @ignore
7462          */
7463     simpleAccess: function(obj, subsc) {
7464         return obj[subsc];
7465     },
7466
7467         /**
7468          * @ignore
7469          */
7470     getJsonAccessor: function(){
7471         var re = /[\[\.]/;
7472         return function(expr) {
7473             try {
7474                 return(re.test(expr))
7475                     ? new Function("obj", "return obj." + expr)
7476                     : function(obj){
7477                         return obj[expr];
7478                     };
7479             } catch(e){}
7480             return Roo.emptyFn;
7481         };
7482     }(),
7483
7484     /**
7485      * Create a data block containing Roo.data.Records from an XML document.
7486      * @param {Object} o An object which contains an Array of row objects in the property specified
7487      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7488      * which contains the total size of the dataset.
7489      * @return {Object} data A data block which is used by an Roo.data.Store object as
7490      * a cache of Roo.data.Records.
7491      */
7492     readRecords : function(o){
7493         /**
7494          * After any data loads, the raw JSON data is available for further custom processing.
7495          * @type Object
7496          */
7497         this.o = o;
7498         var s = this.meta, Record = this.recordType,
7499             f = Record.prototype.fields, fi = f.items, fl = f.length;
7500
7501 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7502         if (!this.ef) {
7503             if(s.totalProperty) {
7504                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7505                 }
7506                 if(s.successProperty) {
7507                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7508                 }
7509                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7510                 if (s.id) {
7511                         var g = this.getJsonAccessor(s.id);
7512                         this.getId = function(rec) {
7513                                 var r = g(rec);
7514                                 return (r === undefined || r === "") ? null : r;
7515                         };
7516                 } else {
7517                         this.getId = function(){return null;};
7518                 }
7519             this.ef = [];
7520             for(var jj = 0; jj < fl; jj++){
7521                 f = fi[jj];
7522                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7523                 this.ef[jj] = this.getJsonAccessor(map);
7524             }
7525         }
7526
7527         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7528         if(s.totalProperty){
7529             var vt = parseInt(this.getTotal(o), 10);
7530             if(!isNaN(vt)){
7531                 totalRecords = vt;
7532             }
7533         }
7534         if(s.successProperty){
7535             var vs = this.getSuccess(o);
7536             if(vs === false || vs === 'false'){
7537                 success = false;
7538             }
7539         }
7540         var records = [];
7541             for(var i = 0; i < c; i++){
7542                     var n = root[i];
7543                 var values = {};
7544                 var id = this.getId(n);
7545                 for(var j = 0; j < fl; j++){
7546                     f = fi[j];
7547                 var v = this.ef[j](n);
7548                 if (!f.convert) {
7549                     Roo.log('missing convert for ' + f.name);
7550                     Roo.log(f);
7551                     continue;
7552                 }
7553                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7554                 }
7555                 var record = new Record(values, id);
7556                 record.json = n;
7557                 records[i] = record;
7558             }
7559             return {
7560             raw : o,
7561                 success : success,
7562                 records : records,
7563                 totalRecords : totalRecords
7564             };
7565     }
7566 });/*
7567  * Based on:
7568  * Ext JS Library 1.1.1
7569  * Copyright(c) 2006-2007, Ext JS, LLC.
7570  *
7571  * Originally Released Under LGPL - original licence link has changed is not relivant.
7572  *
7573  * Fork - LGPL
7574  * <script type="text/javascript">
7575  */
7576
7577 /**
7578  * @class Roo.data.ArrayReader
7579  * @extends Roo.data.DataReader
7580  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7581  * Each element of that Array represents a row of data fields. The
7582  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7583  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7584  * <p>
7585  * Example code:.
7586  * <pre><code>
7587 var RecordDef = Roo.data.Record.create([
7588     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7589     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7590 ]);
7591 var myReader = new Roo.data.ArrayReader({
7592     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7593 }, RecordDef);
7594 </code></pre>
7595  * <p>
7596  * This would consume an Array like this:
7597  * <pre><code>
7598 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7599   </code></pre>
7600  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7601  * @constructor
7602  * Create a new JsonReader
7603  * @param {Object} meta Metadata configuration options.
7604  * @param {Object} recordType Either an Array of field definition objects
7605  * as specified to {@link Roo.data.Record#create},
7606  * or an {@link Roo.data.Record} object
7607  * created using {@link Roo.data.Record#create}.
7608  */
7609 Roo.data.ArrayReader = function(meta, recordType){
7610     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7611 };
7612
7613 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7614     /**
7615      * Create a data block containing Roo.data.Records from an XML document.
7616      * @param {Object} o An Array of row objects which represents the dataset.
7617      * @return {Object} data A data block which is used by an Roo.data.Store object as
7618      * a cache of Roo.data.Records.
7619      */
7620     readRecords : function(o){
7621         var sid = this.meta ? this.meta.id : null;
7622         var recordType = this.recordType, fields = recordType.prototype.fields;
7623         var records = [];
7624         var root = o;
7625             for(var i = 0; i < root.length; i++){
7626                     var n = root[i];
7627                 var values = {};
7628                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7629                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7630                 var f = fields.items[j];
7631                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7632                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7633                 v = f.convert(v);
7634                 values[f.name] = v;
7635             }
7636                 var record = new recordType(values, id);
7637                 record.json = n;
7638                 records[records.length] = record;
7639             }
7640             return {
7641                 records : records,
7642                 totalRecords : records.length
7643             };
7644     }
7645 });/*
7646  * - LGPL
7647  * * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.ComboBox
7652  * @extends Roo.bootstrap.TriggerField
7653  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7654  * @cfg {Boolean} append (true|false) default false
7655  * @constructor
7656  * Create a new ComboBox.
7657  * @param {Object} config Configuration options
7658  */
7659 Roo.bootstrap.ComboBox = function(config){
7660     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7661     this.addEvents({
7662         /**
7663          * @event expand
7664          * Fires when the dropdown list is expanded
7665              * @param {Roo.bootstrap.ComboBox} combo This combo box
7666              */
7667         'expand' : true,
7668         /**
7669          * @event collapse
7670          * Fires when the dropdown list is collapsed
7671              * @param {Roo.bootstrap.ComboBox} combo This combo box
7672              */
7673         'collapse' : true,
7674         /**
7675          * @event beforeselect
7676          * Fires before a list item is selected. Return false to cancel the selection.
7677              * @param {Roo.bootstrap.ComboBox} combo This combo box
7678              * @param {Roo.data.Record} record The data record returned from the underlying store
7679              * @param {Number} index The index of the selected item in the dropdown list
7680              */
7681         'beforeselect' : true,
7682         /**
7683          * @event select
7684          * Fires when a list item is selected
7685              * @param {Roo.bootstrap.ComboBox} combo This combo box
7686              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7687              * @param {Number} index The index of the selected item in the dropdown list
7688              */
7689         'select' : true,
7690         /**
7691          * @event beforequery
7692          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7693          * The event object passed has these properties:
7694              * @param {Roo.bootstrap.ComboBox} combo This combo box
7695              * @param {String} query The query
7696              * @param {Boolean} forceAll true to force "all" query
7697              * @param {Boolean} cancel true to cancel the query
7698              * @param {Object} e The query event object
7699              */
7700         'beforequery': true,
7701          /**
7702          * @event add
7703          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7704              * @param {Roo.bootstrap.ComboBox} combo This combo box
7705              */
7706         'add' : true,
7707         /**
7708          * @event edit
7709          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7710              * @param {Roo.bootstrap.ComboBox} combo This combo box
7711              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7712              */
7713         'edit' : true,
7714         /**
7715          * @event remove
7716          * Fires when the remove value from the combobox array
7717              * @param {Roo.bootstrap.ComboBox} combo This combo box
7718              */
7719         'remove' : true
7720         
7721     });
7722     
7723     
7724     this.selectedIndex = -1;
7725     if(this.mode == 'local'){
7726         if(config.queryDelay === undefined){
7727             this.queryDelay = 10;
7728         }
7729         if(config.minChars === undefined){
7730             this.minChars = 0;
7731         }
7732     }
7733 };
7734
7735 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7736      
7737     /**
7738      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7739      * rendering into an Roo.Editor, defaults to false)
7740      */
7741     /**
7742      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7743      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7744      */
7745     /**
7746      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7747      */
7748     /**
7749      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7750      * the dropdown list (defaults to undefined, with no header element)
7751      */
7752
7753      /**
7754      * @cfg {String/Roo.Template} tpl The template to use to render the output
7755      */
7756      
7757      /**
7758      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7759      */
7760     listWidth: undefined,
7761     /**
7762      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7763      * mode = 'remote' or 'text' if mode = 'local')
7764      */
7765     displayField: undefined,
7766     /**
7767      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7768      * mode = 'remote' or 'value' if mode = 'local'). 
7769      * Note: use of a valueField requires the user make a selection
7770      * in order for a value to be mapped.
7771      */
7772     valueField: undefined,
7773     
7774     
7775     /**
7776      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7777      * field's data value (defaults to the underlying DOM element's name)
7778      */
7779     hiddenName: undefined,
7780     /**
7781      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7782      */
7783     listClass: '',
7784     /**
7785      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7786      */
7787     selectedClass: 'active',
7788     
7789     /**
7790      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7791      */
7792     shadow:'sides',
7793     /**
7794      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7795      * anchor positions (defaults to 'tl-bl')
7796      */
7797     listAlign: 'tl-bl?',
7798     /**
7799      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7800      */
7801     maxHeight: 300,
7802     /**
7803      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7804      * query specified by the allQuery config option (defaults to 'query')
7805      */
7806     triggerAction: 'query',
7807     /**
7808      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7809      * (defaults to 4, does not apply if editable = false)
7810      */
7811     minChars : 4,
7812     /**
7813      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7814      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7815      */
7816     typeAhead: false,
7817     /**
7818      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7819      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7820      */
7821     queryDelay: 500,
7822     /**
7823      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7824      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7825      */
7826     pageSize: 0,
7827     /**
7828      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7829      * when editable = true (defaults to false)
7830      */
7831     selectOnFocus:false,
7832     /**
7833      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7834      */
7835     queryParam: 'query',
7836     /**
7837      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7838      * when mode = 'remote' (defaults to 'Loading...')
7839      */
7840     loadingText: 'Loading...',
7841     /**
7842      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7843      */
7844     resizable: false,
7845     /**
7846      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7847      */
7848     handleHeight : 8,
7849     /**
7850      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7851      * traditional select (defaults to true)
7852      */
7853     editable: true,
7854     /**
7855      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7856      */
7857     allQuery: '',
7858     /**
7859      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7860      */
7861     mode: 'remote',
7862     /**
7863      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7864      * listWidth has a higher value)
7865      */
7866     minListWidth : 70,
7867     /**
7868      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7869      * allow the user to set arbitrary text into the field (defaults to false)
7870      */
7871     forceSelection:false,
7872     /**
7873      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7874      * if typeAhead = true (defaults to 250)
7875      */
7876     typeAheadDelay : 250,
7877     /**
7878      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7879      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7880      */
7881     valueNotFoundText : undefined,
7882     /**
7883      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7884      */
7885     blockFocus : false,
7886     
7887     /**
7888      * @cfg {Boolean} disableClear Disable showing of clear button.
7889      */
7890     disableClear : false,
7891     /**
7892      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7893      */
7894     alwaysQuery : false,
7895     
7896     /**
7897      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7898      */
7899     multiple : false,
7900     
7901     //private
7902     addicon : false,
7903     editicon: false,
7904     
7905     page: 0,
7906     hasQuery: false,
7907     append: false,
7908     loadNext: false,
7909     item: [],
7910     
7911     // element that contains real text value.. (when hidden is used..)
7912      
7913     // private
7914     initEvents: function(){
7915         
7916         if (!this.store) {
7917             throw "can not find store for combo";
7918         }
7919         this.store = Roo.factory(this.store, Roo.data);
7920         
7921         
7922         
7923         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
7924         
7925         
7926         if(this.hiddenName){
7927             
7928             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
7929             
7930             this.hiddenField.dom.value =
7931                 this.hiddenValue !== undefined ? this.hiddenValue :
7932                 this.value !== undefined ? this.value : '';
7933
7934             // prevent input submission
7935             this.el.dom.removeAttribute('name');
7936             this.hiddenField.dom.setAttribute('name', this.hiddenName);
7937              
7938              
7939         }
7940         //if(Roo.isGecko){
7941         //    this.el.dom.setAttribute('autocomplete', 'off');
7942         //}
7943
7944         var cls = 'x-combo-list';
7945         this.list = this.el.select('ul.dropdown-menu',true).first();
7946
7947         //this.list = new Roo.Layer({
7948         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
7949         //});
7950         
7951         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
7952         this.list.setWidth(lw);
7953         
7954         this.list.on('mouseover', this.onViewOver, this);
7955         this.list.on('mousemove', this.onViewMove, this);
7956         
7957         this.list.on('scroll', this.onViewScroll, this);
7958         
7959         /*
7960         this.list.swallowEvent('mousewheel');
7961         this.assetHeight = 0;
7962
7963         if(this.title){
7964             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
7965             this.assetHeight += this.header.getHeight();
7966         }
7967
7968         this.innerList = this.list.createChild({cls:cls+'-inner'});
7969         this.innerList.on('mouseover', this.onViewOver, this);
7970         this.innerList.on('mousemove', this.onViewMove, this);
7971         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
7972         
7973         if(this.allowBlank && !this.pageSize && !this.disableClear){
7974             this.footer = this.list.createChild({cls:cls+'-ft'});
7975             this.pageTb = new Roo.Toolbar(this.footer);
7976            
7977         }
7978         if(this.pageSize){
7979             this.footer = this.list.createChild({cls:cls+'-ft'});
7980             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
7981                     {pageSize: this.pageSize});
7982             
7983         }
7984         
7985         if (this.pageTb && this.allowBlank && !this.disableClear) {
7986             var _this = this;
7987             this.pageTb.add(new Roo.Toolbar.Fill(), {
7988                 cls: 'x-btn-icon x-btn-clear',
7989                 text: '&#160;',
7990                 handler: function()
7991                 {
7992                     _this.collapse();
7993                     _this.clearValue();
7994                     _this.onSelect(false, -1);
7995                 }
7996             });
7997         }
7998         if (this.footer) {
7999             this.assetHeight += this.footer.getHeight();
8000         }
8001         */
8002             
8003         if(!this.tpl){
8004             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8005         }
8006
8007         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8008             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8009         });
8010         //this.view.wrapEl.setDisplayed(false);
8011         this.view.on('click', this.onViewClick, this);
8012         
8013         
8014         
8015         this.store.on('beforeload', this.onBeforeLoad, this);
8016         this.store.on('load', this.onLoad, this);
8017         this.store.on('loadexception', this.onLoadException, this);
8018         /*
8019         if(this.resizable){
8020             this.resizer = new Roo.Resizable(this.list,  {
8021                pinned:true, handles:'se'
8022             });
8023             this.resizer.on('resize', function(r, w, h){
8024                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8025                 this.listWidth = w;
8026                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8027                 this.restrictHeight();
8028             }, this);
8029             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8030         }
8031         */
8032         if(!this.editable){
8033             this.editable = true;
8034             this.setEditable(false);
8035         }
8036         
8037         /*
8038         
8039         if (typeof(this.events.add.listeners) != 'undefined') {
8040             
8041             this.addicon = this.wrap.createChild(
8042                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8043        
8044             this.addicon.on('click', function(e) {
8045                 this.fireEvent('add', this);
8046             }, this);
8047         }
8048         if (typeof(this.events.edit.listeners) != 'undefined') {
8049             
8050             this.editicon = this.wrap.createChild(
8051                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8052             if (this.addicon) {
8053                 this.editicon.setStyle('margin-left', '40px');
8054             }
8055             this.editicon.on('click', function(e) {
8056                 
8057                 // we fire even  if inothing is selected..
8058                 this.fireEvent('edit', this, this.lastData );
8059                 
8060             }, this);
8061         }
8062         */
8063         
8064         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8065             "up" : function(e){
8066                 this.inKeyMode = true;
8067                 this.selectPrev();
8068             },
8069
8070             "down" : function(e){
8071                 if(!this.isExpanded()){
8072                     this.onTriggerClick();
8073                 }else{
8074                     this.inKeyMode = true;
8075                     this.selectNext();
8076                 }
8077             },
8078
8079             "enter" : function(e){
8080                 this.onViewClick();
8081                 //return true;
8082             },
8083
8084             "esc" : function(e){
8085                 this.collapse();
8086             },
8087
8088             "tab" : function(e){
8089                 this.collapse();
8090                 
8091                 if(this.fireEvent("specialkey", this, e)){
8092                     this.onViewClick(false);
8093                 }
8094                 
8095                 return true;
8096             },
8097
8098             scope : this,
8099
8100             doRelay : function(foo, bar, hname){
8101                 if(hname == 'down' || this.scope.isExpanded()){
8102                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8103                 }
8104                 return true;
8105             },
8106
8107             forceKeyDown: true
8108         });
8109         
8110         
8111         this.queryDelay = Math.max(this.queryDelay || 10,
8112                 this.mode == 'local' ? 10 : 250);
8113         
8114         
8115         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8116         
8117         if(this.typeAhead){
8118             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8119         }
8120         if(this.editable !== false){
8121             this.inputEl().on("keyup", this.onKeyUp, this);
8122         }
8123         if(this.forceSelection){
8124             this.on('blur', this.doForce, this);
8125         }
8126         
8127         if(this.multiple){
8128             this.choices = this.el.select('ul.select2-choices', true).first();
8129             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8130         }
8131     },
8132
8133     onDestroy : function(){
8134         if(this.view){
8135             this.view.setStore(null);
8136             this.view.el.removeAllListeners();
8137             this.view.el.remove();
8138             this.view.purgeListeners();
8139         }
8140         if(this.list){
8141             this.list.dom.innerHTML  = '';
8142         }
8143         if(this.store){
8144             this.store.un('beforeload', this.onBeforeLoad, this);
8145             this.store.un('load', this.onLoad, this);
8146             this.store.un('loadexception', this.onLoadException, this);
8147         }
8148         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8149     },
8150
8151     // private
8152     fireKey : function(e){
8153         if(e.isNavKeyPress() && !this.list.isVisible()){
8154             this.fireEvent("specialkey", this, e);
8155         }
8156     },
8157
8158     // private
8159     onResize: function(w, h){
8160 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8161 //        
8162 //        if(typeof w != 'number'){
8163 //            // we do not handle it!?!?
8164 //            return;
8165 //        }
8166 //        var tw = this.trigger.getWidth();
8167 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8168 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8169 //        var x = w - tw;
8170 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8171 //            
8172 //        //this.trigger.setStyle('left', x+'px');
8173 //        
8174 //        if(this.list && this.listWidth === undefined){
8175 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8176 //            this.list.setWidth(lw);
8177 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8178 //        }
8179         
8180     
8181         
8182     },
8183
8184     /**
8185      * Allow or prevent the user from directly editing the field text.  If false is passed,
8186      * the user will only be able to select from the items defined in the dropdown list.  This method
8187      * is the runtime equivalent of setting the 'editable' config option at config time.
8188      * @param {Boolean} value True to allow the user to directly edit the field text
8189      */
8190     setEditable : function(value){
8191         if(value == this.editable){
8192             return;
8193         }
8194         this.editable = value;
8195         if(!value){
8196             this.inputEl().dom.setAttribute('readOnly', true);
8197             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8198             this.inputEl().addClass('x-combo-noedit');
8199         }else{
8200             this.inputEl().dom.setAttribute('readOnly', false);
8201             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8202             this.inputEl().removeClass('x-combo-noedit');
8203         }
8204     },
8205
8206     // private
8207     
8208     onBeforeLoad : function(combo,opts){
8209         if(!this.hasFocus){
8210             return;
8211         }
8212          if (!opts.add) {
8213             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8214          }
8215         this.restrictHeight();
8216         this.selectedIndex = -1;
8217     },
8218
8219     // private
8220     onLoad : function(){
8221         
8222         this.hasQuery = false;
8223         
8224         if(!this.hasFocus){
8225             return;
8226         }
8227         
8228         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8229             this.loading.hide();
8230         }
8231         
8232         if(this.store.getCount() > 0){
8233             this.expand();
8234             this.restrictHeight();
8235             if(this.lastQuery == this.allQuery){
8236                 if(this.editable){
8237                     this.inputEl().dom.select();
8238                 }
8239                 if(!this.selectByValue(this.value, true)){
8240                     this.select(0, true);
8241                 }
8242             }else{
8243                 this.selectNext();
8244                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8245                     this.taTask.delay(this.typeAheadDelay);
8246                 }
8247             }
8248         }else{
8249             this.onEmptyResults();
8250         }
8251         
8252         //this.el.focus();
8253     },
8254     // private
8255     onLoadException : function()
8256     {
8257         this.hasQuery = false;
8258         
8259         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8260             this.loading.hide();
8261         }
8262         
8263         this.collapse();
8264         Roo.log(this.store.reader.jsonData);
8265         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8266             // fixme
8267             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8268         }
8269         
8270         
8271     },
8272     // private
8273     onTypeAhead : function(){
8274         if(this.store.getCount() > 0){
8275             var r = this.store.getAt(0);
8276             var newValue = r.data[this.displayField];
8277             var len = newValue.length;
8278             var selStart = this.getRawValue().length;
8279             
8280             if(selStart != len){
8281                 this.setRawValue(newValue);
8282                 this.selectText(selStart, newValue.length);
8283             }
8284         }
8285     },
8286
8287     // private
8288     onSelect : function(record, index){
8289         
8290         if(this.fireEvent('beforeselect', this, record, index) !== false){
8291         
8292             this.setFromData(index > -1 ? record.data : false);
8293             
8294             this.collapse();
8295             this.fireEvent('select', this, record, index);
8296         }
8297     },
8298
8299     /**
8300      * Returns the currently selected field value or empty string if no value is set.
8301      * @return {String} value The selected value
8302      */
8303     getValue : function(){
8304         
8305         if(this.multiple){
8306             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8307         }
8308         
8309         if(this.valueField){
8310             return typeof this.value != 'undefined' ? this.value : '';
8311         }else{
8312             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8313         }
8314     },
8315
8316     /**
8317      * Clears any text/value currently set in the field
8318      */
8319     clearValue : function(){
8320         if(this.hiddenField){
8321             this.hiddenField.dom.value = '';
8322         }
8323         this.value = '';
8324         this.setRawValue('');
8325         this.lastSelectionText = '';
8326         
8327     },
8328
8329     /**
8330      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8331      * will be displayed in the field.  If the value does not match the data value of an existing item,
8332      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8333      * Otherwise the field will be blank (although the value will still be set).
8334      * @param {String} value The value to match
8335      */
8336     setValue : function(v){
8337         if(this.multiple){
8338             this.syncValue();
8339             return;
8340         }
8341         
8342         var text = v;
8343         if(this.valueField){
8344             var r = this.findRecord(this.valueField, v);
8345             if(r){
8346                 text = r.data[this.displayField];
8347             }else if(this.valueNotFoundText !== undefined){
8348                 text = this.valueNotFoundText;
8349             }
8350         }
8351         this.lastSelectionText = text;
8352         if(this.hiddenField){
8353             this.hiddenField.dom.value = v;
8354         }
8355         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8356         this.value = v;
8357     },
8358     /**
8359      * @property {Object} the last set data for the element
8360      */
8361     
8362     lastData : false,
8363     /**
8364      * Sets the value of the field based on a object which is related to the record format for the store.
8365      * @param {Object} value the value to set as. or false on reset?
8366      */
8367     setFromData : function(o){
8368         
8369         if(this.multiple){
8370             this.addItem(o);
8371             return;
8372         }
8373             
8374         var dv = ''; // display value
8375         var vv = ''; // value value..
8376         this.lastData = o;
8377         if (this.displayField) {
8378             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8379         } else {
8380             // this is an error condition!!!
8381             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8382         }
8383         
8384         if(this.valueField){
8385             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8386         }
8387         
8388         if(this.hiddenField){
8389             this.hiddenField.dom.value = vv;
8390             
8391             this.lastSelectionText = dv;
8392             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8393             this.value = vv;
8394             return;
8395         }
8396         // no hidden field.. - we store the value in 'value', but still display
8397         // display field!!!!
8398         this.lastSelectionText = dv;
8399         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8400         this.value = vv;
8401         
8402         
8403     },
8404     // private
8405     reset : function(){
8406         // overridden so that last data is reset..
8407         this.setValue(this.originalValue);
8408         this.clearInvalid();
8409         this.lastData = false;
8410         if (this.view) {
8411             this.view.clearSelections();
8412         }
8413     },
8414     // private
8415     findRecord : function(prop, value){
8416         var record;
8417         if(this.store.getCount() > 0){
8418             this.store.each(function(r){
8419                 if(r.data[prop] == value){
8420                     record = r;
8421                     return false;
8422                 }
8423                 return true;
8424             });
8425         }
8426         return record;
8427     },
8428     
8429     getName: function()
8430     {
8431         // returns hidden if it's set..
8432         if (!this.rendered) {return ''};
8433         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8434         
8435     },
8436     // private
8437     onViewMove : function(e, t){
8438         this.inKeyMode = false;
8439     },
8440
8441     // private
8442     onViewOver : function(e, t){
8443         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8444             return;
8445         }
8446         var item = this.view.findItemFromChild(t);
8447         if(item){
8448             var index = this.view.indexOf(item);
8449             this.select(index, false);
8450         }
8451     },
8452
8453     // private
8454     onViewClick : function(doFocus)
8455     {
8456         var index = this.view.getSelectedIndexes()[0];
8457         var r = this.store.getAt(index);
8458         if(r){
8459             this.onSelect(r, index);
8460         }
8461         if(doFocus !== false && !this.blockFocus){
8462             this.inputEl().focus();
8463         }
8464     },
8465
8466     // private
8467     restrictHeight : function(){
8468         //this.innerList.dom.style.height = '';
8469         //var inner = this.innerList.dom;
8470         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8471         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8472         //this.list.beginUpdate();
8473         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8474         this.list.alignTo(this.inputEl(), this.listAlign);
8475         //this.list.endUpdate();
8476     },
8477
8478     // private
8479     onEmptyResults : function(){
8480         this.collapse();
8481     },
8482
8483     /**
8484      * Returns true if the dropdown list is expanded, else false.
8485      */
8486     isExpanded : function(){
8487         return this.list.isVisible();
8488     },
8489
8490     /**
8491      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8492      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8493      * @param {String} value The data value of the item to select
8494      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8495      * selected item if it is not currently in view (defaults to true)
8496      * @return {Boolean} True if the value matched an item in the list, else false
8497      */
8498     selectByValue : function(v, scrollIntoView){
8499         if(v !== undefined && v !== null){
8500             var r = this.findRecord(this.valueField || this.displayField, v);
8501             if(r){
8502                 this.select(this.store.indexOf(r), scrollIntoView);
8503                 return true;
8504             }
8505         }
8506         return false;
8507     },
8508
8509     /**
8510      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8511      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8512      * @param {Number} index The zero-based index of the list item to select
8513      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8514      * selected item if it is not currently in view (defaults to true)
8515      */
8516     select : function(index, scrollIntoView){
8517         this.selectedIndex = index;
8518         this.view.select(index);
8519         if(scrollIntoView !== false){
8520             var el = this.view.getNode(index);
8521             if(el){
8522                 //this.innerList.scrollChildIntoView(el, false);
8523                 
8524             }
8525         }
8526     },
8527
8528     // private
8529     selectNext : function(){
8530         var ct = this.store.getCount();
8531         if(ct > 0){
8532             if(this.selectedIndex == -1){
8533                 this.select(0);
8534             }else if(this.selectedIndex < ct-1){
8535                 this.select(this.selectedIndex+1);
8536             }
8537         }
8538     },
8539
8540     // private
8541     selectPrev : function(){
8542         var ct = this.store.getCount();
8543         if(ct > 0){
8544             if(this.selectedIndex == -1){
8545                 this.select(0);
8546             }else if(this.selectedIndex != 0){
8547                 this.select(this.selectedIndex-1);
8548             }
8549         }
8550     },
8551
8552     // private
8553     onKeyUp : function(e){
8554         if(this.editable !== false && !e.isSpecialKey()){
8555             this.lastKey = e.getKey();
8556             this.dqTask.delay(this.queryDelay);
8557         }
8558     },
8559
8560     // private
8561     validateBlur : function(){
8562         return !this.list || !this.list.isVisible();   
8563     },
8564
8565     // private
8566     initQuery : function(){
8567         this.doQuery(this.getRawValue());
8568     },
8569
8570     // private
8571     doForce : function(){
8572         if(this.el.dom.value.length > 0){
8573             this.el.dom.value =
8574                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8575              
8576         }
8577     },
8578
8579     /**
8580      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8581      * query allowing the query action to be canceled if needed.
8582      * @param {String} query The SQL query to execute
8583      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8584      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8585      * saved in the current store (defaults to false)
8586      */
8587     doQuery : function(q, forceAll){
8588         
8589         if(q === undefined || q === null){
8590             q = '';
8591         }
8592         var qe = {
8593             query: q,
8594             forceAll: forceAll,
8595             combo: this,
8596             cancel:false
8597         };
8598         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8599             return false;
8600         }
8601         q = qe.query;
8602         
8603         forceAll = qe.forceAll;
8604         if(forceAll === true || (q.length >= this.minChars)){
8605             
8606             this.hasQuery = true;
8607             
8608             if(this.lastQuery != q || this.alwaysQuery){
8609                 this.lastQuery = q;
8610                 if(this.mode == 'local'){
8611                     this.selectedIndex = -1;
8612                     if(forceAll){
8613                         this.store.clearFilter();
8614                     }else{
8615                         this.store.filter(this.displayField, q);
8616                     }
8617                     this.onLoad();
8618                 }else{
8619                     this.store.baseParams[this.queryParam] = q;
8620                     
8621                     var options = {params : this.getParams(q)};
8622                     
8623                     if(this.loadNext){
8624                         options.add = true;
8625                         options.params.start = this.page * this.pageSize;
8626                     }
8627                     
8628                     this.store.load(options);
8629                     this.expand();
8630                 }
8631             }else{
8632                 this.selectedIndex = -1;
8633                 this.onLoad();   
8634             }
8635         }
8636         
8637         this.loadNext = false;
8638     },
8639
8640     // private
8641     getParams : function(q){
8642         var p = {};
8643         //p[this.queryParam] = q;
8644         
8645         if(this.pageSize){
8646             p.start = 0;
8647             p.limit = this.pageSize;
8648         }
8649         return p;
8650     },
8651
8652     /**
8653      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8654      */
8655     collapse : function(){
8656         if(!this.isExpanded()){
8657             return;
8658         }
8659         
8660         this.list.hide();
8661         Roo.get(document).un('mousedown', this.collapseIf, this);
8662         Roo.get(document).un('mousewheel', this.collapseIf, this);
8663         if (!this.editable) {
8664             Roo.get(document).un('keydown', this.listKeyPress, this);
8665         }
8666         this.fireEvent('collapse', this);
8667     },
8668
8669     // private
8670     collapseIf : function(e){
8671         var in_combo  = e.within(this.el);
8672         var in_list =  e.within(this.list);
8673         
8674         if (in_combo || in_list) {
8675             //e.stopPropagation();
8676             return;
8677         }
8678
8679         this.collapse();
8680         
8681     },
8682
8683     /**
8684      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8685      */
8686     expand : function(){
8687        
8688         if(this.isExpanded() || !this.hasFocus){
8689             return;
8690         }
8691          Roo.log('expand');
8692         this.list.alignTo(this.inputEl(), this.listAlign);
8693         this.list.show();
8694         Roo.get(document).on('mousedown', this.collapseIf, this);
8695         Roo.get(document).on('mousewheel', this.collapseIf, this);
8696         if (!this.editable) {
8697             Roo.get(document).on('keydown', this.listKeyPress, this);
8698         }
8699         
8700         this.fireEvent('expand', this);
8701     },
8702
8703     // private
8704     // Implements the default empty TriggerField.onTriggerClick function
8705     onTriggerClick : function()
8706     {
8707         Roo.log('trigger click');
8708         
8709         if(this.disabled){
8710             return;
8711         }
8712         
8713         this.page = 0;
8714         this.loadNext = false;
8715         
8716         if(this.isExpanded()){
8717             this.collapse();
8718             if (!this.blockFocus) {
8719                 this.inputEl().focus();
8720             }
8721             
8722         }else {
8723             this.hasFocus = true;
8724             if(this.triggerAction == 'all') {
8725                 this.doQuery(this.allQuery, true);
8726             } else {
8727                 this.doQuery(this.getRawValue());
8728             }
8729             if (!this.blockFocus) {
8730                 this.inputEl().focus();
8731             }
8732         }
8733     },
8734     listKeyPress : function(e)
8735     {
8736         //Roo.log('listkeypress');
8737         // scroll to first matching element based on key pres..
8738         if (e.isSpecialKey()) {
8739             return false;
8740         }
8741         var k = String.fromCharCode(e.getKey()).toUpperCase();
8742         //Roo.log(k);
8743         var match  = false;
8744         var csel = this.view.getSelectedNodes();
8745         var cselitem = false;
8746         if (csel.length) {
8747             var ix = this.view.indexOf(csel[0]);
8748             cselitem  = this.store.getAt(ix);
8749             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8750                 cselitem = false;
8751             }
8752             
8753         }
8754         
8755         this.store.each(function(v) { 
8756             if (cselitem) {
8757                 // start at existing selection.
8758                 if (cselitem.id == v.id) {
8759                     cselitem = false;
8760                 }
8761                 return true;
8762             }
8763                 
8764             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8765                 match = this.store.indexOf(v);
8766                 return false;
8767             }
8768             return true;
8769         }, this);
8770         
8771         if (match === false) {
8772             return true; // no more action?
8773         }
8774         // scroll to?
8775         this.view.select(match);
8776         var sn = Roo.get(this.view.getSelectedNodes()[0])
8777         //sn.scrollIntoView(sn.dom.parentNode, false);
8778     },
8779     
8780     onViewScroll : function(e, t){
8781         
8782         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8783             return;
8784         }
8785         
8786         this.hasQuery = true;
8787         
8788         this.loading = this.list.select('.loading', true).first();
8789         
8790         if(this.loading === null){
8791             this.list.createChild({
8792                 tag: 'div',
8793                 cls: 'loading select2-more-results select2-active',
8794                 html: 'Loading more results...'
8795             })
8796             
8797             this.loading = this.list.select('.loading', true).first();
8798             
8799             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8800             
8801             this.loading.hide();
8802         }
8803         
8804         this.loading.show();
8805         
8806         var _combo = this;
8807         
8808         this.page++;
8809         this.loadNext = true;
8810         
8811         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8812         
8813         return;
8814     },
8815     
8816     addItem : function(o)
8817     {   
8818         var dv = ''; // display value
8819         
8820         if (this.displayField) {
8821             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8822         } else {
8823             // this is an error condition!!!
8824             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8825         }
8826         
8827         if(!dv.length){
8828             return;
8829         }
8830         
8831         var choice = this.choices.createChild({
8832             tag: 'li',
8833             cls: 'select2-search-choice',
8834             cn: [
8835                 {
8836                     tag: 'div',
8837                     html: dv
8838                 },
8839                 {
8840                     tag: 'a',
8841                     href: '#',
8842                     cls: 'select2-search-choice-close',
8843                     tabindex: '-1'
8844                 }
8845             ]
8846             
8847         }, this.searchField);
8848         
8849         var close = choice.select('a.select2-search-choice-close', true).first()
8850         
8851         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8852         
8853         this.item.push(o);
8854         this.lastData = o;
8855         
8856         this.syncValue();
8857         
8858         this.inputEl().dom.value = '';
8859         
8860     },
8861     
8862     onRemoveItem : function(e, _self, o)
8863     {
8864         Roo.log('remove item');
8865         var index = this.item.indexOf(o.data) * 1;
8866         
8867         if( index < 0){
8868             Roo.log('not this item?!');
8869             return;
8870         }
8871         
8872         this.item.splice(index, 1);
8873         o.item.remove();
8874         
8875         this.syncValue();
8876         
8877         this.fireEvent('remove', this);
8878         
8879     },
8880     
8881     syncValue : function()
8882     {
8883         if(!this.item.length){
8884             this.clearValue();
8885             return;
8886         }
8887             
8888         var value = [];
8889         var _this = this;
8890         Roo.each(this.item, function(i){
8891             if(_this.valueField){
8892                 value.push(i[_this.valueField]);
8893                 return;
8894             }
8895
8896             value.push(i);
8897         });
8898
8899         this.value = value.join(',');
8900
8901         if(this.hiddenField){
8902             this.hiddenField.dom.value = this.value;
8903         }
8904     }
8905
8906     /** 
8907     * @cfg {Boolean} grow 
8908     * @hide 
8909     */
8910     /** 
8911     * @cfg {Number} growMin 
8912     * @hide 
8913     */
8914     /** 
8915     * @cfg {Number} growMax 
8916     * @hide 
8917     */
8918     /**
8919      * @hide
8920      * @method autoSize
8921      */
8922 });
8923 /*
8924  * Based on:
8925  * Ext JS Library 1.1.1
8926  * Copyright(c) 2006-2007, Ext JS, LLC.
8927  *
8928  * Originally Released Under LGPL - original licence link has changed is not relivant.
8929  *
8930  * Fork - LGPL
8931  * <script type="text/javascript">
8932  */
8933
8934 /**
8935  * @class Roo.View
8936  * @extends Roo.util.Observable
8937  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8938  * This class also supports single and multi selection modes. <br>
8939  * Create a data model bound view:
8940  <pre><code>
8941  var store = new Roo.data.Store(...);
8942
8943  var view = new Roo.View({
8944     el : "my-element",
8945     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8946  
8947     singleSelect: true,
8948     selectedClass: "ydataview-selected",
8949     store: store
8950  });
8951
8952  // listen for node click?
8953  view.on("click", function(vw, index, node, e){
8954  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8955  });
8956
8957  // load XML data
8958  dataModel.load("foobar.xml");
8959  </code></pre>
8960  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8961  * <br><br>
8962  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8963  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8964  * 
8965  * Note: old style constructor is still suported (container, template, config)
8966  * 
8967  * @constructor
8968  * Create a new View
8969  * @param {Object} config The config object
8970  * 
8971  */
8972 Roo.View = function(config, depreciated_tpl, depreciated_config){
8973     
8974     if (typeof(depreciated_tpl) == 'undefined') {
8975         // new way.. - universal constructor.
8976         Roo.apply(this, config);
8977         this.el  = Roo.get(this.el);
8978     } else {
8979         // old format..
8980         this.el  = Roo.get(config);
8981         this.tpl = depreciated_tpl;
8982         Roo.apply(this, depreciated_config);
8983     }
8984     this.wrapEl  = this.el.wrap().wrap();
8985     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8986     
8987     
8988     if(typeof(this.tpl) == "string"){
8989         this.tpl = new Roo.Template(this.tpl);
8990     } else {
8991         // support xtype ctors..
8992         this.tpl = new Roo.factory(this.tpl, Roo);
8993     }
8994     
8995     
8996     this.tpl.compile();
8997    
8998   
8999     
9000      
9001     /** @private */
9002     this.addEvents({
9003         /**
9004          * @event beforeclick
9005          * Fires before a click is processed. Returns false to cancel the default action.
9006          * @param {Roo.View} this
9007          * @param {Number} index The index of the target node
9008          * @param {HTMLElement} node The target node
9009          * @param {Roo.EventObject} e The raw event object
9010          */
9011             "beforeclick" : true,
9012         /**
9013          * @event click
9014          * Fires when a template node is clicked.
9015          * @param {Roo.View} this
9016          * @param {Number} index The index of the target node
9017          * @param {HTMLElement} node The target node
9018          * @param {Roo.EventObject} e The raw event object
9019          */
9020             "click" : true,
9021         /**
9022          * @event dblclick
9023          * Fires when a template node is double clicked.
9024          * @param {Roo.View} this
9025          * @param {Number} index The index of the target node
9026          * @param {HTMLElement} node The target node
9027          * @param {Roo.EventObject} e The raw event object
9028          */
9029             "dblclick" : true,
9030         /**
9031          * @event contextmenu
9032          * Fires when a template node is right clicked.
9033          * @param {Roo.View} this
9034          * @param {Number} index The index of the target node
9035          * @param {HTMLElement} node The target node
9036          * @param {Roo.EventObject} e The raw event object
9037          */
9038             "contextmenu" : true,
9039         /**
9040          * @event selectionchange
9041          * Fires when the selected nodes change.
9042          * @param {Roo.View} this
9043          * @param {Array} selections Array of the selected nodes
9044          */
9045             "selectionchange" : true,
9046     
9047         /**
9048          * @event beforeselect
9049          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9050          * @param {Roo.View} this
9051          * @param {HTMLElement} node The node to be selected
9052          * @param {Array} selections Array of currently selected nodes
9053          */
9054             "beforeselect" : true,
9055         /**
9056          * @event preparedata
9057          * Fires on every row to render, to allow you to change the data.
9058          * @param {Roo.View} this
9059          * @param {Object} data to be rendered (change this)
9060          */
9061           "preparedata" : true
9062           
9063           
9064         });
9065
9066
9067
9068     this.el.on({
9069         "click": this.onClick,
9070         "dblclick": this.onDblClick,
9071         "contextmenu": this.onContextMenu,
9072         scope:this
9073     });
9074
9075     this.selections = [];
9076     this.nodes = [];
9077     this.cmp = new Roo.CompositeElementLite([]);
9078     if(this.store){
9079         this.store = Roo.factory(this.store, Roo.data);
9080         this.setStore(this.store, true);
9081     }
9082     
9083     if ( this.footer && this.footer.xtype) {
9084            
9085          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9086         
9087         this.footer.dataSource = this.store
9088         this.footer.container = fctr;
9089         this.footer = Roo.factory(this.footer, Roo);
9090         fctr.insertFirst(this.el);
9091         
9092         // this is a bit insane - as the paging toolbar seems to detach the el..
9093 //        dom.parentNode.parentNode.parentNode
9094          // they get detached?
9095     }
9096     
9097     
9098     Roo.View.superclass.constructor.call(this);
9099     
9100     
9101 };
9102
9103 Roo.extend(Roo.View, Roo.util.Observable, {
9104     
9105      /**
9106      * @cfg {Roo.data.Store} store Data store to load data from.
9107      */
9108     store : false,
9109     
9110     /**
9111      * @cfg {String|Roo.Element} el The container element.
9112      */
9113     el : '',
9114     
9115     /**
9116      * @cfg {String|Roo.Template} tpl The template used by this View 
9117      */
9118     tpl : false,
9119     /**
9120      * @cfg {String} dataName the named area of the template to use as the data area
9121      *                          Works with domtemplates roo-name="name"
9122      */
9123     dataName: false,
9124     /**
9125      * @cfg {String} selectedClass The css class to add to selected nodes
9126      */
9127     selectedClass : "x-view-selected",
9128      /**
9129      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9130      */
9131     emptyText : "",
9132     
9133     /**
9134      * @cfg {String} text to display on mask (default Loading)
9135      */
9136     mask : false,
9137     /**
9138      * @cfg {Boolean} multiSelect Allow multiple selection
9139      */
9140     multiSelect : false,
9141     /**
9142      * @cfg {Boolean} singleSelect Allow single selection
9143      */
9144     singleSelect:  false,
9145     
9146     /**
9147      * @cfg {Boolean} toggleSelect - selecting 
9148      */
9149     toggleSelect : false,
9150     
9151     /**
9152      * Returns the element this view is bound to.
9153      * @return {Roo.Element}
9154      */
9155     getEl : function(){
9156         return this.wrapEl;
9157     },
9158     
9159     
9160
9161     /**
9162      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9163      */
9164     refresh : function(){
9165         Roo.log('refresh');
9166         var t = this.tpl;
9167         
9168         // if we are using something like 'domtemplate', then
9169         // the what gets used is:
9170         // t.applySubtemplate(NAME, data, wrapping data..)
9171         // the outer template then get' applied with
9172         //     the store 'extra data'
9173         // and the body get's added to the
9174         //      roo-name="data" node?
9175         //      <span class='roo-tpl-{name}'></span> ?????
9176         
9177         
9178         
9179         this.clearSelections();
9180         this.el.update("");
9181         var html = [];
9182         var records = this.store.getRange();
9183         if(records.length < 1) {
9184             
9185             // is this valid??  = should it render a template??
9186             
9187             this.el.update(this.emptyText);
9188             return;
9189         }
9190         var el = this.el;
9191         if (this.dataName) {
9192             this.el.update(t.apply(this.store.meta)); //????
9193             el = this.el.child('.roo-tpl-' + this.dataName);
9194         }
9195         
9196         for(var i = 0, len = records.length; i < len; i++){
9197             var data = this.prepareData(records[i].data, i, records[i]);
9198             this.fireEvent("preparedata", this, data, i, records[i]);
9199             html[html.length] = Roo.util.Format.trim(
9200                 this.dataName ?
9201                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9202                     t.apply(data)
9203             );
9204         }
9205         
9206         
9207         
9208         el.update(html.join(""));
9209         this.nodes = el.dom.childNodes;
9210         this.updateIndexes(0);
9211     },
9212     
9213
9214     /**
9215      * Function to override to reformat the data that is sent to
9216      * the template for each node.
9217      * DEPRICATED - use the preparedata event handler.
9218      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9219      * a JSON object for an UpdateManager bound view).
9220      */
9221     prepareData : function(data, index, record)
9222     {
9223         this.fireEvent("preparedata", this, data, index, record);
9224         return data;
9225     },
9226
9227     onUpdate : function(ds, record){
9228          Roo.log('on update');   
9229         this.clearSelections();
9230         var index = this.store.indexOf(record);
9231         var n = this.nodes[index];
9232         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9233         n.parentNode.removeChild(n);
9234         this.updateIndexes(index, index);
9235     },
9236
9237     
9238     
9239 // --------- FIXME     
9240     onAdd : function(ds, records, index)
9241     {
9242         Roo.log(['on Add', ds, records, index] );        
9243         this.clearSelections();
9244         if(this.nodes.length == 0){
9245             this.refresh();
9246             return;
9247         }
9248         var n = this.nodes[index];
9249         for(var i = 0, len = records.length; i < len; i++){
9250             var d = this.prepareData(records[i].data, i, records[i]);
9251             if(n){
9252                 this.tpl.insertBefore(n, d);
9253             }else{
9254                 
9255                 this.tpl.append(this.el, d);
9256             }
9257         }
9258         this.updateIndexes(index);
9259     },
9260
9261     onRemove : function(ds, record, index){
9262         Roo.log('onRemove');
9263         this.clearSelections();
9264         var el = this.dataName  ?
9265             this.el.child('.roo-tpl-' + this.dataName) :
9266             this.el; 
9267         
9268         el.dom.removeChild(this.nodes[index]);
9269         this.updateIndexes(index);
9270     },
9271
9272     /**
9273      * Refresh an individual node.
9274      * @param {Number} index
9275      */
9276     refreshNode : function(index){
9277         this.onUpdate(this.store, this.store.getAt(index));
9278     },
9279
9280     updateIndexes : function(startIndex, endIndex){
9281         var ns = this.nodes;
9282         startIndex = startIndex || 0;
9283         endIndex = endIndex || ns.length - 1;
9284         for(var i = startIndex; i <= endIndex; i++){
9285             ns[i].nodeIndex = i;
9286         }
9287     },
9288
9289     /**
9290      * Changes the data store this view uses and refresh the view.
9291      * @param {Store} store
9292      */
9293     setStore : function(store, initial){
9294         if(!initial && this.store){
9295             this.store.un("datachanged", this.refresh);
9296             this.store.un("add", this.onAdd);
9297             this.store.un("remove", this.onRemove);
9298             this.store.un("update", this.onUpdate);
9299             this.store.un("clear", this.refresh);
9300             this.store.un("beforeload", this.onBeforeLoad);
9301             this.store.un("load", this.onLoad);
9302             this.store.un("loadexception", this.onLoad);
9303         }
9304         if(store){
9305           
9306             store.on("datachanged", this.refresh, this);
9307             store.on("add", this.onAdd, this);
9308             store.on("remove", this.onRemove, this);
9309             store.on("update", this.onUpdate, this);
9310             store.on("clear", this.refresh, this);
9311             store.on("beforeload", this.onBeforeLoad, this);
9312             store.on("load", this.onLoad, this);
9313             store.on("loadexception", this.onLoad, this);
9314         }
9315         
9316         if(store){
9317             this.refresh();
9318         }
9319     },
9320     /**
9321      * onbeforeLoad - masks the loading area.
9322      *
9323      */
9324     onBeforeLoad : function(store,opts)
9325     {
9326          Roo.log('onBeforeLoad');   
9327         if (!opts.add) {
9328             this.el.update("");
9329         }
9330         this.el.mask(this.mask ? this.mask : "Loading" ); 
9331     },
9332     onLoad : function ()
9333     {
9334         this.el.unmask();
9335     },
9336     
9337
9338     /**
9339      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9340      * @param {HTMLElement} node
9341      * @return {HTMLElement} The template node
9342      */
9343     findItemFromChild : function(node){
9344         var el = this.dataName  ?
9345             this.el.child('.roo-tpl-' + this.dataName,true) :
9346             this.el.dom; 
9347         
9348         if(!node || node.parentNode == el){
9349                     return node;
9350             }
9351             var p = node.parentNode;
9352             while(p && p != el){
9353             if(p.parentNode == el){
9354                 return p;
9355             }
9356             p = p.parentNode;
9357         }
9358             return null;
9359     },
9360
9361     /** @ignore */
9362     onClick : function(e){
9363         var item = this.findItemFromChild(e.getTarget());
9364         if(item){
9365             var index = this.indexOf(item);
9366             if(this.onItemClick(item, index, e) !== false){
9367                 this.fireEvent("click", this, index, item, e);
9368             }
9369         }else{
9370             this.clearSelections();
9371         }
9372     },
9373
9374     /** @ignore */
9375     onContextMenu : function(e){
9376         var item = this.findItemFromChild(e.getTarget());
9377         if(item){
9378             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9379         }
9380     },
9381
9382     /** @ignore */
9383     onDblClick : function(e){
9384         var item = this.findItemFromChild(e.getTarget());
9385         if(item){
9386             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9387         }
9388     },
9389
9390     onItemClick : function(item, index, e)
9391     {
9392         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9393             return false;
9394         }
9395         if (this.toggleSelect) {
9396             var m = this.isSelected(item) ? 'unselect' : 'select';
9397             Roo.log(m);
9398             var _t = this;
9399             _t[m](item, true, false);
9400             return true;
9401         }
9402         if(this.multiSelect || this.singleSelect){
9403             if(this.multiSelect && e.shiftKey && this.lastSelection){
9404                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9405             }else{
9406                 this.select(item, this.multiSelect && e.ctrlKey);
9407                 this.lastSelection = item;
9408             }
9409             e.preventDefault();
9410         }
9411         return true;
9412     },
9413
9414     /**
9415      * Get the number of selected nodes.
9416      * @return {Number}
9417      */
9418     getSelectionCount : function(){
9419         return this.selections.length;
9420     },
9421
9422     /**
9423      * Get the currently selected nodes.
9424      * @return {Array} An array of HTMLElements
9425      */
9426     getSelectedNodes : function(){
9427         return this.selections;
9428     },
9429
9430     /**
9431      * Get the indexes of the selected nodes.
9432      * @return {Array}
9433      */
9434     getSelectedIndexes : function(){
9435         var indexes = [], s = this.selections;
9436         for(var i = 0, len = s.length; i < len; i++){
9437             indexes.push(s[i].nodeIndex);
9438         }
9439         return indexes;
9440     },
9441
9442     /**
9443      * Clear all selections
9444      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9445      */
9446     clearSelections : function(suppressEvent){
9447         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9448             this.cmp.elements = this.selections;
9449             this.cmp.removeClass(this.selectedClass);
9450             this.selections = [];
9451             if(!suppressEvent){
9452                 this.fireEvent("selectionchange", this, this.selections);
9453             }
9454         }
9455     },
9456
9457     /**
9458      * Returns true if the passed node is selected
9459      * @param {HTMLElement/Number} node The node or node index
9460      * @return {Boolean}
9461      */
9462     isSelected : function(node){
9463         var s = this.selections;
9464         if(s.length < 1){
9465             return false;
9466         }
9467         node = this.getNode(node);
9468         return s.indexOf(node) !== -1;
9469     },
9470
9471     /**
9472      * Selects nodes.
9473      * @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
9474      * @param {Boolean} keepExisting (optional) true to keep existing selections
9475      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9476      */
9477     select : function(nodeInfo, keepExisting, suppressEvent){
9478         if(nodeInfo instanceof Array){
9479             if(!keepExisting){
9480                 this.clearSelections(true);
9481             }
9482             for(var i = 0, len = nodeInfo.length; i < len; i++){
9483                 this.select(nodeInfo[i], true, true);
9484             }
9485             return;
9486         } 
9487         var node = this.getNode(nodeInfo);
9488         if(!node || this.isSelected(node)){
9489             return; // already selected.
9490         }
9491         if(!keepExisting){
9492             this.clearSelections(true);
9493         }
9494         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9495             Roo.fly(node).addClass(this.selectedClass);
9496             this.selections.push(node);
9497             if(!suppressEvent){
9498                 this.fireEvent("selectionchange", this, this.selections);
9499             }
9500         }
9501         
9502         
9503     },
9504       /**
9505      * Unselects nodes.
9506      * @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
9507      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9508      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9509      */
9510     unselect : function(nodeInfo, keepExisting, suppressEvent)
9511     {
9512         if(nodeInfo instanceof Array){
9513             Roo.each(this.selections, function(s) {
9514                 this.unselect(s, nodeInfo);
9515             }, this);
9516             return;
9517         }
9518         var node = this.getNode(nodeInfo);
9519         if(!node || !this.isSelected(node)){
9520             Roo.log("not selected");
9521             return; // not selected.
9522         }
9523         // fireevent???
9524         var ns = [];
9525         Roo.each(this.selections, function(s) {
9526             if (s == node ) {
9527                 Roo.fly(node).removeClass(this.selectedClass);
9528
9529                 return;
9530             }
9531             ns.push(s);
9532         },this);
9533         
9534         this.selections= ns;
9535         this.fireEvent("selectionchange", this, this.selections);
9536     },
9537
9538     /**
9539      * Gets a template node.
9540      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9541      * @return {HTMLElement} The node or null if it wasn't found
9542      */
9543     getNode : function(nodeInfo){
9544         if(typeof nodeInfo == "string"){
9545             return document.getElementById(nodeInfo);
9546         }else if(typeof nodeInfo == "number"){
9547             return this.nodes[nodeInfo];
9548         }
9549         return nodeInfo;
9550     },
9551
9552     /**
9553      * Gets a range template nodes.
9554      * @param {Number} startIndex
9555      * @param {Number} endIndex
9556      * @return {Array} An array of nodes
9557      */
9558     getNodes : function(start, end){
9559         var ns = this.nodes;
9560         start = start || 0;
9561         end = typeof end == "undefined" ? ns.length - 1 : end;
9562         var nodes = [];
9563         if(start <= end){
9564             for(var i = start; i <= end; i++){
9565                 nodes.push(ns[i]);
9566             }
9567         } else{
9568             for(var i = start; i >= end; i--){
9569                 nodes.push(ns[i]);
9570             }
9571         }
9572         return nodes;
9573     },
9574
9575     /**
9576      * Finds the index of the passed node
9577      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9578      * @return {Number} The index of the node or -1
9579      */
9580     indexOf : function(node){
9581         node = this.getNode(node);
9582         if(typeof node.nodeIndex == "number"){
9583             return node.nodeIndex;
9584         }
9585         var ns = this.nodes;
9586         for(var i = 0, len = ns.length; i < len; i++){
9587             if(ns[i] == node){
9588                 return i;
9589             }
9590         }
9591         return -1;
9592     }
9593 });
9594 /*
9595  * - LGPL
9596  *
9597  * based on jquery fullcalendar
9598  * 
9599  */
9600
9601 Roo.bootstrap = Roo.bootstrap || {};
9602 /**
9603  * @class Roo.bootstrap.Calendar
9604  * @extends Roo.bootstrap.Component
9605  * Bootstrap Calendar class
9606  * @cfg {Boolean} loadMask (true|false) default false
9607     
9608  * @constructor
9609  * Create a new Container
9610  * @param {Object} config The config object
9611  */
9612
9613
9614
9615 Roo.bootstrap.Calendar = function(config){
9616     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9617      this.addEvents({
9618         /**
9619              * @event select
9620              * Fires when a date is selected
9621              * @param {DatePicker} this
9622              * @param {Date} date The selected date
9623              */
9624         'select': true,
9625         /**
9626              * @event monthchange
9627              * Fires when the displayed month changes 
9628              * @param {DatePicker} this
9629              * @param {Date} date The selected month
9630              */
9631         'monthchange': true,
9632         /**
9633              * @event evententer
9634              * Fires when mouse over an event
9635              * @param {Calendar} this
9636              * @param {event} Event
9637              */
9638         'evententer': true,
9639         /**
9640              * @event eventleave
9641              * Fires when the mouse leaves an
9642              * @param {Calendar} this
9643              * @param {event}
9644              */
9645         'eventleave': true,
9646         /**
9647              * @event eventclick
9648              * Fires when the mouse click an
9649              * @param {Calendar} this
9650              * @param {event}
9651              */
9652         'eventclick': true
9653         
9654     });
9655
9656 };
9657
9658 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9659     
9660      /**
9661      * @cfg {Number} startDay
9662      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9663      */
9664     startDay : 0,
9665     
9666     loadMask : false,
9667       
9668     getAutoCreate : function(){
9669         
9670         
9671         var fc_button = function(name, corner, style, content ) {
9672             return Roo.apply({},{
9673                 tag : 'span',
9674                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9675                          (corner.length ?
9676                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9677                             ''
9678                         ),
9679                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9680                 unselectable: 'on'
9681             });
9682         };
9683         
9684         var header = {
9685             tag : 'table',
9686             cls : 'fc-header',
9687             style : 'width:100%',
9688             cn : [
9689                 {
9690                     tag: 'tr',
9691                     cn : [
9692                         {
9693                             tag : 'td',
9694                             cls : 'fc-header-left',
9695                             cn : [
9696                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9697                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9698                                 { tag: 'span', cls: 'fc-header-space' },
9699                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9700                                 
9701                                 
9702                             ]
9703                         },
9704                         
9705                         {
9706                             tag : 'td',
9707                             cls : 'fc-header-center',
9708                             cn : [
9709                                 {
9710                                     tag: 'span',
9711                                     cls: 'fc-header-title',
9712                                     cn : {
9713                                         tag: 'H2',
9714                                         html : 'month / year'
9715                                     }
9716                                 }
9717                                 
9718                             ]
9719                         },
9720                         {
9721                             tag : 'td',
9722                             cls : 'fc-header-right',
9723                             cn : [
9724                           /*      fc_button('month', 'left', '', 'month' ),
9725                                 fc_button('week', '', '', 'week' ),
9726                                 fc_button('day', 'right', '', 'day' )
9727                             */    
9728                                 
9729                             ]
9730                         }
9731                         
9732                     ]
9733                 }
9734             ]
9735         };
9736         
9737        
9738         var cal_heads = function() {
9739             var ret = [];
9740             // fixme - handle this.
9741             
9742             for (var i =0; i < Date.dayNames.length; i++) {
9743                 var d = Date.dayNames[i];
9744                 ret.push({
9745                     tag: 'th',
9746                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9747                     html : d.substring(0,3)
9748                 });
9749                 
9750             }
9751             ret[0].cls += ' fc-first';
9752             ret[6].cls += ' fc-last';
9753             return ret;
9754         };
9755         var cal_cell = function(n) {
9756             return  {
9757                 tag: 'td',
9758                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9759                 cn : [
9760                     {
9761                         cn : [
9762                             {
9763                                 cls: 'fc-day-number',
9764                                 html: 'D'
9765                             },
9766                             {
9767                                 cls: 'fc-day-content',
9768                              
9769                                 cn : [
9770                                      {
9771                                         style: 'position: relative;' // height: 17px;
9772                                     }
9773                                 ]
9774                             }
9775                             
9776                             
9777                         ]
9778                     }
9779                 ]
9780                 
9781             }
9782         };
9783         var cal_rows = function() {
9784             
9785             var ret = []
9786             for (var r = 0; r < 6; r++) {
9787                 var row= {
9788                     tag : 'tr',
9789                     cls : 'fc-week',
9790                     cn : []
9791                 };
9792                 
9793                 for (var i =0; i < Date.dayNames.length; i++) {
9794                     var d = Date.dayNames[i];
9795                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9796
9797                 }
9798                 row.cn[0].cls+=' fc-first';
9799                 row.cn[0].cn[0].style = 'min-height:90px';
9800                 row.cn[6].cls+=' fc-last';
9801                 ret.push(row);
9802                 
9803             }
9804             ret[0].cls += ' fc-first';
9805             ret[4].cls += ' fc-prev-last';
9806             ret[5].cls += ' fc-last';
9807             return ret;
9808             
9809         };
9810         
9811         var cal_table = {
9812             tag: 'table',
9813             cls: 'fc-border-separate',
9814             style : 'width:100%',
9815             cellspacing  : 0,
9816             cn : [
9817                 { 
9818                     tag: 'thead',
9819                     cn : [
9820                         { 
9821                             tag: 'tr',
9822                             cls : 'fc-first fc-last',
9823                             cn : cal_heads()
9824                         }
9825                     ]
9826                 },
9827                 { 
9828                     tag: 'tbody',
9829                     cn : cal_rows()
9830                 }
9831                   
9832             ]
9833         };
9834          
9835          var cfg = {
9836             cls : 'fc fc-ltr',
9837             cn : [
9838                 header,
9839                 {
9840                     cls : 'fc-content',
9841                     style : "position: relative;",
9842                     cn : [
9843                         {
9844                             cls : 'fc-view fc-view-month fc-grid',
9845                             style : 'position: relative',
9846                             unselectable : 'on',
9847                             cn : [
9848                                 {
9849                                     cls : 'fc-event-container',
9850                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9851                                 },
9852                                 cal_table
9853                             ]
9854                         }
9855                     ]
9856     
9857                 }
9858            ] 
9859             
9860         };
9861         
9862          
9863         
9864         return cfg;
9865     },
9866     
9867     
9868     initEvents : function()
9869     {
9870         if(!this.store){
9871             throw "can not find store for calendar";
9872         }
9873         
9874         var mark = {
9875             tag: "div",
9876             cls:"x-dlg-mask",
9877             style: "text-align:center",
9878             cn: [
9879                 {
9880                     tag: "div",
9881                     style: "background-color:white;width:50%;margin:250 auto",
9882                     cn: [
9883                         {
9884                             tag: "img",
9885                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9886                         },
9887                         {
9888                             tag: "span",
9889                             html: "Loading"
9890                         }
9891                         
9892                     ]
9893                 }
9894             ]
9895         }
9896         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9897         
9898         var size = this.el.select('.fc-content', true).first().getSize();
9899         this.maskEl.setSize(size.width, size.height);
9900         this.maskEl.enableDisplayMode("block");
9901         if(!this.loadMask){
9902             this.maskEl.hide();
9903         }
9904         
9905         this.store = Roo.factory(this.store, Roo.data);
9906         this.store.on('load', this.onLoad, this);
9907         this.store.on('beforeload', this.onBeforeLoad, this);
9908         
9909         this.resize();
9910         
9911         this.cells = this.el.select('.fc-day',true);
9912         //Roo.log(this.cells);
9913         this.textNodes = this.el.query('.fc-day-number');
9914         this.cells.addClassOnOver('fc-state-hover');
9915         
9916         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
9917         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
9918         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
9919         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
9920         
9921         this.on('monthchange', this.onMonthChange, this);
9922         
9923         this.update(new Date().clearTime());
9924     },
9925     
9926     resize : function() {
9927         var sz  = this.el.getSize();
9928         
9929         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
9930         this.el.select('.fc-day-content div',true).setHeight(34);
9931     },
9932     
9933     
9934     // private
9935     showPrevMonth : function(e){
9936         this.update(this.activeDate.add("mo", -1));
9937     },
9938     showToday : function(e){
9939         this.update(new Date().clearTime());
9940     },
9941     // private
9942     showNextMonth : function(e){
9943         this.update(this.activeDate.add("mo", 1));
9944     },
9945
9946     // private
9947     showPrevYear : function(){
9948         this.update(this.activeDate.add("y", -1));
9949     },
9950
9951     // private
9952     showNextYear : function(){
9953         this.update(this.activeDate.add("y", 1));
9954     },
9955
9956     
9957    // private
9958     update : function(date)
9959     {
9960         var vd = this.activeDate;
9961         this.activeDate = date;
9962 //        if(vd && this.el){
9963 //            var t = date.getTime();
9964 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
9965 //                Roo.log('using add remove');
9966 //                
9967 //                this.fireEvent('monthchange', this, date);
9968 //                
9969 //                this.cells.removeClass("fc-state-highlight");
9970 //                this.cells.each(function(c){
9971 //                   if(c.dateValue == t){
9972 //                       c.addClass("fc-state-highlight");
9973 //                       setTimeout(function(){
9974 //                            try{c.dom.firstChild.focus();}catch(e){}
9975 //                       }, 50);
9976 //                       return false;
9977 //                   }
9978 //                   return true;
9979 //                });
9980 //                return;
9981 //            }
9982 //        }
9983         
9984         var days = date.getDaysInMonth();
9985         
9986         var firstOfMonth = date.getFirstDateOfMonth();
9987         var startingPos = firstOfMonth.getDay()-this.startDay;
9988         
9989         if(startingPos < this.startDay){
9990             startingPos += 7;
9991         }
9992         
9993         var pm = date.add(Date.MONTH, -1);
9994         var prevStart = pm.getDaysInMonth()-startingPos;
9995 //        
9996         this.cells = this.el.select('.fc-day',true);
9997         this.textNodes = this.el.query('.fc-day-number');
9998         this.cells.addClassOnOver('fc-state-hover');
9999         
10000         var cells = this.cells.elements;
10001         var textEls = this.textNodes;
10002         
10003         Roo.each(cells, function(cell){
10004             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10005         });
10006         
10007         days += startingPos;
10008
10009         // convert everything to numbers so it's fast
10010         var day = 86400000;
10011         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10012         //Roo.log(d);
10013         //Roo.log(pm);
10014         //Roo.log(prevStart);
10015         
10016         var today = new Date().clearTime().getTime();
10017         var sel = date.clearTime().getTime();
10018         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10019         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10020         var ddMatch = this.disabledDatesRE;
10021         var ddText = this.disabledDatesText;
10022         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10023         var ddaysText = this.disabledDaysText;
10024         var format = this.format;
10025         
10026         var setCellClass = function(cal, cell){
10027             
10028             //Roo.log('set Cell Class');
10029             cell.title = "";
10030             var t = d.getTime();
10031             
10032             //Roo.log(d);
10033             
10034             cell.dateValue = t;
10035             if(t == today){
10036                 cell.className += " fc-today";
10037                 cell.className += " fc-state-highlight";
10038                 cell.title = cal.todayText;
10039             }
10040             if(t == sel){
10041                 // disable highlight in other month..
10042                 //cell.className += " fc-state-highlight";
10043                 
10044             }
10045             // disabling
10046             if(t < min) {
10047                 cell.className = " fc-state-disabled";
10048                 cell.title = cal.minText;
10049                 return;
10050             }
10051             if(t > max) {
10052                 cell.className = " fc-state-disabled";
10053                 cell.title = cal.maxText;
10054                 return;
10055             }
10056             if(ddays){
10057                 if(ddays.indexOf(d.getDay()) != -1){
10058                     cell.title = ddaysText;
10059                     cell.className = " fc-state-disabled";
10060                 }
10061             }
10062             if(ddMatch && format){
10063                 var fvalue = d.dateFormat(format);
10064                 if(ddMatch.test(fvalue)){
10065                     cell.title = ddText.replace("%0", fvalue);
10066                     cell.className = " fc-state-disabled";
10067                 }
10068             }
10069             
10070             if (!cell.initialClassName) {
10071                 cell.initialClassName = cell.dom.className;
10072             }
10073             
10074             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10075         };
10076
10077         var i = 0;
10078         
10079         for(; i < startingPos; i++) {
10080             textEls[i].innerHTML = (++prevStart);
10081             d.setDate(d.getDate()+1);
10082             
10083             cells[i].className = "fc-past fc-other-month";
10084             setCellClass(this, cells[i]);
10085         }
10086         
10087         var intDay = 0;
10088         
10089         for(; i < days; i++){
10090             intDay = i - startingPos + 1;
10091             textEls[i].innerHTML = (intDay);
10092             d.setDate(d.getDate()+1);
10093             
10094             cells[i].className = ''; // "x-date-active";
10095             setCellClass(this, cells[i]);
10096         }
10097         var extraDays = 0;
10098         
10099         for(; i < 42; i++) {
10100             textEls[i].innerHTML = (++extraDays);
10101             d.setDate(d.getDate()+1);
10102             
10103             cells[i].className = "fc-future fc-other-month";
10104             setCellClass(this, cells[i]);
10105         }
10106         
10107         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10108         
10109         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10110         
10111         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10112         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10113         
10114         if(totalRows != 6){
10115             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10116             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10117         }
10118         
10119         this.fireEvent('monthchange', this, date);
10120         
10121         
10122         /*
10123         if(!this.internalRender){
10124             var main = this.el.dom.firstChild;
10125             var w = main.offsetWidth;
10126             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10127             Roo.fly(main).setWidth(w);
10128             this.internalRender = true;
10129             // opera does not respect the auto grow header center column
10130             // then, after it gets a width opera refuses to recalculate
10131             // without a second pass
10132             if(Roo.isOpera && !this.secondPass){
10133                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10134                 this.secondPass = true;
10135                 this.update.defer(10, this, [date]);
10136             }
10137         }
10138         */
10139         
10140     },
10141     
10142     findCell : function(dt) {
10143         dt = dt.clearTime().getTime();
10144         var ret = false;
10145         this.cells.each(function(c){
10146             //Roo.log("check " +c.dateValue + '?=' + dt);
10147             if(c.dateValue == dt){
10148                 ret = c;
10149                 return false;
10150             }
10151             return true;
10152         });
10153         
10154         return ret;
10155     },
10156     
10157     findCells : function(ev) {
10158         var s = ev.start.clone().clearTime().getTime();
10159        // Roo.log(s);
10160         var e= ev.end.clone().clearTime().getTime();
10161        // Roo.log(e);
10162         var ret = [];
10163         this.cells.each(function(c){
10164              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10165             
10166             if(c.dateValue > e){
10167                 return ;
10168             }
10169             if(c.dateValue < s){
10170                 return ;
10171             }
10172             ret.push(c);
10173         });
10174         
10175         return ret;    
10176     },
10177     
10178     findBestRow: function(cells)
10179     {
10180         var ret = 0;
10181         
10182         for (var i =0 ; i < cells.length;i++) {
10183             ret  = Math.max(cells[i].rows || 0,ret);
10184         }
10185         return ret;
10186         
10187     },
10188     
10189     
10190     addItem : function(ev)
10191     {
10192         // look for vertical location slot in
10193         var cells = this.findCells(ev);
10194         
10195         ev.row = this.findBestRow(cells);
10196         
10197         // work out the location.
10198         
10199         var crow = false;
10200         var rows = [];
10201         for(var i =0; i < cells.length; i++) {
10202             if (!crow) {
10203                 crow = {
10204                     start : cells[i],
10205                     end :  cells[i]
10206                 };
10207                 continue;
10208             }
10209             if (crow.start.getY() == cells[i].getY()) {
10210                 // on same row.
10211                 crow.end = cells[i];
10212                 continue;
10213             }
10214             // different row.
10215             rows.push(crow);
10216             crow = {
10217                 start: cells[i],
10218                 end : cells[i]
10219             };
10220             
10221         }
10222         
10223         rows.push(crow);
10224         ev.els = [];
10225         ev.rows = rows;
10226         ev.cells = cells;
10227         for (var i = 0; i < cells.length;i++) {
10228             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10229             
10230         }
10231         
10232         this.calevents.push(ev);
10233     },
10234     
10235     clearEvents: function() {
10236         
10237         if(!this.calevents){
10238             return;
10239         }
10240         
10241         Roo.each(this.cells.elements, function(c){
10242             c.rows = 0;
10243         });
10244         
10245         Roo.each(this.calevents, function(e) {
10246             Roo.each(e.els, function(el) {
10247                 el.un('mouseenter' ,this.onEventEnter, this);
10248                 el.un('mouseleave' ,this.onEventLeave, this);
10249                 el.remove();
10250             },this);
10251         },this);
10252         
10253     },
10254     
10255     renderEvents: function()
10256     {   
10257         // first make sure there is enough space..
10258         
10259         this.cells.each(function(c) {
10260         
10261             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10262         });
10263         
10264         for (var e = 0; e < this.calevents.length; e++) {
10265             var ev = this.calevents[e];
10266             var cells = ev.cells;
10267             var rows = ev.rows;
10268             
10269             for(var i =0; i < rows.length; i++) {
10270                 
10271                  
10272                 // how many rows should it span..
10273                 
10274                 var  cfg = {
10275                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10276                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10277                     
10278                     unselectable : "on",
10279                     cn : [
10280                         {
10281                             cls: 'fc-event-inner',
10282                             cn : [
10283                                 {
10284                                   tag:'span',
10285                                   cls: 'fc-event-time',
10286                                   html : cells.length > 1 ? '' : ev.time
10287                                 },
10288                                 {
10289                                   tag:'span',
10290                                   cls: 'fc-event-title',
10291                                   html : String.format('{0}', ev.title)
10292                                 }
10293                                 
10294                                 
10295                             ]
10296                         },
10297                         {
10298                             cls: 'ui-resizable-handle ui-resizable-e',
10299                             html : '&nbsp;&nbsp;&nbsp'
10300                         }
10301                         
10302                     ]
10303                 };
10304                 if (i == 0) {
10305                     cfg.cls += ' fc-event-start';
10306                 }
10307                 if ((i+1) == rows.length) {
10308                     cfg.cls += ' fc-event-end';
10309                 }
10310                 
10311                 var ctr = this.el.select('.fc-event-container',true).first();
10312                 var cg = ctr.createChild(cfg);
10313                 
10314                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10315                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10316                 cg.on('click', this.onEventClick, this, ev);
10317                 
10318                 ev.els.push(cg);
10319                 
10320                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10321                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10322                 //Roo.log(cg);
10323                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10324                 cg.setWidth(ebox.right - sbox.x -2);
10325             }
10326             
10327             
10328         }
10329         
10330     },
10331     
10332     onEventEnter: function (e, el,event,d) {
10333         this.fireEvent('evententer', this, el, event);
10334     },
10335     
10336     onEventLeave: function (e, el,event,d) {
10337         this.fireEvent('eventleave', this, el, event);
10338     },
10339     
10340     onEventClick: function (e, el,event,d) {
10341         this.fireEvent('eventclick', this, el, event);
10342     },
10343     
10344     onMonthChange: function () {
10345         this.store.load();
10346     },
10347     
10348     onLoad: function () 
10349     {   
10350         this.calevents = [];
10351         var cal = this;
10352         
10353         if(this.store.getCount() > 0){
10354             this.store.data.each(function(d){
10355                cal.addItem({
10356                     id : d.data.id,
10357                     start: new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s'),
10358                     end : new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s'),
10359                     time : d.data.start_time,
10360                     title : d.data.title,
10361                     description : d.data.description,
10362                     venue : d.data.venue
10363                 });
10364             });
10365         }
10366         
10367         this.renderEvents();
10368         
10369         if(this.loadMask){
10370             this.maskEl.hide();
10371         }
10372     },
10373     
10374     onBeforeLoad: function()
10375     {
10376         this.clearEvents();
10377         
10378         if(this.loadMask){
10379             this.maskEl.show();
10380         }
10381     }
10382 });
10383
10384  
10385  /*
10386  * - LGPL
10387  *
10388  * element
10389  * 
10390  */
10391
10392 /**
10393  * @class Roo.bootstrap.Popover
10394  * @extends Roo.bootstrap.Component
10395  * Bootstrap Popover class
10396  * @cfg {String} html contents of the popover   (or false to use children..)
10397  * @cfg {String} title of popover (or false to hide)
10398  * @cfg {String} placement how it is placed
10399  * @cfg {String} trigger click || hover (or false to trigger manually)
10400  * @cfg {String} over what (parent or false to trigger manually.)
10401  * 
10402  * @constructor
10403  * Create a new Popover
10404  * @param {Object} config The config object
10405  */
10406
10407 Roo.bootstrap.Popover = function(config){
10408     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10409 };
10410
10411 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10412     
10413     title: 'Fill in a title',
10414     html: false,
10415     
10416     placement : 'right',
10417     trigger : 'hover', // hover
10418     
10419     over: 'parent',
10420     
10421     can_build_overlaid : false,
10422     
10423     getChildContainer : function()
10424     {
10425         return this.el.select('.popover-content',true).first();
10426     },
10427     
10428     getAutoCreate : function(){
10429          Roo.log('make popover?');
10430         var cfg = {
10431            cls : 'popover roo-dynamic',
10432            style: 'display:block',
10433            cn : [
10434                 {
10435                     cls : 'arrow'
10436                 },
10437                 {
10438                     cls : 'popover-inner',
10439                     cn : [
10440                         {
10441                             tag: 'h3',
10442                             cls: 'popover-title',
10443                             html : this.title
10444                         },
10445                         {
10446                             cls : 'popover-content',
10447                             html : this.html
10448                         }
10449                     ]
10450                     
10451                 }
10452            ]
10453         };
10454         
10455         return cfg;
10456     },
10457     setTitle: function(str)
10458     {
10459         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10460     },
10461     setContent: function(str)
10462     {
10463         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10464     },
10465     // as it get's added to the bottom of the page.
10466     onRender : function(ct, position)
10467     {
10468         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10469         if(!this.el){
10470             var cfg = Roo.apply({},  this.getAutoCreate());
10471             cfg.id = Roo.id();
10472             
10473             if (this.cls) {
10474                 cfg.cls += ' ' + this.cls;
10475             }
10476             if (this.style) {
10477                 cfg.style = this.style;
10478             }
10479             Roo.log("adding to ")
10480             this.el = Roo.get(document.body).createChild(cfg, position);
10481             Roo.log(this.el);
10482         }
10483         this.initEvents();
10484     },
10485     
10486     initEvents : function()
10487     {
10488         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10489         this.el.enableDisplayMode('block');
10490         this.el.hide();
10491         if (this.over === false) {
10492             return; 
10493         }
10494         if (this.triggers === false) {
10495             return;
10496         }
10497         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10498         var triggers = this.trigger ? this.trigger.split(' ') : [];
10499         Roo.each(triggers, function(trigger) {
10500         
10501             if (trigger == 'click') {
10502                 on_el.on('click', this.toggle, this);
10503             } else if (trigger != 'manual') {
10504                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10505                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10506       
10507                 on_el.on(eventIn  ,this.enter, this);
10508                 on_el.on(eventOut, this.leave, this);
10509             }
10510         }, this);
10511         
10512     },
10513     
10514     
10515     // private
10516     timeout : null,
10517     hoverState : null,
10518     
10519     toggle : function () {
10520         this.hoverState == 'in' ? this.leave() : this.enter();
10521     },
10522     
10523     enter : function () {
10524        
10525     
10526         clearTimeout(this.timeout);
10527     
10528         this.hoverState = 'in'
10529     
10530         if (!this.delay || !this.delay.show) {
10531             this.show();
10532             return 
10533         }
10534         var _t = this;
10535         this.timeout = setTimeout(function () {
10536             if (_t.hoverState == 'in') {
10537                 _t.show();
10538             }
10539         }, this.delay.show)
10540     },
10541     leave : function() {
10542         clearTimeout(this.timeout);
10543     
10544         this.hoverState = 'out'
10545     
10546         if (!this.delay || !this.delay.hide) {
10547             this.hide();
10548             return 
10549         }
10550         var _t = this;
10551         this.timeout = setTimeout(function () {
10552             if (_t.hoverState == 'out') {
10553                 _t.hide();
10554             }
10555         }, this.delay.hide)
10556     },
10557     
10558     show : function (on_el)
10559     {
10560         if (!on_el) {
10561             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10562         }
10563         // set content.
10564         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10565         if (this.html !== false) {
10566             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10567         }
10568         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10569         if (!this.title.length) {
10570             this.el.select('.popover-title',true).hide();
10571         }
10572         
10573         var placement = typeof this.placement == 'function' ?
10574             this.placement.call(this, this.el, on_el) :
10575             this.placement;
10576             
10577         var autoToken = /\s?auto?\s?/i;
10578         var autoPlace = autoToken.test(placement);
10579         if (autoPlace) {
10580             placement = placement.replace(autoToken, '') || 'top';
10581         }
10582         
10583         //this.el.detach()
10584         //this.el.setXY([0,0]);
10585         this.el.show();
10586         this.el.dom.style.display='block';
10587         this.el.addClass(placement);
10588         
10589         //this.el.appendTo(on_el);
10590         
10591         var p = this.getPosition();
10592         var box = this.el.getBox();
10593         
10594         if (autoPlace) {
10595             // fixme..
10596         }
10597         var align = Roo.bootstrap.Popover.alignment[placement]
10598         this.el.alignTo(on_el, align[0],align[1]);
10599         //var arrow = this.el.select('.arrow',true).first();
10600         //arrow.set(align[2], 
10601         
10602         this.el.addClass('in');
10603         this.hoverState = null;
10604         
10605         if (this.el.hasClass('fade')) {
10606             // fade it?
10607         }
10608         
10609     },
10610     hide : function()
10611     {
10612         this.el.setXY([0,0]);
10613         this.el.removeClass('in');
10614         this.el.hide();
10615         
10616     }
10617     
10618 });
10619
10620 Roo.bootstrap.Popover.alignment = {
10621     'left' : ['r-l', [-10,0], 'right'],
10622     'right' : ['l-r', [10,0], 'left'],
10623     'bottom' : ['t-b', [0,10], 'top'],
10624     'top' : [ 'b-t', [0,-10], 'bottom']
10625 };
10626
10627  /*
10628  * - LGPL
10629  *
10630  * Progress
10631  * 
10632  */
10633
10634 /**
10635  * @class Roo.bootstrap.Progress
10636  * @extends Roo.bootstrap.Component
10637  * Bootstrap Progress class
10638  * @cfg {Boolean} striped striped of the progress bar
10639  * @cfg {Boolean} active animated of the progress bar
10640  * 
10641  * 
10642  * @constructor
10643  * Create a new Progress
10644  * @param {Object} config The config object
10645  */
10646
10647 Roo.bootstrap.Progress = function(config){
10648     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10649 };
10650
10651 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10652     
10653     striped : false,
10654     active: false,
10655     
10656     getAutoCreate : function(){
10657         var cfg = {
10658             tag: 'div',
10659             cls: 'progress'
10660         };
10661         
10662         
10663         if(this.striped){
10664             cfg.cls += ' progress-striped';
10665         }
10666       
10667         if(this.active){
10668             cfg.cls += ' active';
10669         }
10670         
10671         
10672         return cfg;
10673     }
10674    
10675 });
10676
10677  
10678
10679  /*
10680  * - LGPL
10681  *
10682  * ProgressBar
10683  * 
10684  */
10685
10686 /**
10687  * @class Roo.bootstrap.ProgressBar
10688  * @extends Roo.bootstrap.Component
10689  * Bootstrap ProgressBar class
10690  * @cfg {Number} aria_valuenow aria-value now
10691  * @cfg {Number} aria_valuemin aria-value min
10692  * @cfg {Number} aria_valuemax aria-value max
10693  * @cfg {String} label label for the progress bar
10694  * @cfg {String} panel (success | info | warning | danger )
10695  * @cfg {String} role role of the progress bar
10696  * @cfg {String} sr_only text
10697  * 
10698  * 
10699  * @constructor
10700  * Create a new ProgressBar
10701  * @param {Object} config The config object
10702  */
10703
10704 Roo.bootstrap.ProgressBar = function(config){
10705     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10706 };
10707
10708 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10709     
10710     aria_valuenow : 0,
10711     aria_valuemin : 0,
10712     aria_valuemax : 100,
10713     label : false,
10714     panel : false,
10715     role : false,
10716     sr_only: false,
10717     
10718     getAutoCreate : function()
10719     {
10720         
10721         var cfg = {
10722             tag: 'div',
10723             cls: 'progress-bar',
10724             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10725         };
10726         
10727         if(this.sr_only){
10728             cfg.cn = {
10729                 tag: 'span',
10730                 cls: 'sr-only',
10731                 html: this.sr_only
10732             }
10733         }
10734         
10735         if(this.role){
10736             cfg.role = this.role;
10737         }
10738         
10739         if(this.aria_valuenow){
10740             cfg['aria-valuenow'] = this.aria_valuenow;
10741         }
10742         
10743         if(this.aria_valuemin){
10744             cfg['aria-valuemin'] = this.aria_valuemin;
10745         }
10746         
10747         if(this.aria_valuemax){
10748             cfg['aria-valuemax'] = this.aria_valuemax;
10749         }
10750         
10751         if(this.label && !this.sr_only){
10752             cfg.html = this.label;
10753         }
10754         
10755         if(this.panel){
10756             cfg.cls += ' progress-bar-' + this.panel;
10757         }
10758         
10759         return cfg;
10760     },
10761     
10762     update : function(aria_valuenow)
10763     {
10764         this.aria_valuenow = aria_valuenow;
10765         
10766         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10767     }
10768    
10769 });
10770
10771  
10772
10773  /*
10774  * - LGPL
10775  *
10776  * TabPanel
10777  * 
10778  */
10779
10780 /**
10781  * @class Roo.bootstrap.TabPanel
10782  * @extends Roo.bootstrap.Component
10783  * Bootstrap TabPanel class
10784  * @cfg {Boolean} active panel active
10785  * @cfg {String} html panel content
10786  * @cfg {String} tabId tab relate id
10787  * 
10788  * 
10789  * @constructor
10790  * Create a new TabPanel
10791  * @param {Object} config The config object
10792  */
10793
10794 Roo.bootstrap.TabPanel = function(config){
10795     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10796 };
10797
10798 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10799     
10800     active: false,
10801     html: false,
10802     tabId: false,
10803     
10804     getAutoCreate : function(){
10805         var cfg = {
10806             tag: 'div',
10807             cls: 'tab-pane',
10808             html: this.html || ''
10809         };
10810         
10811         if(this.active){
10812             cfg.cls += ' active';
10813         }
10814         
10815         if(this.tabId){
10816             cfg.tabId = this.tabId;
10817         }
10818         
10819         return cfg;
10820     }
10821    
10822 });
10823
10824  
10825
10826  /*
10827  * - LGPL
10828  *
10829  * DateField
10830  * 
10831  */
10832
10833 /**
10834  * @class Roo.bootstrap.DateField
10835  * @extends Roo.bootstrap.Input
10836  * Bootstrap DateField class
10837  * @cfg {Number} weekStart default 0
10838  * @cfg {Number} weekStart default 0
10839  * @cfg {Number} viewMode default empty, (months|years)
10840  * @cfg {Number} minViewMode default empty, (months|years)
10841  * @cfg {Number} startDate default -Infinity
10842  * @cfg {Number} endDate default Infinity
10843  * @cfg {Boolean} todayHighlight default false
10844  * @cfg {Boolean} todayBtn default false
10845  * @cfg {Boolean} calendarWeeks default false
10846  * @cfg {Object} daysOfWeekDisabled default empty
10847  * @cfg {Boolean} showTime pick the time (default true)
10848  * 
10849  * @cfg {Boolean} keyboardNavigation default true
10850  * @cfg {String} language default en
10851  * 
10852  * @constructor
10853  * Create a new DateField
10854  * @param {Object} config The config object
10855  */
10856
10857 Roo.bootstrap.DateField = function(config){
10858     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10859 };
10860
10861 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10862     
10863     /**
10864      * @cfg {String} format
10865      * The default date format string which can be overriden for localization support.  The format must be
10866      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10867      */
10868     format : "m/d/y",
10869     /**
10870      * @cfg {String} altFormats
10871      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10872      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10873      */
10874     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10875     
10876     weekStart : 0,
10877     
10878     viewMode : 'day',
10879     
10880     minViewMode : '',
10881     
10882     todayHighlight : false,
10883     
10884     todayBtn: false,
10885     
10886     language: 'en',
10887     
10888     keyboardNavigation: true,
10889     
10890     calendarWeeks: false,
10891     
10892     startDate: -Infinity,
10893     
10894     endDate: Infinity,
10895     
10896     daysOfWeekDisabled: [],
10897     
10898     showTime : true,
10899     
10900     UTCDate: function()
10901     {
10902         return new Date(Date.UTC.apply(Date, arguments));
10903     },
10904     
10905     UTCToday: function()
10906     {
10907         var today = new Date();
10908         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
10909     },
10910     
10911     getDate: function() {
10912             var d = this.getUTCDate();
10913             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
10914     },
10915     
10916     getUTCDate: function() {
10917             return this.date;
10918     },
10919     
10920     setDate: function(d) {
10921             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
10922     },
10923     
10924     setUTCDate: function(d) {
10925             this.date = d;
10926             this.setValue(this.formatDate(this.date));
10927     },
10928         
10929     onRender: function(ct, position)
10930     {
10931         
10932         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
10933         
10934         this.language = this.language || 'en';
10935         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
10936         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
10937         
10938         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
10939         this.format = this.format || 'm/d/y';
10940         this.isInline = false;
10941         this.isInput = true;
10942         this.component = this.el.select('.add-on', true).first() || false;
10943         this.component = (this.component && this.component.length === 0) ? false : this.component;
10944         this.hasInput = this.component && this.inputEL().length;
10945         
10946         if (typeof(this.minViewMode === 'string')) {
10947             switch (this.minViewMode) {
10948                 case 'months':
10949                     this.minViewMode = 2;
10950                     break;
10951                 case 'years':
10952                     this.minViewMode = 3;
10953                     break;
10954                 case 'day':
10955                     this.minViewMode = 1;
10956                     break;
10957                 default:
10958                     this.minViewMode = 0;
10959                     break;
10960             }
10961         }
10962         
10963         if (typeof(this.viewMode === 'string')) {
10964             switch (this.viewMode) {
10965                 case 'months':
10966                     this.viewMode = 2;
10967                     break;
10968                 case 'years':
10969                     this.viewMode = 2;
10970                     break;
10971                 case 'day':
10972                     this.viewMode = 1;
10973                     break;
10974                 default:
10975                     this.viewMode = 0;
10976                     break;
10977             }
10978         }
10979                 
10980         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
10981         
10982         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10983         
10984         this.picker().on('mousedown', this.onMousedown, this);
10985         this.picker().on('click', this.onClick, this);
10986         
10987         this.picker().addClass('datepicker-dropdown');
10988         
10989         this.startViewMode = this.viewMode;
10990         
10991         if(this.showTime){
10992             
10993             var dayFoot = this.picker().select('>.datepicker-days th.picker-switch', true).first();
10994             var timeFoot = this.picker().select('>.datepicker-time th.picker-switch', true).first();
10995
10996             var dayFootIcon = this.picker().select('>.datepicker-days span.picker-switch', true).first();
10997             var timeFootIcon = this.picker().select('>.datepicker-time span.picker-switch', true).first();
10998             
10999             timeFoot.addClass('switch-calendar');
11000             dayFoot.addClass('switch-time');
11001             
11002             timeFootIcon.addClass('switch-calendar');
11003             timeFootIcon.addClass('glyphicon-calendar');
11004             
11005             dayFootIcon.addClass('switch-time');
11006             dayFootIcon.addClass('glyphicon-time');
11007             
11008         }else{
11009             Roo.each(this.picker().select('tfoot th.picker-switch', true).elements, function(v){
11010                 v.remove();
11011             });
11012         }
11013                         
11014         
11015         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11016         
11017         this.setStartDate(this.startDate);
11018         this.setEndDate(this.endDate);
11019         
11020         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11021         
11022         this.fillDow();
11023         this.fillMonths();
11024         this.fillTime();
11025         this.update();
11026         this.showMode();
11027         
11028         if(this.isInline) {
11029             this.show();
11030         }
11031     },
11032     
11033     picker : function()
11034     {
11035         return this.el.select('.datepicker', true).first();
11036     },
11037     
11038     fillDow: function()
11039     {
11040         var dowCnt = this.weekStart;
11041         
11042         var dow = {
11043             tag: 'tr',
11044             cn: [
11045                 
11046             ]
11047         };
11048         
11049         if(this.calendarWeeks){
11050             dow.cn.push({
11051                 tag: 'th',
11052                 cls: 'cw',
11053                 html: '&nbsp;'
11054             })
11055         }
11056         
11057         while (dowCnt < this.weekStart + 7) {
11058             dow.cn.push({
11059                 tag: 'th',
11060                 cls: 'dow',
11061                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11062             });
11063         }
11064         
11065         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11066     },
11067     
11068     fillMonths: function()
11069     {    
11070         var i = 0
11071         var months = this.picker().select('>.datepicker-months td', true).first();
11072         
11073         months.dom.innerHTML = '';
11074         
11075         while (i < 12) {
11076             var month = {
11077                 tag: 'span',
11078                 cls: 'month',
11079                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11080             }
11081             
11082             months.createChild(month);
11083         }
11084         
11085     },
11086     
11087     fillTime: function()
11088     {    
11089         var time = this.picker().select('>.datepicker-time tbody', true).first();
11090         
11091         time.dom.innerHTML = '';
11092         
11093         time.createChild({
11094             tag: 'tr',
11095             cn: [
11096                 {
11097                     tag: 'td',
11098                     cn: [
11099                         {
11100                             tag: 'a',
11101                             href: '#',
11102                             cls: 'btn',
11103                             cn: [
11104                                 {
11105                                     tag: 'span',
11106                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
11107                                 }
11108                             ]
11109                         } 
11110                     ]
11111                 },
11112                 {
11113                     tag: 'td',
11114                     cls: 'separator'
11115                 },
11116                 {
11117                     tag: 'td',
11118                     cn: [
11119                         {
11120                             tag: 'a',
11121                             href: '#',
11122                             cls: 'btn',
11123                             cn: [
11124                                 {
11125                                     tag: 'span',
11126                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
11127                                 }
11128                             ]
11129                         }
11130                     ]
11131                 },
11132                 {
11133                     tag: 'td',
11134                     cls: 'separator'
11135                 }
11136             ]
11137         });
11138         
11139         time.createChild({
11140             tag: 'tr',
11141             cn: [
11142                 {
11143                     tag: 'td',
11144                     cn: [
11145                         {
11146                             tag: 'span',
11147                             cls: 'timepicker-hour',
11148                             html: '00'
11149                         }  
11150                     ]
11151                 },
11152                 {
11153                     tag: 'td',
11154                     cls: 'separator',
11155                     html: ':'
11156                 },
11157                 {
11158                     tag: 'td',
11159                     cn: [
11160                         {
11161                             tag: 'span',
11162                             cls: 'timepicker-minute',
11163                             html: '00'
11164                         }  
11165                     ]
11166                 },
11167                 {
11168                     tag: 'td',
11169                     cls: 'separator'
11170                 },
11171                 {
11172                     tag: 'td',
11173                     cn: [
11174                         {
11175                             tag: 'button',
11176                             type: 'button',
11177                             cls: 'btn btn-primary',
11178                             html: 'AM'
11179                             
11180                         }
11181                     ]
11182                 }
11183             ]
11184         });
11185         
11186         time.createChild({
11187             tag: 'tr',
11188             cn: [
11189                 {
11190                     tag: 'td',
11191                     cn: [
11192                         {
11193                             tag: 'a',
11194                             href: '#',
11195                             cls: 'btn',
11196                             cn: [
11197                                 {
11198                                     tag: 'span',
11199                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
11200                                 }
11201                             ]
11202                         }
11203                     ]
11204                 },
11205                 {
11206                     tag: 'td',
11207                     cls: 'separator'
11208                 },
11209                 {
11210                     tag: 'td',
11211                     cn: [
11212                         {
11213                             tag: 'a',
11214                             href: '#',
11215                             cls: 'btn',
11216                             cn: [
11217                                 {
11218                                     tag: 'span',
11219                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
11220                                 }
11221                             ]
11222                         }
11223                     ]
11224                 },
11225                 {
11226                     tag: 'td',
11227                     cls: 'separator'
11228                 }
11229             ]
11230         });
11231         
11232     },
11233     
11234     update: function(){
11235         
11236         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11237         
11238         if (this.date < this.startDate) {
11239             this.viewDate = new Date(this.startDate);
11240         } else if (this.date > this.endDate) {
11241             this.viewDate = new Date(this.endDate);
11242         } else {
11243             this.viewDate = new Date(this.date);
11244         }
11245         
11246         this.fill();
11247     },
11248     
11249     fill: function() {
11250         var d = new Date(this.viewDate),
11251                 year = d.getUTCFullYear(),
11252                 month = d.getUTCMonth(),
11253                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11254                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11255                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11256                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11257                 currentDate = this.date && this.date.valueOf(),
11258                 today = this.UTCToday();
11259         
11260         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11261         
11262 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11263         
11264 //        this.picker.select('>tfoot th.today').
11265 //                                              .text(dates[this.language].today)
11266 //                                              .toggle(this.todayBtn !== false);
11267     
11268         this.updateNavArrows();
11269         this.fillMonths();
11270                                                 
11271         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11272         
11273         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11274          
11275         prevMonth.setUTCDate(day);
11276         
11277         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11278         
11279         var nextMonth = new Date(prevMonth);
11280         
11281         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11282         
11283         nextMonth = nextMonth.valueOf();
11284         
11285         var fillMonths = false;
11286         
11287         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11288         
11289         while(prevMonth.valueOf() < nextMonth) {
11290             var clsName = '';
11291             
11292             if (prevMonth.getUTCDay() === this.weekStart) {
11293                 if(fillMonths){
11294                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11295                 }
11296                     
11297                 fillMonths = {
11298                     tag: 'tr',
11299                     cn: []
11300                 };
11301                 
11302                 if(this.calendarWeeks){
11303                     // ISO 8601: First week contains first thursday.
11304                     // ISO also states week starts on Monday, but we can be more abstract here.
11305                     var
11306                     // Start of current week: based on weekstart/current date
11307                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11308                     // Thursday of this week
11309                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11310                     // First Thursday of year, year from thursday
11311                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11312                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11313                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11314                     
11315                     fillMonths.cn.push({
11316                         tag: 'td',
11317                         cls: 'cw',
11318                         html: calWeek
11319                     });
11320                 }
11321             }
11322             
11323             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11324                 clsName += ' old';
11325             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11326                 clsName += ' new';
11327             }
11328             if (this.todayHighlight &&
11329                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11330                 prevMonth.getUTCMonth() == today.getMonth() &&
11331                 prevMonth.getUTCDate() == today.getDate()) {
11332                 clsName += ' today';
11333             }
11334             
11335             if (currentDate && prevMonth.valueOf() === currentDate) {
11336                 clsName += ' active';
11337             }
11338             
11339             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11340                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11341                     clsName += ' disabled';
11342             }
11343             
11344             fillMonths.cn.push({
11345                 tag: 'td',
11346                 cls: 'day ' + clsName,
11347                 html: prevMonth.getDate()
11348             })
11349             
11350             prevMonth.setDate(prevMonth.getDate()+1);
11351         }
11352           
11353         var currentYear = this.date && this.date.getUTCFullYear();
11354         var currentMonth = this.date && this.date.getUTCMonth();
11355         
11356         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11357         
11358         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11359             v.removeClass('active');
11360             
11361             if(currentYear === year && k === currentMonth){
11362                 v.addClass('active');
11363             }
11364             
11365             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11366                 v.addClass('disabled');
11367             }
11368             
11369         });
11370         
11371         
11372         year = parseInt(year/10, 10) * 10;
11373         
11374         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11375         
11376         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11377         
11378         year -= 1;
11379         for (var i = -1; i < 11; i++) {
11380             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11381                 tag: 'span',
11382                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11383                 html: year
11384             })
11385             
11386             year += 1;
11387         }
11388     },
11389     
11390     showMode: function(dir) {
11391         if (dir) {
11392             this.viewMode = Math.max(this.minViewMode, Math.min(3, this.viewMode + dir));
11393         }
11394         
11395         Roo.each(this.picker().select('>div',true).elements, function(v){
11396             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11397             v.hide();
11398         });
11399         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11400         
11401     },
11402     
11403     place: function()
11404     {
11405         if(this.isInline) return;
11406         
11407         this.picker().removeClass(['bottom', 'top']);
11408         
11409         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11410             /*
11411              * place to the top of element!
11412              *
11413              */
11414             
11415             this.picker().addClass('top');
11416             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11417             
11418             return;
11419         }
11420         
11421         this.picker().addClass('bottom');
11422         
11423         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11424     },
11425     
11426     parseDate : function(value){
11427         if(!value || value instanceof Date){
11428             return value;
11429         }
11430         var v = Date.parseDate(value, this.format);
11431         if (!v && this.useIso) {
11432             v = Date.parseDate(value, 'Y-m-d');
11433         }
11434         if(!v && this.altFormats){
11435             if(!this.altFormatsArray){
11436                 this.altFormatsArray = this.altFormats.split("|");
11437             }
11438             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11439                 v = Date.parseDate(value, this.altFormatsArray[i]);
11440             }
11441         }
11442         return v;
11443     },
11444     
11445     formatDate : function(date, fmt){
11446         return (!date || !(date instanceof Date)) ?
11447         date : date.dateFormat(fmt || this.format);
11448     },
11449     
11450     onFocus : function()
11451     {
11452         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11453         this.show();
11454     },
11455     
11456     onBlur : function()
11457     {
11458         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11459         this.hide();
11460     },
11461     
11462     show : function()
11463     {
11464         this.picker().show();
11465         this.update();
11466         this.place();
11467     },
11468     
11469     hide : function()
11470     {
11471         if(this.isInline) return;
11472         this.picker().hide();
11473         this.viewMode = this.startViewMode;
11474         this.showMode();
11475         
11476     },
11477     
11478     onMousedown: function(e){
11479         e.stopPropagation();
11480         e.preventDefault();
11481     },
11482     
11483     keyup: function(e){
11484         Roo.bootstrap.DateField.superclass.keyup.call(this);
11485         this.update();
11486         
11487     },
11488     
11489     fireKey: function(e){
11490         if (!this.picker().isVisible()){
11491             if (e.keyCode == 27) // allow escape to hide and re-show picker
11492                 this.show();
11493             return;
11494         }
11495         var dateChanged = false,
11496         dir, day, month,
11497         newDate, newViewDate;
11498         switch(e.keyCode){
11499             case 27: // escape
11500                 this.hide();
11501                 e.preventDefault();
11502                 break;
11503             case 37: // left
11504             case 39: // right
11505                 if (!this.keyboardNavigation) break;
11506                 dir = e.keyCode == 37 ? -1 : 1;
11507                 
11508                 if (e.ctrlKey){
11509                     newDate = this.moveYear(this.date, dir);
11510                     newViewDate = this.moveYear(this.viewDate, dir);
11511                 } else if (e.shiftKey){
11512                     newDate = this.moveMonth(this.date, dir);
11513                     newViewDate = this.moveMonth(this.viewDate, dir);
11514                 } else {
11515                     newDate = new Date(this.date);
11516                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11517                     newViewDate = new Date(this.viewDate);
11518                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11519                 }
11520                 if (this.dateWithinRange(newDate)){
11521                     this.date = newDate;
11522                     this.viewDate = newViewDate;
11523                     this.setValue(this.formatDate(this.date));
11524                     this.update();
11525                     e.preventDefault();
11526                     dateChanged = true;
11527                 }
11528                 break;
11529             case 38: // up
11530             case 40: // down
11531                 if (!this.keyboardNavigation) break;
11532                 dir = e.keyCode == 38 ? -1 : 1;
11533                 if (e.ctrlKey){
11534                     newDate = this.moveYear(this.date, dir);
11535                     newViewDate = this.moveYear(this.viewDate, dir);
11536                 } else if (e.shiftKey){
11537                     newDate = this.moveMonth(this.date, dir);
11538                     newViewDate = this.moveMonth(this.viewDate, dir);
11539                 } else {
11540                     newDate = new Date(this.date);
11541                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11542                     newViewDate = new Date(this.viewDate);
11543                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11544                 }
11545                 if (this.dateWithinRange(newDate)){
11546                     this.date = newDate;
11547                     this.viewDate = newViewDate;
11548                     this.setValue(this.formatDate(this.date));
11549                     this.update();
11550                     e.preventDefault();
11551                     dateChanged = true;
11552                 }
11553                 break;
11554             case 13: // enter
11555                 this.setValue(this.formatDate(this.date));
11556                 this.hide();
11557                 e.preventDefault();
11558                 break;
11559             case 9: // tab
11560                 this.setValue(this.formatDate(this.date));
11561                 this.hide();
11562                 break;
11563         }
11564     },
11565     
11566     
11567     onClick: function(e) {
11568         e.stopPropagation();
11569         e.preventDefault();
11570         
11571         var target = e.getTarget();
11572         
11573         if(target.nodeName.toLowerCase() === 'i'){
11574             target = Roo.get(target).dom.parentNode;
11575         }
11576         
11577         var nodeName = target.nodeName;
11578         var className = target.className;
11579         var html = target.innerHTML;
11580         
11581         Roo.log(target);
11582         
11583         Roo.log(nodeName);
11584         Roo.log(className);
11585         Roo.log(html);
11586         
11587         switch(nodeName.toLowerCase()) {
11588             case 'th':
11589                 switch(className) {
11590                     case 'switch':
11591                         this.showMode(1);
11592                         break;
11593                     case 'prev':
11594                     case 'next':
11595                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11596                         switch(this.viewMode){
11597                                 case 0:
11598                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11599                                         break;
11600                                 case 1:
11601                                 case 2:
11602                                         this.viewDate = this.moveYear(this.viewDate, dir);
11603                                         break;
11604                         }
11605                         this.fill();
11606                         break;
11607                     case 'today':
11608                         var date = new Date();
11609                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11610                         this.fill()
11611                         this.setValue(this.formatDate(this.date));
11612                         this.hide();
11613                         break;
11614                      case 'picker-switch':
11615                         if(className.indexOf('switch-time') !== -1){
11616                             Roo.log('got switch-time');
11617                             this.showMode(-1);
11618                             this.fill();
11619                         }else{
11620                             Roo.log('not got switch-time');
11621                             this.showMode(1);
11622                             this.fill();
11623                         }
11624                         
11625                         break;
11626                          
11627                 }
11628                 break;
11629             case 'span':
11630                 if (className.indexOf('disabled') === -1) {
11631                     this.viewDate.setUTCDate(1);
11632                     if (className.indexOf('month') !== -1) {
11633                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11634                     } else if(className.indexOf('picker-switch') !== -1){
11635                         if(className.indexOf('switch-time') !== -1){
11636                             this.showMode(-1);
11637                             this.fill();
11638                         }else{
11639                             this.showMode(1);
11640                             this.fill();
11641                         }
11642                         
11643                         break;
11644                         
11645                     } else {
11646                         var year = parseInt(html, 10) || 0;
11647                         this.viewDate.setUTCFullYear(year);
11648                         
11649                     }
11650                     this.showMode(-1);
11651                     this.fill();
11652                 }
11653                 break;
11654                 
11655             case 'td':
11656                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11657                     var day = parseInt(html, 10) || 1;
11658                     var year = this.viewDate.getUTCFullYear(),
11659                         month = this.viewDate.getUTCMonth();
11660
11661                     if (className.indexOf('old') !== -1) {
11662                         if(month === 0 ){
11663                             month = 11;
11664                             year -= 1;
11665                         }else{
11666                             month -= 1;
11667                         }
11668                     } else if (className.indexOf('new') !== -1) {
11669                         if (month == 11) {
11670                             month = 0;
11671                             year += 1;
11672                         } else {
11673                             month += 1;
11674                         }
11675                     }
11676                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11677                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11678                     this.fill();
11679                     this.setValue(this.formatDate(this.date));
11680                     this.hide();
11681                 }
11682                 break;
11683         }
11684     },
11685     
11686     setStartDate: function(startDate){
11687         this.startDate = startDate || -Infinity;
11688         if (this.startDate !== -Infinity) {
11689             this.startDate = this.parseDate(this.startDate);
11690         }
11691         this.update();
11692         this.updateNavArrows();
11693     },
11694
11695     setEndDate: function(endDate){
11696         this.endDate = endDate || Infinity;
11697         if (this.endDate !== Infinity) {
11698             this.endDate = this.parseDate(this.endDate);
11699         }
11700         this.update();
11701         this.updateNavArrows();
11702     },
11703     
11704     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11705         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11706         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11707             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11708         }
11709         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11710             return parseInt(d, 10);
11711         });
11712         this.update();
11713         this.updateNavArrows();
11714     },
11715     
11716     updateNavArrows: function() {
11717         var d = new Date(this.viewDate),
11718         year = d.getUTCFullYear(),
11719         month = d.getUTCMonth();
11720         
11721         Roo.each(this.picker().select('.prev', true).elements, function(v){
11722             v.show();
11723             switch (this.viewMode) {
11724                 case 0:
11725
11726                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11727                         v.hide();
11728                     }
11729                     break;
11730                 case 1:
11731                 case 2:
11732                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11733                         v.hide();
11734                     }
11735                     break;
11736             }
11737         });
11738         
11739         Roo.each(this.picker().select('.next', true).elements, function(v){
11740             v.show();
11741             switch (this.viewMode) {
11742                 case 0:
11743
11744                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11745                         v.hide();
11746                     }
11747                     break;
11748                 case 1:
11749                 case 2:
11750                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11751                         v.hide();
11752                     }
11753                     break;
11754             }
11755         })
11756     },
11757     
11758     moveMonth: function(date, dir){
11759         if (!dir) return date;
11760         var new_date = new Date(date.valueOf()),
11761         day = new_date.getUTCDate(),
11762         month = new_date.getUTCMonth(),
11763         mag = Math.abs(dir),
11764         new_month, test;
11765         dir = dir > 0 ? 1 : -1;
11766         if (mag == 1){
11767             test = dir == -1
11768             // If going back one month, make sure month is not current month
11769             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11770             ? function(){
11771                 return new_date.getUTCMonth() == month;
11772             }
11773             // If going forward one month, make sure month is as expected
11774             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11775             : function(){
11776                 return new_date.getUTCMonth() != new_month;
11777             };
11778             new_month = month + dir;
11779             new_date.setUTCMonth(new_month);
11780             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11781             if (new_month < 0 || new_month > 11)
11782                 new_month = (new_month + 12) % 12;
11783         } else {
11784             // For magnitudes >1, move one month at a time...
11785             for (var i=0; i<mag; i++)
11786                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11787                 new_date = this.moveMonth(new_date, dir);
11788             // ...then reset the day, keeping it in the new month
11789             new_month = new_date.getUTCMonth();
11790             new_date.setUTCDate(day);
11791             test = function(){
11792                 return new_month != new_date.getUTCMonth();
11793             };
11794         }
11795         // Common date-resetting loop -- if date is beyond end of month, make it
11796         // end of month
11797         while (test()){
11798             new_date.setUTCDate(--day);
11799             new_date.setUTCMonth(new_month);
11800         }
11801         return new_date;
11802     },
11803
11804     moveYear: function(date, dir){
11805         return this.moveMonth(date, dir*12);
11806     },
11807
11808     dateWithinRange: function(date){
11809         return date >= this.startDate && date <= this.endDate;
11810     },
11811
11812     
11813     remove: function() {
11814         this.picker().remove();
11815     }
11816    
11817 });
11818
11819 Roo.apply(Roo.bootstrap.DateField,  {
11820     
11821     head : {
11822         tag: 'thead',
11823         cn: [
11824         {
11825             tag: 'tr',
11826             cn: [
11827             {
11828                 tag: 'th',
11829                 cls: 'prev',
11830                 html: '<i class="icon-arrow-left"/>'
11831             },
11832             {
11833                 tag: 'th',
11834                 cls: 'switch',
11835                 colspan: '5'
11836             },
11837             {
11838                 tag: 'th',
11839                 cls: 'next',
11840                 html: '<i class="icon-arrow-right"/>'
11841             }
11842
11843             ]
11844         }
11845         ]
11846     },
11847     
11848     content : {
11849         tag: 'tbody',
11850         cn: [
11851         {
11852             tag: 'tr',
11853             cn: [
11854             {
11855                 tag: 'td',
11856                 colspan: '7'
11857             }
11858             ]
11859         }
11860         ]
11861     },
11862     
11863     footer : {
11864         tag: 'tfoot',
11865         cn: [
11866         {
11867             tag: 'tr',
11868             cn: [
11869             {
11870                 tag: 'th',
11871                 colspan: '7',
11872                 cls: 'picker-switch',
11873                 cn: [
11874                     {
11875                         tag: 'span',
11876                         cls: 'picker-switch glyphicon'   
11877                     }
11878                 ]
11879             }
11880                     
11881             ]
11882         }
11883         ]
11884     },
11885     
11886     dates:{
11887         en: {
11888             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11889             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11890             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11891             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11892             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11893             today: "Today"
11894         }
11895     },
11896     
11897     modes: [
11898     {
11899         clsName: 'time',
11900         navFnc: 'Time',
11901         navStep: 0
11902     },
11903     {
11904         clsName: 'days',
11905         navFnc: 'Month',
11906         navStep: 1
11907     },
11908     {
11909         clsName: 'months',
11910         navFnc: 'FullYear',
11911         navStep: 1
11912     },
11913     {
11914         clsName: 'years',
11915         navFnc: 'FullYear',
11916         navStep: 10
11917     }
11918     ]
11919 });
11920
11921 Roo.apply(Roo.bootstrap.DateField,  {
11922   
11923     template : {
11924         tag: 'div',
11925         cls: 'datepicker dropdown-menu',
11926         cn: [
11927         {
11928             tag: 'div',
11929             cls: 'datepicker-days',
11930             cn: [
11931             {
11932                 tag: 'table',
11933                 cls: 'table-condensed',
11934                 cn:[
11935                 Roo.bootstrap.DateField.head,
11936                 {
11937                     tag: 'tbody'
11938                 },
11939                 Roo.bootstrap.DateField.footer
11940                 ]
11941             }
11942             ]
11943         },
11944         {
11945             tag: 'div',
11946             cls: 'datepicker-months',
11947             cn: [
11948             {
11949                 tag: 'table',
11950                 cls: 'table-condensed',
11951                 cn:[
11952                 Roo.bootstrap.DateField.head,
11953                 Roo.bootstrap.DateField.content
11954 //                Roo.bootstrap.DateField.footer
11955                 ]
11956             }
11957             ]
11958         },
11959         {
11960             tag: 'div',
11961             cls: 'datepicker-years',
11962             cn: [
11963             {
11964                 tag: 'table',
11965                 cls: 'table-condensed',
11966                 cn:[
11967                 Roo.bootstrap.DateField.head,
11968                 Roo.bootstrap.DateField.content
11969 //                Roo.bootstrap.DateField.footer
11970                 ]
11971             }
11972             ]
11973         },
11974         {
11975             tag: 'div',
11976             cls: 'datepicker-time',
11977             cn: [
11978             {
11979                 tag: 'table',
11980                 cls: 'table-condensed',
11981                 cn:[
11982                 Roo.bootstrap.DateField.content,
11983                 Roo.bootstrap.DateField.footer
11984                 ]
11985             }
11986             ]
11987         }
11988         ]
11989     }
11990 });
11991
11992  
11993
11994  /*
11995  * - LGPL
11996  *
11997  * CheckBox
11998  * 
11999  */
12000
12001 /**
12002  * @class Roo.bootstrap.CheckBox
12003  * @extends Roo.bootstrap.Input
12004  * Bootstrap CheckBox class
12005  * 
12006  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12007  * @cfg {String} boxLabel The text that appears beside the checkbox
12008  * @cfg {Boolean} checked initnal the element
12009  * 
12010  * @constructor
12011  * Create a new CheckBox
12012  * @param {Object} config The config object
12013  */
12014
12015 Roo.bootstrap.CheckBox = function(config){
12016     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12017    
12018         this.addEvents({
12019             /**
12020             * @event check
12021             * Fires when the element is checked or unchecked.
12022             * @param {Roo.bootstrap.CheckBox} this This input
12023             * @param {Boolean} checked The new checked value
12024             */
12025            check : true
12026         });
12027 };
12028
12029 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12030     
12031     inputType: 'checkbox',
12032     value: 1,
12033     valueOff: 0,
12034     boxLabel: false,
12035     checked: false,
12036     
12037     getAutoCreate : function()
12038     {
12039         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12040         
12041         var id = Roo.id();
12042         
12043         var cfg = {};
12044         
12045         cfg.cls = 'form-group' //input-group
12046         
12047         var input =  {
12048             tag: 'input',
12049             id : id,
12050             type : this.inputType,
12051             value : (!this.checked) ? this.valueOff : this.value,
12052             cls : 'form-box',
12053             placeholder : this.placeholder || ''
12054             
12055         };
12056         
12057         if (this.disabled) {
12058             input.disabled=true;
12059         }
12060         
12061         if(this.checked){
12062             input.checked = this.checked;
12063         }
12064         
12065         if (this.name) {
12066             input.name = this.name;
12067         }
12068         
12069         if (this.size) {
12070             input.cls += ' input-' + this.size;
12071         }
12072         
12073         var settings=this;
12074         ['xs','sm','md','lg'].map(function(size){
12075             if (settings[size]) {
12076                 cfg.cls += ' col-' + size + '-' + settings[size];
12077             }
12078         });
12079         
12080         var inputblock = input;
12081         
12082         if (this.before || this.after) {
12083             
12084             inputblock = {
12085                 cls : 'input-group',
12086                 cn :  [] 
12087             };
12088             if (this.before) {
12089                 inputblock.cn.push({
12090                     tag :'span',
12091                     cls : 'input-group-addon',
12092                     html : this.before
12093                 });
12094             }
12095             inputblock.cn.push(input);
12096             if (this.after) {
12097                 inputblock.cn.push({
12098                     tag :'span',
12099                     cls : 'input-group-addon',
12100                     html : this.after
12101                 });
12102             }
12103             
12104         };
12105         
12106         if (align ==='left' && this.fieldLabel.length) {
12107                 Roo.log("left and has label");
12108                 cfg.cn = [
12109                     
12110                     {
12111                         tag: 'label',
12112                         'for' :  id,
12113                         cls : 'control-label col-md-' + this.labelWidth,
12114                         html : this.fieldLabel
12115                         
12116                     },
12117                     {
12118                         cls : "col-md-" + (12 - this.labelWidth), 
12119                         cn: [
12120                             inputblock
12121                         ]
12122                     }
12123                     
12124                 ];
12125         } else if ( this.fieldLabel.length) {
12126                 Roo.log(" label");
12127                  cfg.cn = [
12128                    
12129                     {
12130                         tag: 'label',
12131                         'for': id,
12132                         cls: 'control-label box-input-label',
12133                         //cls : 'input-group-addon',
12134                         html : this.fieldLabel
12135                         
12136                     },
12137                     
12138                     inputblock
12139                     
12140                 ];
12141
12142         } else {
12143             
12144                    Roo.log(" no label && no align");
12145                 cfg.cn = [
12146                     
12147                         inputblock
12148                     
12149                 ];
12150                 
12151                 
12152         };
12153         
12154         if(this.boxLabel){
12155             cfg.cn.push({
12156                 tag: 'span',
12157                 'for': id,
12158                 cls: 'box-label',
12159                 html: this.boxLabel
12160             })
12161         }
12162         
12163         return cfg;
12164         
12165     },
12166     
12167     /**
12168      * return the real input element.
12169      */
12170     inputEl: function ()
12171     {
12172         return this.el.select('input.form-box',true).first();
12173     },
12174     
12175     initEvents : function()
12176     {
12177         Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12178         
12179         this.inputEl().on('click', this.onClick,  this);
12180         
12181     },
12182     
12183     onClick : function()
12184     {   
12185         this.setChecked(!this.checked);
12186     },
12187     
12188     setChecked : function(state,suppressEvent)
12189     {
12190         this.checked = state;
12191         
12192         if(suppressEvent !== true){
12193             this.fireEvent('check', this, state);
12194         }
12195         
12196         this.inputEl().dom.value = state ? this.value : this.valueOff;
12197         
12198     }
12199 });
12200
12201  
12202 /*
12203  * - LGPL
12204  *
12205  * Radio
12206  * 
12207  */
12208
12209 /**
12210  * @class Roo.bootstrap.Radio
12211  * @extends Roo.bootstrap.CheckBox
12212  * Bootstrap Radio class
12213
12214  * @constructor
12215  * Create a new Radio
12216  * @param {Object} config The config object
12217  */
12218
12219 Roo.bootstrap.Radio = function(config){
12220     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12221    
12222 };
12223
12224 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12225     
12226     inputType: 'radio',
12227     
12228     getAutoCreate : function()
12229     {
12230         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12231         
12232         var id = Roo.id();
12233         
12234         var cfg = {};
12235         
12236         cfg.cls = 'form-group' //input-group
12237         
12238         var input =  {
12239             tag: 'input',
12240             id : id,
12241             type : this.inputType,
12242             value : (!this.checked) ? this.valueOff : this.value,
12243             cls : 'form-box',
12244             placeholder : this.placeholder || ''
12245             
12246         };
12247         
12248         if (this.disabled) {
12249             input.disabled=true;
12250         }
12251         
12252         if(this.checked){
12253             input.checked = this.checked;
12254         }
12255         
12256         if (this.name) {
12257             input.name = this.name;
12258         }
12259         
12260         if (this.size) {
12261             input.cls += ' input-' + this.size;
12262         }
12263         
12264         var settings=this;
12265         ['xs','sm','md','lg'].map(function(size){
12266             if (settings[size]) {
12267                 cfg.cls += ' col-' + size + '-' + settings[size];
12268             }
12269         });
12270         
12271         var inputblock = input;
12272         
12273         if (this.before || this.after) {
12274             
12275             inputblock = {
12276                 cls : 'input-group',
12277                 cn :  [] 
12278             };
12279             if (this.before) {
12280                 inputblock.cn.push({
12281                     tag :'span',
12282                     cls : 'input-group-addon',
12283                     html : this.before
12284                 });
12285             }
12286             inputblock.cn.push(input);
12287             if (this.after) {
12288                 inputblock.cn.push({
12289                     tag :'span',
12290                     cls : 'input-group-addon',
12291                     html : this.after
12292                 });
12293             }
12294             
12295         };
12296         
12297         if (align ==='left' && this.fieldLabel.length) {
12298                 Roo.log("left and has label");
12299                 cfg.cn = [
12300                     
12301                     {
12302                         tag: 'label',
12303                         'for' :  id,
12304                         cls : 'control-label col-md-' + this.labelWidth,
12305                         html : this.fieldLabel
12306                         
12307                     },
12308                     {
12309                         cls : "col-md-" + (12 - this.labelWidth), 
12310                         cn: [
12311                             inputblock
12312                         ]
12313                     }
12314                     
12315                 ];
12316         } else if ( this.fieldLabel.length) {
12317                 Roo.log(" label");
12318                  cfg.cn = [
12319                    
12320                     {
12321                         tag: 'label',
12322                         'for': id,
12323                         cls: 'control-label box-input-label',
12324                         //cls : 'input-group-addon',
12325                         html : this.fieldLabel
12326                         
12327                     },
12328                     
12329                     inputblock
12330                     
12331                 ];
12332
12333         } else {
12334             
12335                    Roo.log(" no label && no align");
12336                 cfg.cn = [
12337                     
12338                         inputblock
12339                     
12340                 ];
12341                 
12342                 
12343         };
12344         
12345         if(this.boxLabel){
12346             cfg.cn.push({
12347                 tag: 'span',
12348                 'for': id,
12349                 cls: 'box-label',
12350                 html: this.boxLabel
12351             })
12352         }
12353         
12354         return cfg;
12355         
12356     },
12357     
12358     onClick : function()
12359     {   
12360         this.setChecked(true);
12361     },
12362     
12363     setChecked : function(state,suppressEvent)
12364     {
12365         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12366             v.checked = false;
12367         });
12368         
12369         this.checked = state;
12370         
12371         if(suppressEvent !== true){
12372             this.fireEvent('check', this, state);
12373         }
12374         
12375         this.inputEl().dom.value = state ? this.value : this.valueOff;
12376         
12377     },
12378     
12379     getGroupValue : function()
12380     {
12381         if(typeof(this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true)) == 'undefined'){
12382             return '';
12383         }
12384         
12385         return this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true).value;
12386     },
12387     
12388     /**
12389      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12390      * @return {Mixed} value The field value
12391      */
12392     getValue : function(){
12393         return this.getGroupValue();
12394     }
12395     
12396 });
12397
12398  
12399 /*
12400  * - LGPL
12401  *
12402  * HtmlEditor
12403  * 
12404  */
12405
12406 /**
12407  * @class Roo.bootstrap.HtmlEditor
12408  * @extends Roo.bootstrap.Component
12409  * Bootstrap HtmlEditor class
12410
12411  * @constructor
12412  * Create a new HtmlEditor
12413  * @param {Object} config The config object
12414  */
12415
12416 Roo.bootstrap.HtmlEditor = function(config){
12417     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
12418     if (!this.toolbars) {
12419         this.toolbars = [];
12420     }
12421     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
12422     this.addEvents({
12423             /**
12424              * @event initialize
12425              * Fires when the editor is fully initialized (including the iframe)
12426              * @param {HtmlEditor} this
12427              */
12428             initialize: true,
12429             /**
12430              * @event activate
12431              * Fires when the editor is first receives the focus. Any insertion must wait
12432              * until after this event.
12433              * @param {HtmlEditor} this
12434              */
12435             activate: true,
12436              /**
12437              * @event beforesync
12438              * Fires before the textarea is updated with content from the editor iframe. Return false
12439              * to cancel the sync.
12440              * @param {HtmlEditor} this
12441              * @param {String} html
12442              */
12443             beforesync: true,
12444              /**
12445              * @event beforepush
12446              * Fires before the iframe editor is updated with content from the textarea. Return false
12447              * to cancel the push.
12448              * @param {HtmlEditor} this
12449              * @param {String} html
12450              */
12451             beforepush: true,
12452              /**
12453              * @event sync
12454              * Fires when the textarea is updated with content from the editor iframe.
12455              * @param {HtmlEditor} this
12456              * @param {String} html
12457              */
12458             sync: true,
12459              /**
12460              * @event push
12461              * Fires when the iframe editor is updated with content from the textarea.
12462              * @param {HtmlEditor} this
12463              * @param {String} html
12464              */
12465             push: true,
12466              /**
12467              * @event editmodechange
12468              * Fires when the editor switches edit modes
12469              * @param {HtmlEditor} this
12470              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
12471              */
12472             editmodechange: true,
12473             /**
12474              * @event editorevent
12475              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12476              * @param {HtmlEditor} this
12477              */
12478             editorevent: true,
12479             /**
12480              * @event firstfocus
12481              * Fires when on first focus - needed by toolbars..
12482              * @param {HtmlEditor} this
12483              */
12484             firstfocus: true,
12485             /**
12486              * @event autosave
12487              * Auto save the htmlEditor value as a file into Events
12488              * @param {HtmlEditor} this
12489              */
12490             autosave: true,
12491             /**
12492              * @event savedpreview
12493              * preview the saved version of htmlEditor
12494              * @param {HtmlEditor} this
12495              */
12496             savedpreview: true
12497         });
12498 };
12499
12500
12501 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
12502     
12503     
12504       /**
12505      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
12506      */
12507     toolbars : false,
12508    
12509      /**
12510      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12511      *                        Roo.resizable.
12512      */
12513     resizable : false,
12514      /**
12515      * @cfg {Number} height (in pixels)
12516      */   
12517     height: 300,
12518    /**
12519      * @cfg {Number} width (in pixels)
12520      */   
12521     width: 500,
12522     
12523     /**
12524      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12525      * 
12526      */
12527     stylesheets: false,
12528     
12529     // id of frame..
12530     frameId: false,
12531     
12532     // private properties
12533     validationEvent : false,
12534     deferHeight: true,
12535     initialized : false,
12536     activated : false,
12537     
12538     onFocus : Roo.emptyFn,
12539     iframePad:3,
12540     hideMode:'offsets',
12541     
12542     
12543     tbContainer : false,
12544     
12545     toolbarContainer :function() {
12546         return this.wrap.select('.x-html-editor-tb',true).first();
12547     },
12548
12549     /**
12550      * Protected method that will not generally be called directly. It
12551      * is called when the editor creates its toolbar. Override this method if you need to
12552      * add custom toolbar buttons.
12553      * @param {HtmlEditor} editor
12554      */
12555     createToolbar : function(){
12556         
12557         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
12558         this.toolbars[0].render(this.toolbarContainer());
12559          
12560         Roo.log("create toolbars");
12561         return;
12562         if (!editor.toolbars || !editor.toolbars.length) {
12563             editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
12564         }
12565         
12566         for (var i =0 ; i < editor.toolbars.length;i++) {
12567             editor.toolbars[i] = Roo.factory(
12568                     typeof(editor.toolbars[i]) == 'string' ?
12569                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
12570                 Roo.bootstrap.HtmlEditor);
12571             editor.toolbars[i].init(editor);
12572         }
12573          
12574         
12575     },
12576
12577      
12578     // private
12579     onRender : function(ct, position)
12580     {
12581        // Roo.log("Call onRender: " + this.xtype);
12582         
12583         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
12584       
12585         this.wrap = this.inputEl().wrap({
12586             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
12587         });
12588         
12589         this.editorcore.onRender(ct, position);
12590          
12591         if (this.resizable) {
12592             this.resizeEl = new Roo.Resizable(this.wrap, {
12593                 pinned : true,
12594                 wrap: true,
12595                 dynamic : true,
12596                 minHeight : this.height,
12597                 height: this.height,
12598                 handles : this.resizable,
12599                 width: this.width,
12600                 listeners : {
12601                     resize : function(r, w, h) {
12602                         _t.onResize(w,h); // -something
12603                     }
12604                 }
12605             });
12606             
12607         }
12608         this.createToolbar(this);
12609        
12610         
12611         if(!this.width){
12612             this.setSize(this.wrap.getSize());
12613         }
12614         if (this.resizeEl) {
12615             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
12616             // should trigger onReize..
12617         }
12618         
12619 //        if(this.autosave && this.w){
12620 //            this.autoSaveFn = setInterval(this.autosave, 1000);
12621 //        }
12622     },
12623
12624     // private
12625     onResize : function(w, h)
12626     {
12627         Roo.log('resize: ' +w + ',' + h );
12628         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
12629         var ew = false;
12630         var eh = false;
12631         
12632         if(this.inputEl() ){
12633             if(typeof w == 'number'){
12634                 var aw = w - this.wrap.getFrameWidth('lr');
12635                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
12636                 ew = aw;
12637             }
12638             if(typeof h == 'number'){
12639                  var tbh = -11;  // fixme it needs to tool bar size!
12640                 for (var i =0; i < this.toolbars.length;i++) {
12641                     // fixme - ask toolbars for heights?
12642                     tbh += this.toolbars[i].el.getHeight();
12643                     //if (this.toolbars[i].footer) {
12644                     //    tbh += this.toolbars[i].footer.el.getHeight();
12645                     //}
12646                 }
12647               
12648                 
12649                 
12650                 
12651                 
12652                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
12653                 ah -= 5; // knock a few pixes off for look..
12654                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
12655                 var eh = ah;
12656             }
12657         }
12658         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
12659         this.editorcore.onResize(ew,eh);
12660         
12661     },
12662
12663     /**
12664      * Toggles the editor between standard and source edit mode.
12665      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
12666      */
12667     toggleSourceEdit : function(sourceEditMode)
12668     {
12669         this.editorcore.toggleSourceEdit(sourceEditMode);
12670         
12671         if(this.editorcore.sourceEditMode){
12672             Roo.log('editor - showing textarea');
12673             
12674 //            Roo.log('in');
12675 //            Roo.log(this.syncValue());
12676             this.editorcore.syncValue();
12677             this.inputEl().removeClass('hide');
12678             this.inputEl().dom.removeAttribute('tabIndex');
12679             this.inputEl().focus();
12680         }else{
12681             Roo.log('editor - hiding textarea');
12682 //            Roo.log('out')
12683 //            Roo.log(this.pushValue()); 
12684             this.editorcore.pushValue();
12685             
12686             this.inputEl().addClass('hide');
12687             this.inputEl().dom.setAttribute('tabIndex', -1);
12688             //this.deferFocus();
12689         }
12690          
12691         this.setSize(this.wrap.getSize());
12692         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
12693     },
12694  
12695     // private (for BoxComponent)
12696     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12697
12698     // private (for BoxComponent)
12699     getResizeEl : function(){
12700         return this.wrap;
12701     },
12702
12703     // private (for BoxComponent)
12704     getPositionEl : function(){
12705         return this.wrap;
12706     },
12707
12708     // private
12709     initEvents : function(){
12710         this.originalValue = this.getValue();
12711     },
12712
12713     /**
12714      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
12715      * @method
12716      */
12717     markInvalid : Roo.emptyFn,
12718     /**
12719      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
12720      * @method
12721      */
12722     clearInvalid : Roo.emptyFn,
12723
12724     setValue : function(v){
12725         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
12726         this.editorcore.pushValue();
12727     },
12728
12729      
12730     // private
12731     deferFocus : function(){
12732         this.focus.defer(10, this);
12733     },
12734
12735     // doc'ed in Field
12736     focus : function(){
12737         this.editorcore.focus();
12738         
12739     },
12740       
12741
12742     // private
12743     onDestroy : function(){
12744         
12745         
12746         
12747         if(this.rendered){
12748             
12749             for (var i =0; i < this.toolbars.length;i++) {
12750                 // fixme - ask toolbars for heights?
12751                 this.toolbars[i].onDestroy();
12752             }
12753             
12754             this.wrap.dom.innerHTML = '';
12755             this.wrap.remove();
12756         }
12757     },
12758
12759     // private
12760     onFirstFocus : function(){
12761         //Roo.log("onFirstFocus");
12762         this.editorcore.onFirstFocus();
12763          for (var i =0; i < this.toolbars.length;i++) {
12764             this.toolbars[i].onFirstFocus();
12765         }
12766         
12767     },
12768     
12769     // private
12770     syncValue : function()
12771     {
12772         this.editorcore.syncValue();
12773     }
12774      
12775     
12776     // hide stuff that is not compatible
12777     /**
12778      * @event blur
12779      * @hide
12780      */
12781     /**
12782      * @event change
12783      * @hide
12784      */
12785     /**
12786      * @event focus
12787      * @hide
12788      */
12789     /**
12790      * @event specialkey
12791      * @hide
12792      */
12793     /**
12794      * @cfg {String} fieldClass @hide
12795      */
12796     /**
12797      * @cfg {String} focusClass @hide
12798      */
12799     /**
12800      * @cfg {String} autoCreate @hide
12801      */
12802     /**
12803      * @cfg {String} inputType @hide
12804      */
12805     /**
12806      * @cfg {String} invalidClass @hide
12807      */
12808     /**
12809      * @cfg {String} invalidText @hide
12810      */
12811     /**
12812      * @cfg {String} msgFx @hide
12813      */
12814     /**
12815      * @cfg {String} validateOnBlur @hide
12816      */
12817 });
12818  
12819     
12820    
12821    
12822    
12823     
12824 /**
12825  * @class Roo.bootstrap.Table.AbstractSelectionModel
12826  * @extends Roo.util.Observable
12827  * Abstract base class for grid SelectionModels.  It provides the interface that should be
12828  * implemented by descendant classes.  This class should not be directly instantiated.
12829  * @constructor
12830  */
12831 Roo.bootstrap.Table.AbstractSelectionModel = function(){
12832     this.locked = false;
12833     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
12834 };
12835
12836
12837 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
12838     /** @ignore Called by the grid automatically. Do not call directly. */
12839     init : function(grid){
12840         this.grid = grid;
12841         this.initEvents();
12842     },
12843
12844     /**
12845      * Locks the selections.
12846      */
12847     lock : function(){
12848         this.locked = true;
12849     },
12850
12851     /**
12852      * Unlocks the selections.
12853      */
12854     unlock : function(){
12855         this.locked = false;
12856     },
12857
12858     /**
12859      * Returns true if the selections are locked.
12860      * @return {Boolean}
12861      */
12862     isLocked : function(){
12863         return this.locked;
12864     }
12865 });
12866 /**
12867  * @class Roo.bootstrap.Table.ColumnModel
12868  * @extends Roo.util.Observable
12869  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
12870  * the columns in the table.
12871  
12872  * @constructor
12873  * @param {Object} config An Array of column config objects. See this class's
12874  * config objects for details.
12875 */
12876 Roo.bootstrap.Table.ColumnModel = function(config){
12877         /**
12878      * The config passed into the constructor
12879      */
12880     this.config = config;
12881     this.lookup = {};
12882
12883     // if no id, create one
12884     // if the column does not have a dataIndex mapping,
12885     // map it to the order it is in the config
12886     for(var i = 0, len = config.length; i < len; i++){
12887         var c = config[i];
12888         if(typeof c.dataIndex == "undefined"){
12889             c.dataIndex = i;
12890         }
12891         if(typeof c.renderer == "string"){
12892             c.renderer = Roo.util.Format[c.renderer];
12893         }
12894         if(typeof c.id == "undefined"){
12895             c.id = Roo.id();
12896         }
12897 //        if(c.editor && c.editor.xtype){
12898 //            c.editor  = Roo.factory(c.editor, Roo.grid);
12899 //        }
12900 //        if(c.editor && c.editor.isFormField){
12901 //            c.editor = new Roo.grid.GridEditor(c.editor);
12902 //        }
12903
12904         this.lookup[c.id] = c;
12905     }
12906
12907     /**
12908      * The width of columns which have no width specified (defaults to 100)
12909      * @type Number
12910      */
12911     this.defaultWidth = 100;
12912
12913     /**
12914      * Default sortable of columns which have no sortable specified (defaults to false)
12915      * @type Boolean
12916      */
12917     this.defaultSortable = false;
12918
12919     this.addEvents({
12920         /**
12921              * @event widthchange
12922              * Fires when the width of a column changes.
12923              * @param {ColumnModel} this
12924              * @param {Number} columnIndex The column index
12925              * @param {Number} newWidth The new width
12926              */
12927             "widthchange": true,
12928         /**
12929              * @event headerchange
12930              * Fires when the text of a header changes.
12931              * @param {ColumnModel} this
12932              * @param {Number} columnIndex The column index
12933              * @param {Number} newText The new header text
12934              */
12935             "headerchange": true,
12936         /**
12937              * @event hiddenchange
12938              * Fires when a column is hidden or "unhidden".
12939              * @param {ColumnModel} this
12940              * @param {Number} columnIndex The column index
12941              * @param {Boolean} hidden true if hidden, false otherwise
12942              */
12943             "hiddenchange": true,
12944             /**
12945          * @event columnmoved
12946          * Fires when a column is moved.
12947          * @param {ColumnModel} this
12948          * @param {Number} oldIndex
12949          * @param {Number} newIndex
12950          */
12951         "columnmoved" : true,
12952         /**
12953          * @event columlockchange
12954          * Fires when a column's locked state is changed
12955          * @param {ColumnModel} this
12956          * @param {Number} colIndex
12957          * @param {Boolean} locked true if locked
12958          */
12959         "columnlockchange" : true
12960     });
12961     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
12962 };
12963 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
12964     /**
12965      * @cfg {String} header The header text to display in the Grid view.
12966      */
12967     /**
12968      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
12969      * {@link Roo.data.Record} definition from which to draw the column's value. If not
12970      * specified, the column's index is used as an index into the Record's data Array.
12971      */
12972     /**
12973      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
12974      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
12975      */
12976     /**
12977      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
12978      * Defaults to the value of the {@link #defaultSortable} property.
12979      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
12980      */
12981     /**
12982      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
12983      */
12984     /**
12985      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
12986      */
12987     /**
12988      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
12989      */
12990     /**
12991      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
12992      */
12993     /**
12994      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
12995      * given the cell's data value. See {@link #setRenderer}. If not specified, the
12996      * default renderer uses the raw data value.
12997      */
12998     /**
12999      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
13000      */
13001
13002     /**
13003      * Returns the id of the column at the specified index.
13004      * @param {Number} index The column index
13005      * @return {String} the id
13006      */
13007     getColumnId : function(index){
13008         return this.config[index].id;
13009     },
13010
13011     /**
13012      * Returns the column for a specified id.
13013      * @param {String} id The column id
13014      * @return {Object} the column
13015      */
13016     getColumnById : function(id){
13017         return this.lookup[id];
13018     },
13019
13020     
13021     /**
13022      * Returns the column for a specified dataIndex.
13023      * @param {String} dataIndex The column dataIndex
13024      * @return {Object|Boolean} the column or false if not found
13025      */
13026     getColumnByDataIndex: function(dataIndex){
13027         var index = this.findColumnIndex(dataIndex);
13028         return index > -1 ? this.config[index] : false;
13029     },
13030     
13031     /**
13032      * Returns the index for a specified column id.
13033      * @param {String} id The column id
13034      * @return {Number} the index, or -1 if not found
13035      */
13036     getIndexById : function(id){
13037         for(var i = 0, len = this.config.length; i < len; i++){
13038             if(this.config[i].id == id){
13039                 return i;
13040             }
13041         }
13042         return -1;
13043     },
13044     
13045     /**
13046      * Returns the index for a specified column dataIndex.
13047      * @param {String} dataIndex The column dataIndex
13048      * @return {Number} the index, or -1 if not found
13049      */
13050     
13051     findColumnIndex : function(dataIndex){
13052         for(var i = 0, len = this.config.length; i < len; i++){
13053             if(this.config[i].dataIndex == dataIndex){
13054                 return i;
13055             }
13056         }
13057         return -1;
13058     },
13059     
13060     
13061     moveColumn : function(oldIndex, newIndex){
13062         var c = this.config[oldIndex];
13063         this.config.splice(oldIndex, 1);
13064         this.config.splice(newIndex, 0, c);
13065         this.dataMap = null;
13066         this.fireEvent("columnmoved", this, oldIndex, newIndex);
13067     },
13068
13069     isLocked : function(colIndex){
13070         return this.config[colIndex].locked === true;
13071     },
13072
13073     setLocked : function(colIndex, value, suppressEvent){
13074         if(this.isLocked(colIndex) == value){
13075             return;
13076         }
13077         this.config[colIndex].locked = value;
13078         if(!suppressEvent){
13079             this.fireEvent("columnlockchange", this, colIndex, value);
13080         }
13081     },
13082
13083     getTotalLockedWidth : function(){
13084         var totalWidth = 0;
13085         for(var i = 0; i < this.config.length; i++){
13086             if(this.isLocked(i) && !this.isHidden(i)){
13087                 this.totalWidth += this.getColumnWidth(i);
13088             }
13089         }
13090         return totalWidth;
13091     },
13092
13093     getLockedCount : function(){
13094         for(var i = 0, len = this.config.length; i < len; i++){
13095             if(!this.isLocked(i)){
13096                 return i;
13097             }
13098         }
13099     },
13100
13101     /**
13102      * Returns the number of columns.
13103      * @return {Number}
13104      */
13105     getColumnCount : function(visibleOnly){
13106         if(visibleOnly === true){
13107             var c = 0;
13108             for(var i = 0, len = this.config.length; i < len; i++){
13109                 if(!this.isHidden(i)){
13110                     c++;
13111                 }
13112             }
13113             return c;
13114         }
13115         return this.config.length;
13116     },
13117
13118     /**
13119      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
13120      * @param {Function} fn
13121      * @param {Object} scope (optional)
13122      * @return {Array} result
13123      */
13124     getColumnsBy : function(fn, scope){
13125         var r = [];
13126         for(var i = 0, len = this.config.length; i < len; i++){
13127             var c = this.config[i];
13128             if(fn.call(scope||this, c, i) === true){
13129                 r[r.length] = c;
13130             }
13131         }
13132         return r;
13133     },
13134
13135     /**
13136      * Returns true if the specified column is sortable.
13137      * @param {Number} col The column index
13138      * @return {Boolean}
13139      */
13140     isSortable : function(col){
13141         if(typeof this.config[col].sortable == "undefined"){
13142             return this.defaultSortable;
13143         }
13144         return this.config[col].sortable;
13145     },
13146
13147     /**
13148      * Returns the rendering (formatting) function defined for the column.
13149      * @param {Number} col The column index.
13150      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
13151      */
13152     getRenderer : function(col){
13153         if(!this.config[col].renderer){
13154             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
13155         }
13156         return this.config[col].renderer;
13157     },
13158
13159     /**
13160      * Sets the rendering (formatting) function for a column.
13161      * @param {Number} col The column index
13162      * @param {Function} fn The function to use to process the cell's raw data
13163      * to return HTML markup for the grid view. The render function is called with
13164      * the following parameters:<ul>
13165      * <li>Data value.</li>
13166      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
13167      * <li>css A CSS style string to apply to the table cell.</li>
13168      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
13169      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
13170      * <li>Row index</li>
13171      * <li>Column index</li>
13172      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
13173      */
13174     setRenderer : function(col, fn){
13175         this.config[col].renderer = fn;
13176     },
13177
13178     /**
13179      * Returns the width for the specified column.
13180      * @param {Number} col The column index
13181      * @return {Number}
13182      */
13183     getColumnWidth : function(col){
13184         return this.config[col].width * 1 || this.defaultWidth;
13185     },
13186
13187     /**
13188      * Sets the width for a column.
13189      * @param {Number} col The column index
13190      * @param {Number} width The new width
13191      */
13192     setColumnWidth : function(col, width, suppressEvent){
13193         this.config[col].width = width;
13194         this.totalWidth = null;
13195         if(!suppressEvent){
13196              this.fireEvent("widthchange", this, col, width);
13197         }
13198     },
13199
13200     /**
13201      * Returns the total width of all columns.
13202      * @param {Boolean} includeHidden True to include hidden column widths
13203      * @return {Number}
13204      */
13205     getTotalWidth : function(includeHidden){
13206         if(!this.totalWidth){
13207             this.totalWidth = 0;
13208             for(var i = 0, len = this.config.length; i < len; i++){
13209                 if(includeHidden || !this.isHidden(i)){
13210                     this.totalWidth += this.getColumnWidth(i);
13211                 }
13212             }
13213         }
13214         return this.totalWidth;
13215     },
13216
13217     /**
13218      * Returns the header for the specified column.
13219      * @param {Number} col The column index
13220      * @return {String}
13221      */
13222     getColumnHeader : function(col){
13223         return this.config[col].header;
13224     },
13225
13226     /**
13227      * Sets the header for a column.
13228      * @param {Number} col The column index
13229      * @param {String} header The new header
13230      */
13231     setColumnHeader : function(col, header){
13232         this.config[col].header = header;
13233         this.fireEvent("headerchange", this, col, header);
13234     },
13235
13236     /**
13237      * Returns the tooltip for the specified column.
13238      * @param {Number} col The column index
13239      * @return {String}
13240      */
13241     getColumnTooltip : function(col){
13242             return this.config[col].tooltip;
13243     },
13244     /**
13245      * Sets the tooltip for a column.
13246      * @param {Number} col The column index
13247      * @param {String} tooltip The new tooltip
13248      */
13249     setColumnTooltip : function(col, tooltip){
13250             this.config[col].tooltip = tooltip;
13251     },
13252
13253     /**
13254      * Returns the dataIndex for the specified column.
13255      * @param {Number} col The column index
13256      * @return {Number}
13257      */
13258     getDataIndex : function(col){
13259         return this.config[col].dataIndex;
13260     },
13261
13262     /**
13263      * Sets the dataIndex for a column.
13264      * @param {Number} col The column index
13265      * @param {Number} dataIndex The new dataIndex
13266      */
13267     setDataIndex : function(col, dataIndex){
13268         this.config[col].dataIndex = dataIndex;
13269     },
13270
13271     
13272     
13273     /**
13274      * Returns true if the cell is editable.
13275      * @param {Number} colIndex The column index
13276      * @param {Number} rowIndex The row index
13277      * @return {Boolean}
13278      */
13279     isCellEditable : function(colIndex, rowIndex){
13280         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
13281     },
13282
13283     /**
13284      * Returns the editor defined for the cell/column.
13285      * return false or null to disable editing.
13286      * @param {Number} colIndex The column index
13287      * @param {Number} rowIndex The row index
13288      * @return {Object}
13289      */
13290     getCellEditor : function(colIndex, rowIndex){
13291         return this.config[colIndex].editor;
13292     },
13293
13294     /**
13295      * Sets if a column is editable.
13296      * @param {Number} col The column index
13297      * @param {Boolean} editable True if the column is editable
13298      */
13299     setEditable : function(col, editable){
13300         this.config[col].editable = editable;
13301     },
13302
13303
13304     /**
13305      * Returns true if the column is hidden.
13306      * @param {Number} colIndex The column index
13307      * @return {Boolean}
13308      */
13309     isHidden : function(colIndex){
13310         return this.config[colIndex].hidden;
13311     },
13312
13313
13314     /**
13315      * Returns true if the column width cannot be changed
13316      */
13317     isFixed : function(colIndex){
13318         return this.config[colIndex].fixed;
13319     },
13320
13321     /**
13322      * Returns true if the column can be resized
13323      * @return {Boolean}
13324      */
13325     isResizable : function(colIndex){
13326         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
13327     },
13328     /**
13329      * Sets if a column is hidden.
13330      * @param {Number} colIndex The column index
13331      * @param {Boolean} hidden True if the column is hidden
13332      */
13333     setHidden : function(colIndex, hidden){
13334         this.config[colIndex].hidden = hidden;
13335         this.totalWidth = null;
13336         this.fireEvent("hiddenchange", this, colIndex, hidden);
13337     },
13338
13339     /**
13340      * Sets the editor for a column.
13341      * @param {Number} col The column index
13342      * @param {Object} editor The editor object
13343      */
13344     setEditor : function(col, editor){
13345         this.config[col].editor = editor;
13346     }
13347 });
13348
13349 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
13350         if(typeof value == "string" && value.length < 1){
13351             return "&#160;";
13352         }
13353         return value;
13354 };
13355
13356 // Alias for backwards compatibility
13357 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
13358
13359 /**
13360  * @extends Roo.bootstrap.Table.AbstractSelectionModel
13361  * @class Roo.bootstrap.Table.RowSelectionModel
13362  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
13363  * It supports multiple selections and keyboard selection/navigation. 
13364  * @constructor
13365  * @param {Object} config
13366  */
13367
13368 Roo.bootstrap.Table.RowSelectionModel = function(config){
13369     Roo.apply(this, config);
13370     this.selections = new Roo.util.MixedCollection(false, function(o){
13371         return o.id;
13372     });
13373
13374     this.last = false;
13375     this.lastActive = false;
13376
13377     this.addEvents({
13378         /**
13379              * @event selectionchange
13380              * Fires when the selection changes
13381              * @param {SelectionModel} this
13382              */
13383             "selectionchange" : true,
13384         /**
13385              * @event afterselectionchange
13386              * Fires after the selection changes (eg. by key press or clicking)
13387              * @param {SelectionModel} this
13388              */
13389             "afterselectionchange" : true,
13390         /**
13391              * @event beforerowselect
13392              * Fires when a row is selected being selected, return false to cancel.
13393              * @param {SelectionModel} this
13394              * @param {Number} rowIndex The selected index
13395              * @param {Boolean} keepExisting False if other selections will be cleared
13396              */
13397             "beforerowselect" : true,
13398         /**
13399              * @event rowselect
13400              * Fires when a row is selected.
13401              * @param {SelectionModel} this
13402              * @param {Number} rowIndex The selected index
13403              * @param {Roo.data.Record} r The record
13404              */
13405             "rowselect" : true,
13406         /**
13407              * @event rowdeselect
13408              * Fires when a row is deselected.
13409              * @param {SelectionModel} this
13410              * @param {Number} rowIndex The selected index
13411              */
13412         "rowdeselect" : true
13413     });
13414     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
13415     this.locked = false;
13416 };
13417
13418 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
13419     /**
13420      * @cfg {Boolean} singleSelect
13421      * True to allow selection of only one row at a time (defaults to false)
13422      */
13423     singleSelect : false,
13424
13425     // private
13426     initEvents : function(){
13427
13428         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
13429             this.grid.on("mousedown", this.handleMouseDown, this);
13430         }else{ // allow click to work like normal
13431             this.grid.on("rowclick", this.handleDragableRowClick, this);
13432         }
13433
13434         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
13435             "up" : function(e){
13436                 if(!e.shiftKey){
13437                     this.selectPrevious(e.shiftKey);
13438                 }else if(this.last !== false && this.lastActive !== false){
13439                     var last = this.last;
13440                     this.selectRange(this.last,  this.lastActive-1);
13441                     this.grid.getView().focusRow(this.lastActive);
13442                     if(last !== false){
13443                         this.last = last;
13444                     }
13445                 }else{
13446                     this.selectFirstRow();
13447                 }
13448                 this.fireEvent("afterselectionchange", this);
13449             },
13450             "down" : function(e){
13451                 if(!e.shiftKey){
13452                     this.selectNext(e.shiftKey);
13453                 }else if(this.last !== false && this.lastActive !== false){
13454                     var last = this.last;
13455                     this.selectRange(this.last,  this.lastActive+1);
13456                     this.grid.getView().focusRow(this.lastActive);
13457                     if(last !== false){
13458                         this.last = last;
13459                     }
13460                 }else{
13461                     this.selectFirstRow();
13462                 }
13463                 this.fireEvent("afterselectionchange", this);
13464             },
13465             scope: this
13466         });
13467
13468         var view = this.grid.view;
13469         view.on("refresh", this.onRefresh, this);
13470         view.on("rowupdated", this.onRowUpdated, this);
13471         view.on("rowremoved", this.onRemove, this);
13472     },
13473
13474     // private
13475     onRefresh : function(){
13476         var ds = this.grid.dataSource, i, v = this.grid.view;
13477         var s = this.selections;
13478         s.each(function(r){
13479             if((i = ds.indexOfId(r.id)) != -1){
13480                 v.onRowSelect(i);
13481             }else{
13482                 s.remove(r);
13483             }
13484         });
13485     },
13486
13487     // private
13488     onRemove : function(v, index, r){
13489         this.selections.remove(r);
13490     },
13491
13492     // private
13493     onRowUpdated : function(v, index, r){
13494         if(this.isSelected(r)){
13495             v.onRowSelect(index);
13496         }
13497     },
13498
13499     /**
13500      * Select records.
13501      * @param {Array} records The records to select
13502      * @param {Boolean} keepExisting (optional) True to keep existing selections
13503      */
13504     selectRecords : function(records, keepExisting){
13505         if(!keepExisting){
13506             this.clearSelections();
13507         }
13508         var ds = this.grid.dataSource;
13509         for(var i = 0, len = records.length; i < len; i++){
13510             this.selectRow(ds.indexOf(records[i]), true);
13511         }
13512     },
13513
13514     /**
13515      * Gets the number of selected rows.
13516      * @return {Number}
13517      */
13518     getCount : function(){
13519         return this.selections.length;
13520     },
13521
13522     /**
13523      * Selects the first row in the grid.
13524      */
13525     selectFirstRow : function(){
13526         this.selectRow(0);
13527     },
13528
13529     /**
13530      * Select the last row.
13531      * @param {Boolean} keepExisting (optional) True to keep existing selections
13532      */
13533     selectLastRow : function(keepExisting){
13534         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
13535     },
13536
13537     /**
13538      * Selects the row immediately following the last selected row.
13539      * @param {Boolean} keepExisting (optional) True to keep existing selections
13540      */
13541     selectNext : function(keepExisting){
13542         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
13543             this.selectRow(this.last+1, keepExisting);
13544             this.grid.getView().focusRow(this.last);
13545         }
13546     },
13547
13548     /**
13549      * Selects the row that precedes the last selected row.
13550      * @param {Boolean} keepExisting (optional) True to keep existing selections
13551      */
13552     selectPrevious : function(keepExisting){
13553         if(this.last){
13554             this.selectRow(this.last-1, keepExisting);
13555             this.grid.getView().focusRow(this.last);
13556         }
13557     },
13558
13559     /**
13560      * Returns the selected records
13561      * @return {Array} Array of selected records
13562      */
13563     getSelections : function(){
13564         return [].concat(this.selections.items);
13565     },
13566
13567     /**
13568      * Returns the first selected record.
13569      * @return {Record}
13570      */
13571     getSelected : function(){
13572         return this.selections.itemAt(0);
13573     },
13574
13575
13576     /**
13577      * Clears all selections.
13578      */
13579     clearSelections : function(fast){
13580         if(this.locked) return;
13581         if(fast !== true){
13582             var ds = this.grid.dataSource;
13583             var s = this.selections;
13584             s.each(function(r){
13585                 this.deselectRow(ds.indexOfId(r.id));
13586             }, this);
13587             s.clear();
13588         }else{
13589             this.selections.clear();
13590         }
13591         this.last = false;
13592     },
13593
13594
13595     /**
13596      * Selects all rows.
13597      */
13598     selectAll : function(){
13599         if(this.locked) return;
13600         this.selections.clear();
13601         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
13602             this.selectRow(i, true);
13603         }
13604     },
13605
13606     /**
13607      * Returns True if there is a selection.
13608      * @return {Boolean}
13609      */
13610     hasSelection : function(){
13611         return this.selections.length > 0;
13612     },
13613
13614     /**
13615      * Returns True if the specified row is selected.
13616      * @param {Number/Record} record The record or index of the record to check
13617      * @return {Boolean}
13618      */
13619     isSelected : function(index){
13620         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
13621         return (r && this.selections.key(r.id) ? true : false);
13622     },
13623
13624     /**
13625      * Returns True if the specified record id is selected.
13626      * @param {String} id The id of record to check
13627      * @return {Boolean}
13628      */
13629     isIdSelected : function(id){
13630         return (this.selections.key(id) ? true : false);
13631     },
13632
13633     // private
13634     handleMouseDown : function(e, t){
13635         var view = this.grid.getView(), rowIndex;
13636         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
13637             return;
13638         };
13639         if(e.shiftKey && this.last !== false){
13640             var last = this.last;
13641             this.selectRange(last, rowIndex, e.ctrlKey);
13642             this.last = last; // reset the last
13643             view.focusRow(rowIndex);
13644         }else{
13645             var isSelected = this.isSelected(rowIndex);
13646             if(e.button !== 0 && isSelected){
13647                 view.focusRow(rowIndex);
13648             }else if(e.ctrlKey && isSelected){
13649                 this.deselectRow(rowIndex);
13650             }else if(!isSelected){
13651                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
13652                 view.focusRow(rowIndex);
13653             }
13654         }
13655         this.fireEvent("afterselectionchange", this);
13656     },
13657     // private
13658     handleDragableRowClick :  function(grid, rowIndex, e) 
13659     {
13660         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
13661             this.selectRow(rowIndex, false);
13662             grid.view.focusRow(rowIndex);
13663              this.fireEvent("afterselectionchange", this);
13664         }
13665     },
13666     
13667     /**
13668      * Selects multiple rows.
13669      * @param {Array} rows Array of the indexes of the row to select
13670      * @param {Boolean} keepExisting (optional) True to keep existing selections
13671      */
13672     selectRows : function(rows, keepExisting){
13673         if(!keepExisting){
13674             this.clearSelections();
13675         }
13676         for(var i = 0, len = rows.length; i < len; i++){
13677             this.selectRow(rows[i], true);
13678         }
13679     },
13680
13681     /**
13682      * Selects a range of rows. All rows in between startRow and endRow are also selected.
13683      * @param {Number} startRow The index of the first row in the range
13684      * @param {Number} endRow The index of the last row in the range
13685      * @param {Boolean} keepExisting (optional) True to retain existing selections
13686      */
13687     selectRange : function(startRow, endRow, keepExisting){
13688         if(this.locked) return;
13689         if(!keepExisting){
13690             this.clearSelections();
13691         }
13692         if(startRow <= endRow){
13693             for(var i = startRow; i <= endRow; i++){
13694                 this.selectRow(i, true);
13695             }
13696         }else{
13697             for(var i = startRow; i >= endRow; i--){
13698                 this.selectRow(i, true);
13699             }
13700         }
13701     },
13702
13703     /**
13704      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
13705      * @param {Number} startRow The index of the first row in the range
13706      * @param {Number} endRow The index of the last row in the range
13707      */
13708     deselectRange : function(startRow, endRow, preventViewNotify){
13709         if(this.locked) return;
13710         for(var i = startRow; i <= endRow; i++){
13711             this.deselectRow(i, preventViewNotify);
13712         }
13713     },
13714
13715     /**
13716      * Selects a row.
13717      * @param {Number} row The index of the row to select
13718      * @param {Boolean} keepExisting (optional) True to keep existing selections
13719      */
13720     selectRow : function(index, keepExisting, preventViewNotify){
13721         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
13722         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
13723             if(!keepExisting || this.singleSelect){
13724                 this.clearSelections();
13725             }
13726             var r = this.grid.dataSource.getAt(index);
13727             this.selections.add(r);
13728             this.last = this.lastActive = index;
13729             if(!preventViewNotify){
13730                 this.grid.getView().onRowSelect(index);
13731             }
13732             this.fireEvent("rowselect", this, index, r);
13733             this.fireEvent("selectionchange", this);
13734         }
13735     },
13736
13737     /**
13738      * Deselects a row.
13739      * @param {Number} row The index of the row to deselect
13740      */
13741     deselectRow : function(index, preventViewNotify){
13742         if(this.locked) return;
13743         if(this.last == index){
13744             this.last = false;
13745         }
13746         if(this.lastActive == index){
13747             this.lastActive = false;
13748         }
13749         var r = this.grid.dataSource.getAt(index);
13750         this.selections.remove(r);
13751         if(!preventViewNotify){
13752             this.grid.getView().onRowDeselect(index);
13753         }
13754         this.fireEvent("rowdeselect", this, index);
13755         this.fireEvent("selectionchange", this);
13756     },
13757
13758     // private
13759     restoreLast : function(){
13760         if(this._last){
13761             this.last = this._last;
13762         }
13763     },
13764
13765     // private
13766     acceptsNav : function(row, col, cm){
13767         return !cm.isHidden(col) && cm.isCellEditable(col, row);
13768     },
13769
13770     // private
13771     onEditorKey : function(field, e){
13772         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
13773         if(k == e.TAB){
13774             e.stopEvent();
13775             ed.completeEdit();
13776             if(e.shiftKey){
13777                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
13778             }else{
13779                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
13780             }
13781         }else if(k == e.ENTER && !e.ctrlKey){
13782             e.stopEvent();
13783             ed.completeEdit();
13784             if(e.shiftKey){
13785                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
13786             }else{
13787                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
13788             }
13789         }else if(k == e.ESC){
13790             ed.cancelEdit();
13791         }
13792         if(newCell){
13793             g.startEditing(newCell[0], newCell[1]);
13794         }
13795     }
13796 });