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     altFormats : false,
10876     
10877     weekStart : 0,
10878     
10879     viewMode : 'day',
10880     
10881     minViewMode : '',
10882     
10883     todayHighlight : false,
10884     
10885     todayBtn: false,
10886     
10887     language: 'en',
10888     
10889     keyboardNavigation: true,
10890     
10891     calendarWeeks: false,
10892     
10893     startDate: -Infinity,
10894     
10895     endDate: Infinity,
10896     
10897     daysOfWeekDisabled: [],
10898     
10899     showTime : true,
10900     
10901     UTCDate: function()
10902     {
10903         return new Date(Date.UTC.apply(Date, arguments));
10904     },
10905     
10906     UTCTime: function()
10907     {
10908         return new Date(Date.UTC.apply(Date, arguments));
10909     },
10910     
10911     UTCToday: function()
10912     {
10913         var today = new Date();
10914         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
10915     },
10916     
10917     UTCTodayTime: function()
10918     {
10919         var today = new Date();
10920         return this.UTCTime(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate(), today.getUTCHours(), today.getUTCMinutes());
10921     },
10922     
10923     getDate: function() {
10924             var d = this.getUTCDate();
10925             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
10926     },
10927     
10928     getUTCDate: function() {
10929             return this.date;
10930     },
10931     
10932     setDate: function(d) {
10933             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
10934     },
10935     
10936     setUTCDate: function(d) {
10937             this.date = d;
10938             this.setValue(this.formatDate(this.date));
10939     },
10940         
10941     onRender: function(ct, position)
10942     {
10943         
10944         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
10945         
10946         this.language = this.language || 'en';
10947         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
10948         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
10949         
10950         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
10951         this.format = this.format || 'm/d/y';
10952         this.isInline = false;
10953         this.isInput = true;
10954         this.component = this.el.select('.add-on', true).first() || false;
10955         this.component = (this.component && this.component.length === 0) ? false : this.component;
10956         this.hasInput = this.component && this.inputEL().length;
10957         
10958         if (typeof(this.minViewMode === 'string')) {
10959             switch (this.minViewMode) {
10960                 case 'months':
10961                     this.minViewMode = 2;
10962                     break;
10963                 case 'years':
10964                     this.minViewMode = 3;
10965                     break;
10966                 case 'day':
10967                     this.minViewMode = 1;
10968                     break;
10969                 default:
10970                     this.minViewMode = 0;
10971                     break;
10972             }
10973         }
10974         
10975         if (typeof(this.viewMode === 'string')) {
10976             switch (this.viewMode) {
10977                 case 'months':
10978                     this.viewMode = 2;
10979                     break;
10980                 case 'years':
10981                     this.viewMode = 2;
10982                     break;
10983                 case 'day':
10984                     this.viewMode = 1;
10985                     break;
10986                 default:
10987                     this.viewMode = 0;
10988                     break;
10989             }
10990         }
10991                 
10992         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
10993         
10994         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10995         
10996         this.picker().on('mousedown', this.onMousedown, this);
10997         this.picker().on('click', this.onClick, this);
10998         
10999         this.picker().addClass('datepicker-dropdown');
11000         
11001         this.startViewMode = this.viewMode;
11002         
11003         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11004         
11005         this.setStartDate(this.startDate);
11006         this.setEndDate(this.endDate);
11007         
11008         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11009         
11010         this.fillDow();
11011         this.fillMonths();
11012         this.fillTime();
11013         this.update();
11014         this.showMode();
11015         
11016         if(this.showTime){
11017             
11018             var dayFoot = this.picker().select('>.datepicker-days tfoot th', true).first();
11019             var timeFoot = this.picker().select('>.datepicker-time tfoot th', true).first();
11020
11021             var dayFootIcon = this.picker().select('>.datepicker-days tfoot span.picker-switch-icon', true).first();
11022             var timeFootIcon = this.picker().select('>.datepicker-time tfoot span.picker-switch-icon', true).first();
11023             
11024             timeFoot.addClass('switch-calendar');
11025             dayFoot.addClass('switch-time');
11026             
11027             timeFootIcon.addClass('switch-calendar');
11028             timeFootIcon.addClass('glyphicon-calendar');
11029             
11030             dayFootIcon.addClass('switch-time');
11031             dayFootIcon.addClass('glyphicon-time');
11032             
11033             var hours_up = this.picker().select('>.datepicker-time span.hours-up', true).first();
11034             var hours_down = this.picker().select('>.datepicker-time span.hours-down', true).first();
11035             var minutes_up = this.picker().select('>.datepicker-time span.minutes-up', true).first();
11036             var minutes_down = this.picker().select('>.datepicker-time span.minutes-down', true).first();
11037             
11038             var period = this.picker().select('>.datepicker-time button.period-btn', true).first();
11039             
11040             hours_up.on('click', this.onIncrementHours, this);
11041             hours_down.on('click', this.onDecrementHours, this);
11042             minutes_up.on('click', this.onIncrementMinutes, this);
11043             minutes_down.on('click', this.onDecrementMinutes, this);
11044             
11045             period.on('click', this.onTogglePeriod, this);
11046             
11047             this.updateTime();
11048             
11049         }else{
11050             Roo.each(this.picker().select('tfoot th', true).elements, function(v){
11051                 v.remove();
11052             });
11053         }
11054         
11055         if(this.isInline) {
11056             this.show();
11057         }
11058     },
11059     
11060     picker : function()
11061     {
11062         return this.el.select('.datepicker', true).first();
11063     },
11064     
11065     fillDow: function()
11066     {
11067         var dowCnt = this.weekStart;
11068         
11069         var dow = {
11070             tag: 'tr',
11071             cn: [
11072                 
11073             ]
11074         };
11075         
11076         if(this.calendarWeeks){
11077             dow.cn.push({
11078                 tag: 'th',
11079                 cls: 'cw',
11080                 html: '&nbsp;'
11081             })
11082         }
11083         
11084         while (dowCnt < this.weekStart + 7) {
11085             dow.cn.push({
11086                 tag: 'th',
11087                 cls: 'dow',
11088                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11089             });
11090         }
11091         
11092         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11093     },
11094     
11095     fillMonths: function()
11096     {    
11097         var i = 0
11098         var months = this.picker().select('>.datepicker-months td', true).first();
11099         
11100         months.dom.innerHTML = '';
11101         
11102         while (i < 12) {
11103             var month = {
11104                 tag: 'span',
11105                 cls: 'month',
11106                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11107             }
11108             
11109             months.createChild(month);
11110         }
11111         
11112     },
11113     
11114     fillTime: function()
11115     {    
11116         var time = this.picker().select('>.datepicker-time tbody', true).first();
11117         
11118         time.dom.innerHTML = '';
11119         
11120         time.createChild({
11121             tag: 'tr',
11122             cn: [
11123                 {
11124                     tag: 'td',
11125                     cn: [
11126                         {
11127                             tag: 'a',
11128                             href: '#',
11129                             cls: 'btn',
11130                             cn: [
11131                                 {
11132                                     tag: 'span',
11133                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
11134                                 }
11135                             ]
11136                         } 
11137                     ]
11138                 },
11139                 {
11140                     tag: 'td',
11141                     cls: 'separator'
11142                 },
11143                 {
11144                     tag: 'td',
11145                     cn: [
11146                         {
11147                             tag: 'a',
11148                             href: '#',
11149                             cls: 'btn',
11150                             cn: [
11151                                 {
11152                                     tag: 'span',
11153                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
11154                                 }
11155                             ]
11156                         }
11157                     ]
11158                 },
11159                 {
11160                     tag: 'td',
11161                     cls: 'separator'
11162                 }
11163             ]
11164         });
11165         
11166         time.createChild({
11167             tag: 'tr',
11168             cn: [
11169                 {
11170                     tag: 'td',
11171                     cn: [
11172                         {
11173                             tag: 'span',
11174                             cls: 'timepicker-hour',
11175                             html: '00'
11176                         }  
11177                     ]
11178                 },
11179                 {
11180                     tag: 'td',
11181                     cls: 'separator',
11182                     html: ':'
11183                 },
11184                 {
11185                     tag: 'td',
11186                     cn: [
11187                         {
11188                             tag: 'span',
11189                             cls: 'timepicker-minute',
11190                             html: '00'
11191                         }  
11192                     ]
11193                 },
11194                 {
11195                     tag: 'td',
11196                     cls: 'separator'
11197                 },
11198                 {
11199                     tag: 'td',
11200                     cn: [
11201                         {
11202                             tag: 'button',
11203                             type: 'button',
11204                             cls: 'btn btn-primary period-btn',
11205                             html: 'AM'
11206                             
11207                         }
11208                     ]
11209                 }
11210             ]
11211         });
11212         
11213         time.createChild({
11214             tag: 'tr',
11215             cn: [
11216                 {
11217                     tag: 'td',
11218                     cn: [
11219                         {
11220                             tag: 'a',
11221                             href: '#',
11222                             cls: 'btn',
11223                             cn: [
11224                                 {
11225                                     tag: 'span',
11226                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
11227                                 }
11228                             ]
11229                         }
11230                     ]
11231                 },
11232                 {
11233                     tag: 'td',
11234                     cls: 'separator'
11235                 },
11236                 {
11237                     tag: 'td',
11238                     cn: [
11239                         {
11240                             tag: 'a',
11241                             href: '#',
11242                             cls: 'btn',
11243                             cn: [
11244                                 {
11245                                     tag: 'span',
11246                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
11247                                 }
11248                             ]
11249                         }
11250                     ]
11251                 },
11252                 {
11253                     tag: 'td',
11254                     cls: 'separator'
11255                 }
11256             ]
11257         });
11258         
11259     },
11260     
11261     update: function(){
11262         
11263         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11264         
11265         if (this.date < this.startDate) {
11266             this.viewDate = new Date(this.startDate);
11267         } else if (this.date > this.endDate) {
11268             this.viewDate = new Date(this.endDate);
11269         } else {
11270             this.viewDate = new Date(this.date);
11271         }
11272         
11273         this.fill();
11274     },
11275     
11276     fill: function() {
11277         var d = new Date(this.viewDate),
11278                 year = d.getUTCFullYear(),
11279                 month = d.getUTCMonth(),
11280                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11281                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11282                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11283                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11284                 currentDate = this.date && this.date.valueOf(),
11285                 today = this.UTCToday();
11286         
11287         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11288         
11289 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11290         
11291 //        this.picker.select('>tfoot th.today').
11292 //                                              .text(dates[this.language].today)
11293 //                                              .toggle(this.todayBtn !== false);
11294     
11295         this.updateNavArrows();
11296         this.fillMonths();
11297                                                 
11298         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11299         
11300         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11301          
11302         prevMonth.setUTCDate(day);
11303         
11304         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11305         
11306         var nextMonth = new Date(prevMonth);
11307         
11308         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11309         
11310         nextMonth = nextMonth.valueOf();
11311         
11312         var fillMonths = false;
11313         
11314         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11315         
11316         while(prevMonth.valueOf() < nextMonth) {
11317             var clsName = '';
11318             
11319             if (prevMonth.getUTCDay() === this.weekStart) {
11320                 if(fillMonths){
11321                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11322                 }
11323                     
11324                 fillMonths = {
11325                     tag: 'tr',
11326                     cn: []
11327                 };
11328                 
11329                 if(this.calendarWeeks){
11330                     // ISO 8601: First week contains first thursday.
11331                     // ISO also states week starts on Monday, but we can be more abstract here.
11332                     var
11333                     // Start of current week: based on weekstart/current date
11334                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11335                     // Thursday of this week
11336                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11337                     // First Thursday of year, year from thursday
11338                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11339                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11340                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11341                     
11342                     fillMonths.cn.push({
11343                         tag: 'td',
11344                         cls: 'cw',
11345                         html: calWeek
11346                     });
11347                 }
11348             }
11349             
11350             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11351                 clsName += ' old';
11352             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11353                 clsName += ' new';
11354             }
11355             if (this.todayHighlight &&
11356                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11357                 prevMonth.getUTCMonth() == today.getMonth() &&
11358                 prevMonth.getUTCDate() == today.getDate()) {
11359                 clsName += ' today';
11360             }
11361             
11362             if (currentDate && prevMonth.valueOf() === currentDate) {
11363                 clsName += ' active';
11364             }
11365             
11366             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11367                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11368                     clsName += ' disabled';
11369             }
11370             
11371             fillMonths.cn.push({
11372                 tag: 'td',
11373                 cls: 'day ' + clsName,
11374                 html: prevMonth.getDate()
11375             })
11376             
11377             prevMonth.setDate(prevMonth.getDate()+1);
11378         }
11379           
11380         var currentYear = this.date && this.date.getUTCFullYear();
11381         var currentMonth = this.date && this.date.getUTCMonth();
11382         
11383         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11384         
11385         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11386             v.removeClass('active');
11387             
11388             if(currentYear === year && k === currentMonth){
11389                 v.addClass('active');
11390             }
11391             
11392             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11393                 v.addClass('disabled');
11394             }
11395             
11396         });
11397         
11398         
11399         year = parseInt(year/10, 10) * 10;
11400         
11401         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11402         
11403         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11404         
11405         year -= 1;
11406         for (var i = -1; i < 11; i++) {
11407             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11408                 tag: 'span',
11409                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11410                 html: year
11411             })
11412             
11413             year += 1;
11414         }
11415         
11416     },
11417     
11418     updateTime: function()
11419     {
11420         this.time = (typeof(this.time) === 'undefined') ? this.UTCTodayTime() : this.time;
11421         
11422         var hours = this.time.getHours();
11423         var minutes = this.time.getMinutes();
11424
11425         if(hours * 1 > 12){
11426             hours -= 12;
11427         }
11428
11429         if(hours < 10){
11430             hours = '0' + hours;
11431         }
11432
11433         if(minutes < 10){
11434             minutes = '0' + minutes;
11435         }
11436
11437         var period = this.time.format('A');
11438
11439         this.picker().select('>.datepicker-time span.timepicker-hour', true).first().dom.innerHTML = hours;
11440
11441         this.picker().select('>.datepicker-time span.timepicker-minute', true).first().dom.innerHTML = minutes;
11442
11443         this.picker().select('>.datepicker-time button.period-btn', true).first().dom.innerHTML = period;
11444     },
11445     
11446     showMode: function(dir) {
11447         if (dir) {
11448             this.viewMode = Math.max(this.minViewMode, Math.min(3, this.viewMode + dir));
11449         }
11450         
11451         Roo.each(this.picker().select('>div',true).elements, function(v){
11452             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11453             v.hide();
11454         });
11455         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11456         
11457     },
11458     
11459     place: function()
11460     {
11461         if(this.isInline) return;
11462         
11463         this.picker().removeClass(['bottom', 'top']);
11464         
11465         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11466             /*
11467              * place to the top of element!
11468              *
11469              */
11470             
11471             this.picker().addClass('top');
11472             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11473             
11474             return;
11475         }
11476         
11477         this.picker().addClass('bottom');
11478         
11479         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11480     },
11481     
11482     parseDate : function(value){
11483         if(!value || value instanceof Date){
11484             return value;
11485         }
11486         var v = Date.parseDate(value, this.format);
11487         if (!v && this.useIso) {
11488             v = Date.parseDate(value, 'Y-m-d');
11489         }
11490         if(!v && this.altFormats){
11491             if(!this.altFormatsArray){
11492                 this.altFormatsArray = this.altFormats.split("|");
11493             }
11494             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11495                 v = Date.parseDate(value, this.altFormatsArray[i]);
11496             }
11497         }
11498         return v;
11499     },
11500     
11501     formatDate : function(date, fmt){
11502         return (!date || !(date instanceof Date)) ?
11503         date : date.dateFormat(fmt || this.format);
11504     },
11505     
11506     onFocus : function()
11507     {
11508         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11509         this.show();
11510     },
11511     
11512     onBlur : function()
11513     {
11514         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11515         this.hide();
11516     },
11517     
11518     show : function()
11519     {
11520         this.picker().show();
11521         this.update();
11522         this.place();
11523     },
11524     
11525     hide : function()
11526     {
11527         if(this.isInline) return;
11528         this.picker().hide();
11529         this.viewMode = this.startViewMode;
11530         this.showMode();
11531         
11532     },
11533     
11534     onMousedown: function(e){
11535         e.stopPropagation();
11536         e.preventDefault();
11537     },
11538     
11539     keyup: function(e){
11540         Roo.bootstrap.DateField.superclass.keyup.call(this);
11541         this.update();
11542         
11543     },
11544     
11545     fireKey: function(e){
11546         if (!this.picker().isVisible()){
11547             if (e.keyCode == 27) // allow escape to hide and re-show picker
11548                 this.show();
11549             return;
11550         }
11551         var dateChanged = false,
11552         dir, day, month,
11553         newDate, newViewDate;
11554         switch(e.keyCode){
11555             case 27: // escape
11556                 this.hide();
11557                 e.preventDefault();
11558                 break;
11559             case 37: // left
11560             case 39: // right
11561                 if (!this.keyboardNavigation) break;
11562                 dir = e.keyCode == 37 ? -1 : 1;
11563                 
11564                 if (e.ctrlKey){
11565                     newDate = this.moveYear(this.date, dir);
11566                     newViewDate = this.moveYear(this.viewDate, dir);
11567                 } else if (e.shiftKey){
11568                     newDate = this.moveMonth(this.date, dir);
11569                     newViewDate = this.moveMonth(this.viewDate, dir);
11570                 } else {
11571                     newDate = new Date(this.date);
11572                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11573                     newViewDate = new Date(this.viewDate);
11574                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11575                 }
11576                 if (this.dateWithinRange(newDate)){
11577                     this.date = newDate;
11578                     this.viewDate = newViewDate;
11579                     this.setValue(this.formatDate(this.date));
11580                     this.update();
11581                     e.preventDefault();
11582                     dateChanged = true;
11583                 }
11584                 break;
11585             case 38: // up
11586             case 40: // down
11587                 if (!this.keyboardNavigation) break;
11588                 dir = e.keyCode == 38 ? -1 : 1;
11589                 if (e.ctrlKey){
11590                     newDate = this.moveYear(this.date, dir);
11591                     newViewDate = this.moveYear(this.viewDate, dir);
11592                 } else if (e.shiftKey){
11593                     newDate = this.moveMonth(this.date, dir);
11594                     newViewDate = this.moveMonth(this.viewDate, dir);
11595                 } else {
11596                     newDate = new Date(this.date);
11597                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11598                     newViewDate = new Date(this.viewDate);
11599                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11600                 }
11601                 if (this.dateWithinRange(newDate)){
11602                     this.date = newDate;
11603                     this.viewDate = newViewDate;
11604                     this.setValue(this.formatDate(this.date));
11605                     this.update();
11606                     e.preventDefault();
11607                     dateChanged = true;
11608                 }
11609                 break;
11610             case 13: // enter
11611                 this.setValue(this.formatDate(this.date));
11612                 this.hide();
11613                 e.preventDefault();
11614                 break;
11615             case 9: // tab
11616                 this.setValue(this.formatDate(this.date));
11617                 this.hide();
11618                 break;
11619         }
11620     },
11621     
11622     
11623     onClick: function(e) {
11624         e.stopPropagation();
11625         e.preventDefault();
11626         
11627         var target = e.getTarget();
11628         
11629         if(target.nodeName.toLowerCase() === 'i'){
11630             target = Roo.get(target).dom.parentNode;
11631         }
11632         
11633         var nodeName = target.nodeName.trim();
11634         var className = target.className.trim();
11635         var html = target.innerHTML;
11636         
11637         switch(nodeName.toLowerCase()) {
11638             case 'th':
11639                 switch(className) {
11640                     case 'switch':
11641                         this.showMode(1);
11642                         break;
11643                     case 'prev':
11644                     case 'next':
11645                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11646                         switch(this.viewMode){
11647                                 case 0:
11648                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11649                                         break;
11650                                 case 1:
11651                                 case 2:
11652                                         this.viewDate = this.moveYear(this.viewDate, dir);
11653                                         break;
11654                         }
11655                         this.fill();
11656                         break;
11657                     case 'today':
11658                         var date = new Date();
11659                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11660                         this.fill()
11661                         this.setValue(this.formatDate(this.date));
11662                         this.hide();
11663                         break;
11664                      case 'switch-time':
11665                         this.showMode(-1);
11666                         this.fill();
11667                         break;
11668                      case 'switch-calendar':
11669                          this.showMode(1);
11670                          this.fill();
11671                          break;
11672                 }
11673                 break;
11674             case 'span':
11675                 if (className.indexOf('disabled') === -1) {
11676                     this.viewDate.setUTCDate(1);
11677                     if (className.indexOf('month') !== -1) {
11678                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11679                     } else if(className.indexOf('picker-switch-icon') !== -1){
11680                         if(className.indexOf('switch-time') !== -1){
11681                             this.showMode(-1);
11682                             this.fill();
11683                         }else{
11684                             this.showMode(1);
11685                             this.fill();
11686                         }
11687                         
11688                         break;
11689                         
11690                     } else {
11691                         var year = parseInt(html, 10) || 0;
11692                         this.viewDate.setUTCFullYear(year);
11693                         
11694                     }
11695                     this.showMode(-1);
11696                     this.fill();
11697                 }
11698                 break;
11699                 
11700             case 'td':
11701                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11702                     var day = parseInt(html, 10) || 1;
11703                     var year = this.viewDate.getUTCFullYear(),
11704                         month = this.viewDate.getUTCMonth();
11705
11706                     if (className.indexOf('old') !== -1) {
11707                         if(month === 0 ){
11708                             month = 11;
11709                             year -= 1;
11710                         }else{
11711                             month -= 1;
11712                         }
11713                     } else if (className.indexOf('new') !== -1) {
11714                         if (month == 11) {
11715                             month = 0;
11716                             year += 1;
11717                         } else {
11718                             month += 1;
11719                         }
11720                     }
11721                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11722                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11723                     this.fill();
11724                     this.setValue(this.formatDate(this.date));
11725                     this.hide();
11726                 }
11727                 break;
11728         }
11729     },
11730     
11731     setStartDate: function(startDate){
11732         this.startDate = startDate || -Infinity;
11733         if (this.startDate !== -Infinity) {
11734             this.startDate = this.parseDate(this.startDate);
11735         }
11736         this.update();
11737         this.updateNavArrows();
11738     },
11739
11740     setEndDate: function(endDate){
11741         this.endDate = endDate || Infinity;
11742         if (this.endDate !== Infinity) {
11743             this.endDate = this.parseDate(this.endDate);
11744         }
11745         this.update();
11746         this.updateNavArrows();
11747     },
11748     
11749     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11750         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11751         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11752             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11753         }
11754         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11755             return parseInt(d, 10);
11756         });
11757         this.update();
11758         this.updateNavArrows();
11759     },
11760     
11761     updateNavArrows: function() {
11762         var d = new Date(this.viewDate),
11763         year = d.getUTCFullYear(),
11764         month = d.getUTCMonth();
11765         
11766         Roo.each(this.picker().select('.prev', true).elements, function(v){
11767             v.show();
11768             switch (this.viewMode) {
11769                 case 0:
11770
11771                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11772                         v.hide();
11773                     }
11774                     break;
11775                 case 1:
11776                 case 2:
11777                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11778                         v.hide();
11779                     }
11780                     break;
11781             }
11782         });
11783         
11784         Roo.each(this.picker().select('.next', true).elements, function(v){
11785             v.show();
11786             switch (this.viewMode) {
11787                 case 0:
11788
11789                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11790                         v.hide();
11791                     }
11792                     break;
11793                 case 1:
11794                 case 2:
11795                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11796                         v.hide();
11797                     }
11798                     break;
11799             }
11800         })
11801     },
11802     
11803     moveMonth: function(date, dir){
11804         if (!dir) return date;
11805         var new_date = new Date(date.valueOf()),
11806         day = new_date.getUTCDate(),
11807         month = new_date.getUTCMonth(),
11808         mag = Math.abs(dir),
11809         new_month, test;
11810         dir = dir > 0 ? 1 : -1;
11811         if (mag == 1){
11812             test = dir == -1
11813             // If going back one month, make sure month is not current month
11814             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11815             ? function(){
11816                 return new_date.getUTCMonth() == month;
11817             }
11818             // If going forward one month, make sure month is as expected
11819             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11820             : function(){
11821                 return new_date.getUTCMonth() != new_month;
11822             };
11823             new_month = month + dir;
11824             new_date.setUTCMonth(new_month);
11825             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11826             if (new_month < 0 || new_month > 11)
11827                 new_month = (new_month + 12) % 12;
11828         } else {
11829             // For magnitudes >1, move one month at a time...
11830             for (var i=0; i<mag; i++)
11831                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11832                 new_date = this.moveMonth(new_date, dir);
11833             // ...then reset the day, keeping it in the new month
11834             new_month = new_date.getUTCMonth();
11835             new_date.setUTCDate(day);
11836             test = function(){
11837                 return new_month != new_date.getUTCMonth();
11838             };
11839         }
11840         // Common date-resetting loop -- if date is beyond end of month, make it
11841         // end of month
11842         while (test()){
11843             new_date.setUTCDate(--day);
11844             new_date.setUTCMonth(new_month);
11845         }
11846         return new_date;
11847     },
11848
11849     moveYear: function(date, dir){
11850         return this.moveMonth(date, dir*12);
11851     },
11852
11853     dateWithinRange: function(date){
11854         return date >= this.startDate && date <= this.endDate;
11855     },
11856
11857     remove: function() {
11858         this.picker().remove();
11859     },
11860     
11861     onIncrementHours: function()
11862     {
11863         this.time.add(Date.HOUR, 1);
11864         this.updateTime();
11865     },
11866     
11867     onDecrementHours: function()
11868     {
11869         this.time.add(Date.HOUR, -1);
11870         this.updateTime();
11871     },
11872     
11873     onIncrementMinutes: function()
11874     {
11875         this.time.add(Date.MINUTE, 1);
11876         this.updateTime();
11877     },
11878     
11879     onDecrementMinutes: function()
11880     {
11881         this.time.add(Date.MINUTE, -1);
11882         this.updateTime();
11883     },
11884     
11885     onTogglePeriod: function()
11886     {
11887         Roo.log(this.time);
11888         var h = 12;
11889         if(this.time.format('A') === 'PM'){
11890             h = -12;
11891         }
11892         
11893         this.time.add(Date.HOUR, h);
11894         this.updateTime();
11895     }
11896     
11897    
11898 });
11899
11900 Roo.apply(Roo.bootstrap.DateField,  {
11901     
11902     head : {
11903         tag: 'thead',
11904         cn: [
11905         {
11906             tag: 'tr',
11907             cn: [
11908             {
11909                 tag: 'th',
11910                 cls: 'prev',
11911                 html: '<i class="icon-arrow-left"/>'
11912             },
11913             {
11914                 tag: 'th',
11915                 cls: 'switch',
11916                 colspan: '5'
11917             },
11918             {
11919                 tag: 'th',
11920                 cls: 'next',
11921                 html: '<i class="icon-arrow-right"/>'
11922             }
11923
11924             ]
11925         }
11926         ]
11927     },
11928     
11929     content : {
11930         tag: 'tbody',
11931         cn: [
11932         {
11933             tag: 'tr',
11934             cn: [
11935             {
11936                 tag: 'td',
11937                 colspan: '7'
11938             }
11939             ]
11940         }
11941         ]
11942     },
11943     
11944     footer : {
11945         tag: 'tfoot',
11946         cn: [
11947         {
11948             tag: 'tr',
11949             cn: [
11950             {
11951                 tag: 'th',
11952                 colspan: '7',
11953                 cls: '',
11954                 cn: [
11955                     {
11956                         tag: 'span',
11957                         cls: 'picker-switch-icon glyphicon'   
11958                     }
11959                 ]
11960             }
11961                     
11962             ]
11963         }
11964         ]
11965     },
11966     
11967     dates:{
11968         en: {
11969             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11970             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11971             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11972             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11973             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11974             today: "Today"
11975         }
11976     },
11977     
11978     modes: [
11979     {
11980         clsName: 'time',
11981         navFnc: 'Time',
11982         navStep: 0
11983     },
11984     {
11985         clsName: 'days',
11986         navFnc: 'Month',
11987         navStep: 1
11988     },
11989     {
11990         clsName: 'months',
11991         navFnc: 'FullYear',
11992         navStep: 1
11993     },
11994     {
11995         clsName: 'years',
11996         navFnc: 'FullYear',
11997         navStep: 10
11998     }
11999     ]
12000 });
12001
12002 Roo.apply(Roo.bootstrap.DateField,  {
12003   
12004     template : {
12005         tag: 'div',
12006         cls: 'datepicker dropdown-menu',
12007         cn: [
12008         {
12009             tag: 'div',
12010             cls: 'datepicker-days',
12011             cn: [
12012             {
12013                 tag: 'table',
12014                 cls: 'table-condensed',
12015                 cn:[
12016                 Roo.bootstrap.DateField.head,
12017                 {
12018                     tag: 'tbody'
12019                 },
12020                 Roo.bootstrap.DateField.footer
12021                 ]
12022             }
12023             ]
12024         },
12025         {
12026             tag: 'div',
12027             cls: 'datepicker-months',
12028             cn: [
12029             {
12030                 tag: 'table',
12031                 cls: 'table-condensed',
12032                 cn:[
12033                 Roo.bootstrap.DateField.head,
12034                 Roo.bootstrap.DateField.content
12035 //                Roo.bootstrap.DateField.footer
12036                 ]
12037             }
12038             ]
12039         },
12040         {
12041             tag: 'div',
12042             cls: 'datepicker-years',
12043             cn: [
12044             {
12045                 tag: 'table',
12046                 cls: 'table-condensed',
12047                 cn:[
12048                 Roo.bootstrap.DateField.head,
12049                 Roo.bootstrap.DateField.content
12050 //                Roo.bootstrap.DateField.footer
12051                 ]
12052             }
12053             ]
12054         },
12055         {
12056             tag: 'div',
12057             cls: 'datepicker-time',
12058             cn: [
12059             {
12060                 tag: 'table',
12061                 cls: 'table-condensed',
12062                 cn:[
12063                 Roo.bootstrap.DateField.content,
12064                 Roo.bootstrap.DateField.footer
12065                 ]
12066             }
12067             ]
12068         }
12069         ]
12070     }
12071 });
12072
12073  
12074
12075  /*
12076  * - LGPL
12077  *
12078  * CheckBox
12079  * 
12080  */
12081
12082 /**
12083  * @class Roo.bootstrap.CheckBox
12084  * @extends Roo.bootstrap.Input
12085  * Bootstrap CheckBox class
12086  * 
12087  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12088  * @cfg {String} boxLabel The text that appears beside the checkbox
12089  * @cfg {Boolean} checked initnal the element
12090  * 
12091  * @constructor
12092  * Create a new CheckBox
12093  * @param {Object} config The config object
12094  */
12095
12096 Roo.bootstrap.CheckBox = function(config){
12097     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12098    
12099         this.addEvents({
12100             /**
12101             * @event check
12102             * Fires when the element is checked or unchecked.
12103             * @param {Roo.bootstrap.CheckBox} this This input
12104             * @param {Boolean} checked The new checked value
12105             */
12106            check : true
12107         });
12108 };
12109
12110 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12111     
12112     inputType: 'checkbox',
12113     value: 1,
12114     valueOff: 0,
12115     boxLabel: false,
12116     checked: false,
12117     
12118     getAutoCreate : function()
12119     {
12120         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12121         
12122         var id = Roo.id();
12123         
12124         var cfg = {};
12125         
12126         cfg.cls = 'form-group' //input-group
12127         
12128         var input =  {
12129             tag: 'input',
12130             id : id,
12131             type : this.inputType,
12132             value : (!this.checked) ? this.valueOff : this.value,
12133             cls : 'form-box',
12134             placeholder : this.placeholder || ''
12135             
12136         };
12137         
12138         if (this.disabled) {
12139             input.disabled=true;
12140         }
12141         
12142         if(this.checked){
12143             input.checked = this.checked;
12144         }
12145         
12146         if (this.name) {
12147             input.name = this.name;
12148         }
12149         
12150         if (this.size) {
12151             input.cls += ' input-' + this.size;
12152         }
12153         
12154         var settings=this;
12155         ['xs','sm','md','lg'].map(function(size){
12156             if (settings[size]) {
12157                 cfg.cls += ' col-' + size + '-' + settings[size];
12158             }
12159         });
12160         
12161         var inputblock = input;
12162         
12163         if (this.before || this.after) {
12164             
12165             inputblock = {
12166                 cls : 'input-group',
12167                 cn :  [] 
12168             };
12169             if (this.before) {
12170                 inputblock.cn.push({
12171                     tag :'span',
12172                     cls : 'input-group-addon',
12173                     html : this.before
12174                 });
12175             }
12176             inputblock.cn.push(input);
12177             if (this.after) {
12178                 inputblock.cn.push({
12179                     tag :'span',
12180                     cls : 'input-group-addon',
12181                     html : this.after
12182                 });
12183             }
12184             
12185         };
12186         
12187         if (align ==='left' && this.fieldLabel.length) {
12188                 Roo.log("left and has label");
12189                 cfg.cn = [
12190                     
12191                     {
12192                         tag: 'label',
12193                         'for' :  id,
12194                         cls : 'control-label col-md-' + this.labelWidth,
12195                         html : this.fieldLabel
12196                         
12197                     },
12198                     {
12199                         cls : "col-md-" + (12 - this.labelWidth), 
12200                         cn: [
12201                             inputblock
12202                         ]
12203                     }
12204                     
12205                 ];
12206         } else if ( this.fieldLabel.length) {
12207                 Roo.log(" label");
12208                  cfg.cn = [
12209                    
12210                     {
12211                         tag: 'label',
12212                         'for': id,
12213                         cls: 'control-label box-input-label',
12214                         //cls : 'input-group-addon',
12215                         html : this.fieldLabel
12216                         
12217                     },
12218                     
12219                     inputblock
12220                     
12221                 ];
12222
12223         } else {
12224             
12225                    Roo.log(" no label && no align");
12226                 cfg.cn = [
12227                     
12228                         inputblock
12229                     
12230                 ];
12231                 
12232                 
12233         };
12234         
12235         if(this.boxLabel){
12236             cfg.cn.push({
12237                 tag: 'span',
12238                 'for': id,
12239                 cls: 'box-label',
12240                 html: this.boxLabel
12241             })
12242         }
12243         
12244         return cfg;
12245         
12246     },
12247     
12248     /**
12249      * return the real input element.
12250      */
12251     inputEl: function ()
12252     {
12253         return this.el.select('input.form-box',true).first();
12254     },
12255     
12256     initEvents : function()
12257     {
12258         Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12259         
12260         this.inputEl().on('click', this.onClick,  this);
12261         
12262     },
12263     
12264     onClick : function()
12265     {   
12266         this.setChecked(!this.checked);
12267     },
12268     
12269     setChecked : function(state,suppressEvent)
12270     {
12271         this.checked = state;
12272         
12273         if(suppressEvent !== true){
12274             this.fireEvent('check', this, state);
12275         }
12276         
12277         this.inputEl().dom.value = state ? this.value : this.valueOff;
12278         
12279     }
12280 });
12281
12282  
12283 /*
12284  * - LGPL
12285  *
12286  * Radio
12287  * 
12288  */
12289
12290 /**
12291  * @class Roo.bootstrap.Radio
12292  * @extends Roo.bootstrap.CheckBox
12293  * Bootstrap Radio class
12294
12295  * @constructor
12296  * Create a new Radio
12297  * @param {Object} config The config object
12298  */
12299
12300 Roo.bootstrap.Radio = function(config){
12301     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12302    
12303 };
12304
12305 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12306     
12307     inputType: 'radio',
12308     
12309     getAutoCreate : function()
12310     {
12311         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12312         
12313         var id = Roo.id();
12314         
12315         var cfg = {};
12316         
12317         cfg.cls = 'form-group' //input-group
12318         
12319         var input =  {
12320             tag: 'input',
12321             id : id,
12322             type : this.inputType,
12323             value : (!this.checked) ? this.valueOff : this.value,
12324             cls : 'form-box',
12325             placeholder : this.placeholder || ''
12326             
12327         };
12328         
12329         if (this.disabled) {
12330             input.disabled=true;
12331         }
12332         
12333         if(this.checked){
12334             input.checked = this.checked;
12335         }
12336         
12337         if (this.name) {
12338             input.name = this.name;
12339         }
12340         
12341         if (this.size) {
12342             input.cls += ' input-' + this.size;
12343         }
12344         
12345         var settings=this;
12346         ['xs','sm','md','lg'].map(function(size){
12347             if (settings[size]) {
12348                 cfg.cls += ' col-' + size + '-' + settings[size];
12349             }
12350         });
12351         
12352         var inputblock = input;
12353         
12354         if (this.before || this.after) {
12355             
12356             inputblock = {
12357                 cls : 'input-group',
12358                 cn :  [] 
12359             };
12360             if (this.before) {
12361                 inputblock.cn.push({
12362                     tag :'span',
12363                     cls : 'input-group-addon',
12364                     html : this.before
12365                 });
12366             }
12367             inputblock.cn.push(input);
12368             if (this.after) {
12369                 inputblock.cn.push({
12370                     tag :'span',
12371                     cls : 'input-group-addon',
12372                     html : this.after
12373                 });
12374             }
12375             
12376         };
12377         
12378         if (align ==='left' && this.fieldLabel.length) {
12379                 Roo.log("left and has label");
12380                 cfg.cn = [
12381                     
12382                     {
12383                         tag: 'label',
12384                         'for' :  id,
12385                         cls : 'control-label col-md-' + this.labelWidth,
12386                         html : this.fieldLabel
12387                         
12388                     },
12389                     {
12390                         cls : "col-md-" + (12 - this.labelWidth), 
12391                         cn: [
12392                             inputblock
12393                         ]
12394                     }
12395                     
12396                 ];
12397         } else if ( this.fieldLabel.length) {
12398                 Roo.log(" label");
12399                  cfg.cn = [
12400                    
12401                     {
12402                         tag: 'label',
12403                         'for': id,
12404                         cls: 'control-label box-input-label',
12405                         //cls : 'input-group-addon',
12406                         html : this.fieldLabel
12407                         
12408                     },
12409                     
12410                     inputblock
12411                     
12412                 ];
12413
12414         } else {
12415             
12416                    Roo.log(" no label && no align");
12417                 cfg.cn = [
12418                     
12419                         inputblock
12420                     
12421                 ];
12422                 
12423                 
12424         };
12425         
12426         if(this.boxLabel){
12427             cfg.cn.push({
12428                 tag: 'span',
12429                 'for': id,
12430                 cls: 'box-label',
12431                 html: this.boxLabel
12432             })
12433         }
12434         
12435         return cfg;
12436         
12437     },
12438     
12439     onClick : function()
12440     {   
12441         this.setChecked(true);
12442     },
12443     
12444     setChecked : function(state,suppressEvent)
12445     {
12446         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12447             v.checked = false;
12448         });
12449         
12450         this.checked = state;
12451         
12452         if(suppressEvent !== true){
12453             this.fireEvent('check', this, state);
12454         }
12455         
12456         this.inputEl().dom.value = state ? this.value : this.valueOff;
12457         
12458     },
12459     
12460     getGroupValue : function()
12461     {
12462         if(typeof(this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true)) == 'undefined'){
12463             return '';
12464         }
12465         
12466         return this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true).value;
12467     },
12468     
12469     /**
12470      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12471      * @return {Mixed} value The field value
12472      */
12473     getValue : function(){
12474         return this.getGroupValue();
12475     }
12476     
12477 });
12478
12479  
12480 /*
12481  * - LGPL
12482  *
12483  * HtmlEditor
12484  * 
12485  */
12486
12487 /**
12488  * @class Roo.bootstrap.HtmlEditor
12489  * @extends Roo.bootstrap.Component
12490  * Bootstrap HtmlEditor class
12491
12492  * @constructor
12493  * Create a new HtmlEditor
12494  * @param {Object} config The config object
12495  */
12496
12497 Roo.bootstrap.HtmlEditor = function(config){
12498     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
12499     if (!this.toolbars) {
12500         this.toolbars = [];
12501     }
12502     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
12503     this.addEvents({
12504             /**
12505              * @event initialize
12506              * Fires when the editor is fully initialized (including the iframe)
12507              * @param {HtmlEditor} this
12508              */
12509             initialize: true,
12510             /**
12511              * @event activate
12512              * Fires when the editor is first receives the focus. Any insertion must wait
12513              * until after this event.
12514              * @param {HtmlEditor} this
12515              */
12516             activate: true,
12517              /**
12518              * @event beforesync
12519              * Fires before the textarea is updated with content from the editor iframe. Return false
12520              * to cancel the sync.
12521              * @param {HtmlEditor} this
12522              * @param {String} html
12523              */
12524             beforesync: true,
12525              /**
12526              * @event beforepush
12527              * Fires before the iframe editor is updated with content from the textarea. Return false
12528              * to cancel the push.
12529              * @param {HtmlEditor} this
12530              * @param {String} html
12531              */
12532             beforepush: true,
12533              /**
12534              * @event sync
12535              * Fires when the textarea is updated with content from the editor iframe.
12536              * @param {HtmlEditor} this
12537              * @param {String} html
12538              */
12539             sync: true,
12540              /**
12541              * @event push
12542              * Fires when the iframe editor is updated with content from the textarea.
12543              * @param {HtmlEditor} this
12544              * @param {String} html
12545              */
12546             push: true,
12547              /**
12548              * @event editmodechange
12549              * Fires when the editor switches edit modes
12550              * @param {HtmlEditor} this
12551              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
12552              */
12553             editmodechange: true,
12554             /**
12555              * @event editorevent
12556              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12557              * @param {HtmlEditor} this
12558              */
12559             editorevent: true,
12560             /**
12561              * @event firstfocus
12562              * Fires when on first focus - needed by toolbars..
12563              * @param {HtmlEditor} this
12564              */
12565             firstfocus: true,
12566             /**
12567              * @event autosave
12568              * Auto save the htmlEditor value as a file into Events
12569              * @param {HtmlEditor} this
12570              */
12571             autosave: true,
12572             /**
12573              * @event savedpreview
12574              * preview the saved version of htmlEditor
12575              * @param {HtmlEditor} this
12576              */
12577             savedpreview: true
12578         });
12579 };
12580
12581
12582 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
12583     
12584     
12585       /**
12586      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
12587      */
12588     toolbars : false,
12589    
12590      /**
12591      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12592      *                        Roo.resizable.
12593      */
12594     resizable : false,
12595      /**
12596      * @cfg {Number} height (in pixels)
12597      */   
12598     height: 300,
12599    /**
12600      * @cfg {Number} width (in pixels)
12601      */   
12602     width: 500,
12603     
12604     /**
12605      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12606      * 
12607      */
12608     stylesheets: false,
12609     
12610     // id of frame..
12611     frameId: false,
12612     
12613     // private properties
12614     validationEvent : false,
12615     deferHeight: true,
12616     initialized : false,
12617     activated : false,
12618     
12619     onFocus : Roo.emptyFn,
12620     iframePad:3,
12621     hideMode:'offsets',
12622     
12623     
12624     tbContainer : false,
12625     
12626     toolbarContainer :function() {
12627         return this.wrap.select('.x-html-editor-tb',true).first();
12628     },
12629
12630     /**
12631      * Protected method that will not generally be called directly. It
12632      * is called when the editor creates its toolbar. Override this method if you need to
12633      * add custom toolbar buttons.
12634      * @param {HtmlEditor} editor
12635      */
12636     createToolbar : function(){
12637         
12638         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
12639         this.toolbars[0].render(this.toolbarContainer());
12640          
12641         Roo.log("create toolbars");
12642         return;
12643         if (!editor.toolbars || !editor.toolbars.length) {
12644             editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
12645         }
12646         
12647         for (var i =0 ; i < editor.toolbars.length;i++) {
12648             editor.toolbars[i] = Roo.factory(
12649                     typeof(editor.toolbars[i]) == 'string' ?
12650                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
12651                 Roo.bootstrap.HtmlEditor);
12652             editor.toolbars[i].init(editor);
12653         }
12654          
12655         
12656     },
12657
12658      
12659     // private
12660     onRender : function(ct, position)
12661     {
12662        // Roo.log("Call onRender: " + this.xtype);
12663         
12664         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
12665       
12666         this.wrap = this.inputEl().wrap({
12667             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
12668         });
12669         
12670         this.editorcore.onRender(ct, position);
12671          
12672         if (this.resizable) {
12673             this.resizeEl = new Roo.Resizable(this.wrap, {
12674                 pinned : true,
12675                 wrap: true,
12676                 dynamic : true,
12677                 minHeight : this.height,
12678                 height: this.height,
12679                 handles : this.resizable,
12680                 width: this.width,
12681                 listeners : {
12682                     resize : function(r, w, h) {
12683                         _t.onResize(w,h); // -something
12684                     }
12685                 }
12686             });
12687             
12688         }
12689         this.createToolbar(this);
12690        
12691         
12692         if(!this.width){
12693             this.setSize(this.wrap.getSize());
12694         }
12695         if (this.resizeEl) {
12696             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
12697             // should trigger onReize..
12698         }
12699         
12700 //        if(this.autosave && this.w){
12701 //            this.autoSaveFn = setInterval(this.autosave, 1000);
12702 //        }
12703     },
12704
12705     // private
12706     onResize : function(w, h)
12707     {
12708         Roo.log('resize: ' +w + ',' + h );
12709         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
12710         var ew = false;
12711         var eh = false;
12712         
12713         if(this.inputEl() ){
12714             if(typeof w == 'number'){
12715                 var aw = w - this.wrap.getFrameWidth('lr');
12716                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
12717                 ew = aw;
12718             }
12719             if(typeof h == 'number'){
12720                  var tbh = -11;  // fixme it needs to tool bar size!
12721                 for (var i =0; i < this.toolbars.length;i++) {
12722                     // fixme - ask toolbars for heights?
12723                     tbh += this.toolbars[i].el.getHeight();
12724                     //if (this.toolbars[i].footer) {
12725                     //    tbh += this.toolbars[i].footer.el.getHeight();
12726                     //}
12727                 }
12728               
12729                 
12730                 
12731                 
12732                 
12733                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
12734                 ah -= 5; // knock a few pixes off for look..
12735                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
12736                 var eh = ah;
12737             }
12738         }
12739         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
12740         this.editorcore.onResize(ew,eh);
12741         
12742     },
12743
12744     /**
12745      * Toggles the editor between standard and source edit mode.
12746      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
12747      */
12748     toggleSourceEdit : function(sourceEditMode)
12749     {
12750         this.editorcore.toggleSourceEdit(sourceEditMode);
12751         
12752         if(this.editorcore.sourceEditMode){
12753             Roo.log('editor - showing textarea');
12754             
12755 //            Roo.log('in');
12756 //            Roo.log(this.syncValue());
12757             this.editorcore.syncValue();
12758             this.inputEl().removeClass('hide');
12759             this.inputEl().dom.removeAttribute('tabIndex');
12760             this.inputEl().focus();
12761         }else{
12762             Roo.log('editor - hiding textarea');
12763 //            Roo.log('out')
12764 //            Roo.log(this.pushValue()); 
12765             this.editorcore.pushValue();
12766             
12767             this.inputEl().addClass('hide');
12768             this.inputEl().dom.setAttribute('tabIndex', -1);
12769             //this.deferFocus();
12770         }
12771          
12772         this.setSize(this.wrap.getSize());
12773         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
12774     },
12775  
12776     // private (for BoxComponent)
12777     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12778
12779     // private (for BoxComponent)
12780     getResizeEl : function(){
12781         return this.wrap;
12782     },
12783
12784     // private (for BoxComponent)
12785     getPositionEl : function(){
12786         return this.wrap;
12787     },
12788
12789     // private
12790     initEvents : function(){
12791         this.originalValue = this.getValue();
12792     },
12793
12794     /**
12795      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
12796      * @method
12797      */
12798     markInvalid : Roo.emptyFn,
12799     /**
12800      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
12801      * @method
12802      */
12803     clearInvalid : Roo.emptyFn,
12804
12805     setValue : function(v){
12806         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
12807         this.editorcore.pushValue();
12808     },
12809
12810      
12811     // private
12812     deferFocus : function(){
12813         this.focus.defer(10, this);
12814     },
12815
12816     // doc'ed in Field
12817     focus : function(){
12818         this.editorcore.focus();
12819         
12820     },
12821       
12822
12823     // private
12824     onDestroy : function(){
12825         
12826         
12827         
12828         if(this.rendered){
12829             
12830             for (var i =0; i < this.toolbars.length;i++) {
12831                 // fixme - ask toolbars for heights?
12832                 this.toolbars[i].onDestroy();
12833             }
12834             
12835             this.wrap.dom.innerHTML = '';
12836             this.wrap.remove();
12837         }
12838     },
12839
12840     // private
12841     onFirstFocus : function(){
12842         //Roo.log("onFirstFocus");
12843         this.editorcore.onFirstFocus();
12844          for (var i =0; i < this.toolbars.length;i++) {
12845             this.toolbars[i].onFirstFocus();
12846         }
12847         
12848     },
12849     
12850     // private
12851     syncValue : function()
12852     {
12853         this.editorcore.syncValue();
12854     }
12855      
12856     
12857     // hide stuff that is not compatible
12858     /**
12859      * @event blur
12860      * @hide
12861      */
12862     /**
12863      * @event change
12864      * @hide
12865      */
12866     /**
12867      * @event focus
12868      * @hide
12869      */
12870     /**
12871      * @event specialkey
12872      * @hide
12873      */
12874     /**
12875      * @cfg {String} fieldClass @hide
12876      */
12877     /**
12878      * @cfg {String} focusClass @hide
12879      */
12880     /**
12881      * @cfg {String} autoCreate @hide
12882      */
12883     /**
12884      * @cfg {String} inputType @hide
12885      */
12886     /**
12887      * @cfg {String} invalidClass @hide
12888      */
12889     /**
12890      * @cfg {String} invalidText @hide
12891      */
12892     /**
12893      * @cfg {String} msgFx @hide
12894      */
12895     /**
12896      * @cfg {String} validateOnBlur @hide
12897      */
12898 });
12899  
12900     
12901    
12902    
12903    
12904     
12905 /**
12906  * @class Roo.bootstrap.Table.AbstractSelectionModel
12907  * @extends Roo.util.Observable
12908  * Abstract base class for grid SelectionModels.  It provides the interface that should be
12909  * implemented by descendant classes.  This class should not be directly instantiated.
12910  * @constructor
12911  */
12912 Roo.bootstrap.Table.AbstractSelectionModel = function(){
12913     this.locked = false;
12914     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
12915 };
12916
12917
12918 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
12919     /** @ignore Called by the grid automatically. Do not call directly. */
12920     init : function(grid){
12921         this.grid = grid;
12922         this.initEvents();
12923     },
12924
12925     /**
12926      * Locks the selections.
12927      */
12928     lock : function(){
12929         this.locked = true;
12930     },
12931
12932     /**
12933      * Unlocks the selections.
12934      */
12935     unlock : function(){
12936         this.locked = false;
12937     },
12938
12939     /**
12940      * Returns true if the selections are locked.
12941      * @return {Boolean}
12942      */
12943     isLocked : function(){
12944         return this.locked;
12945     }
12946 });
12947 /**
12948  * @class Roo.bootstrap.Table.ColumnModel
12949  * @extends Roo.util.Observable
12950  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
12951  * the columns in the table.
12952  
12953  * @constructor
12954  * @param {Object} config An Array of column config objects. See this class's
12955  * config objects for details.
12956 */
12957 Roo.bootstrap.Table.ColumnModel = function(config){
12958         /**
12959      * The config passed into the constructor
12960      */
12961     this.config = config;
12962     this.lookup = {};
12963
12964     // if no id, create one
12965     // if the column does not have a dataIndex mapping,
12966     // map it to the order it is in the config
12967     for(var i = 0, len = config.length; i < len; i++){
12968         var c = config[i];
12969         if(typeof c.dataIndex == "undefined"){
12970             c.dataIndex = i;
12971         }
12972         if(typeof c.renderer == "string"){
12973             c.renderer = Roo.util.Format[c.renderer];
12974         }
12975         if(typeof c.id == "undefined"){
12976             c.id = Roo.id();
12977         }
12978 //        if(c.editor && c.editor.xtype){
12979 //            c.editor  = Roo.factory(c.editor, Roo.grid);
12980 //        }
12981 //        if(c.editor && c.editor.isFormField){
12982 //            c.editor = new Roo.grid.GridEditor(c.editor);
12983 //        }
12984
12985         this.lookup[c.id] = c;
12986     }
12987
12988     /**
12989      * The width of columns which have no width specified (defaults to 100)
12990      * @type Number
12991      */
12992     this.defaultWidth = 100;
12993
12994     /**
12995      * Default sortable of columns which have no sortable specified (defaults to false)
12996      * @type Boolean
12997      */
12998     this.defaultSortable = false;
12999
13000     this.addEvents({
13001         /**
13002              * @event widthchange
13003              * Fires when the width of a column changes.
13004              * @param {ColumnModel} this
13005              * @param {Number} columnIndex The column index
13006              * @param {Number} newWidth The new width
13007              */
13008             "widthchange": true,
13009         /**
13010              * @event headerchange
13011              * Fires when the text of a header changes.
13012              * @param {ColumnModel} this
13013              * @param {Number} columnIndex The column index
13014              * @param {Number} newText The new header text
13015              */
13016             "headerchange": true,
13017         /**
13018              * @event hiddenchange
13019              * Fires when a column is hidden or "unhidden".
13020              * @param {ColumnModel} this
13021              * @param {Number} columnIndex The column index
13022              * @param {Boolean} hidden true if hidden, false otherwise
13023              */
13024             "hiddenchange": true,
13025             /**
13026          * @event columnmoved
13027          * Fires when a column is moved.
13028          * @param {ColumnModel} this
13029          * @param {Number} oldIndex
13030          * @param {Number} newIndex
13031          */
13032         "columnmoved" : true,
13033         /**
13034          * @event columlockchange
13035          * Fires when a column's locked state is changed
13036          * @param {ColumnModel} this
13037          * @param {Number} colIndex
13038          * @param {Boolean} locked true if locked
13039          */
13040         "columnlockchange" : true
13041     });
13042     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
13043 };
13044 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
13045     /**
13046      * @cfg {String} header The header text to display in the Grid view.
13047      */
13048     /**
13049      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
13050      * {@link Roo.data.Record} definition from which to draw the column's value. If not
13051      * specified, the column's index is used as an index into the Record's data Array.
13052      */
13053     /**
13054      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
13055      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
13056      */
13057     /**
13058      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
13059      * Defaults to the value of the {@link #defaultSortable} property.
13060      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
13061      */
13062     /**
13063      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
13064      */
13065     /**
13066      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
13067      */
13068     /**
13069      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
13070      */
13071     /**
13072      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
13073      */
13074     /**
13075      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
13076      * given the cell's data value. See {@link #setRenderer}. If not specified, the
13077      * default renderer uses the raw data value.
13078      */
13079     /**
13080      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
13081      */
13082
13083     /**
13084      * Returns the id of the column at the specified index.
13085      * @param {Number} index The column index
13086      * @return {String} the id
13087      */
13088     getColumnId : function(index){
13089         return this.config[index].id;
13090     },
13091
13092     /**
13093      * Returns the column for a specified id.
13094      * @param {String} id The column id
13095      * @return {Object} the column
13096      */
13097     getColumnById : function(id){
13098         return this.lookup[id];
13099     },
13100
13101     
13102     /**
13103      * Returns the column for a specified dataIndex.
13104      * @param {String} dataIndex The column dataIndex
13105      * @return {Object|Boolean} the column or false if not found
13106      */
13107     getColumnByDataIndex: function(dataIndex){
13108         var index = this.findColumnIndex(dataIndex);
13109         return index > -1 ? this.config[index] : false;
13110     },
13111     
13112     /**
13113      * Returns the index for a specified column id.
13114      * @param {String} id The column id
13115      * @return {Number} the index, or -1 if not found
13116      */
13117     getIndexById : function(id){
13118         for(var i = 0, len = this.config.length; i < len; i++){
13119             if(this.config[i].id == id){
13120                 return i;
13121             }
13122         }
13123         return -1;
13124     },
13125     
13126     /**
13127      * Returns the index for a specified column dataIndex.
13128      * @param {String} dataIndex The column dataIndex
13129      * @return {Number} the index, or -1 if not found
13130      */
13131     
13132     findColumnIndex : function(dataIndex){
13133         for(var i = 0, len = this.config.length; i < len; i++){
13134             if(this.config[i].dataIndex == dataIndex){
13135                 return i;
13136             }
13137         }
13138         return -1;
13139     },
13140     
13141     
13142     moveColumn : function(oldIndex, newIndex){
13143         var c = this.config[oldIndex];
13144         this.config.splice(oldIndex, 1);
13145         this.config.splice(newIndex, 0, c);
13146         this.dataMap = null;
13147         this.fireEvent("columnmoved", this, oldIndex, newIndex);
13148     },
13149
13150     isLocked : function(colIndex){
13151         return this.config[colIndex].locked === true;
13152     },
13153
13154     setLocked : function(colIndex, value, suppressEvent){
13155         if(this.isLocked(colIndex) == value){
13156             return;
13157         }
13158         this.config[colIndex].locked = value;
13159         if(!suppressEvent){
13160             this.fireEvent("columnlockchange", this, colIndex, value);
13161         }
13162     },
13163
13164     getTotalLockedWidth : function(){
13165         var totalWidth = 0;
13166         for(var i = 0; i < this.config.length; i++){
13167             if(this.isLocked(i) && !this.isHidden(i)){
13168                 this.totalWidth += this.getColumnWidth(i);
13169             }
13170         }
13171         return totalWidth;
13172     },
13173
13174     getLockedCount : function(){
13175         for(var i = 0, len = this.config.length; i < len; i++){
13176             if(!this.isLocked(i)){
13177                 return i;
13178             }
13179         }
13180     },
13181
13182     /**
13183      * Returns the number of columns.
13184      * @return {Number}
13185      */
13186     getColumnCount : function(visibleOnly){
13187         if(visibleOnly === true){
13188             var c = 0;
13189             for(var i = 0, len = this.config.length; i < len; i++){
13190                 if(!this.isHidden(i)){
13191                     c++;
13192                 }
13193             }
13194             return c;
13195         }
13196         return this.config.length;
13197     },
13198
13199     /**
13200      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
13201      * @param {Function} fn
13202      * @param {Object} scope (optional)
13203      * @return {Array} result
13204      */
13205     getColumnsBy : function(fn, scope){
13206         var r = [];
13207         for(var i = 0, len = this.config.length; i < len; i++){
13208             var c = this.config[i];
13209             if(fn.call(scope||this, c, i) === true){
13210                 r[r.length] = c;
13211             }
13212         }
13213         return r;
13214     },
13215
13216     /**
13217      * Returns true if the specified column is sortable.
13218      * @param {Number} col The column index
13219      * @return {Boolean}
13220      */
13221     isSortable : function(col){
13222         if(typeof this.config[col].sortable == "undefined"){
13223             return this.defaultSortable;
13224         }
13225         return this.config[col].sortable;
13226     },
13227
13228     /**
13229      * Returns the rendering (formatting) function defined for the column.
13230      * @param {Number} col The column index.
13231      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
13232      */
13233     getRenderer : function(col){
13234         if(!this.config[col].renderer){
13235             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
13236         }
13237         return this.config[col].renderer;
13238     },
13239
13240     /**
13241      * Sets the rendering (formatting) function for a column.
13242      * @param {Number} col The column index
13243      * @param {Function} fn The function to use to process the cell's raw data
13244      * to return HTML markup for the grid view. The render function is called with
13245      * the following parameters:<ul>
13246      * <li>Data value.</li>
13247      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
13248      * <li>css A CSS style string to apply to the table cell.</li>
13249      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
13250      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
13251      * <li>Row index</li>
13252      * <li>Column index</li>
13253      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
13254      */
13255     setRenderer : function(col, fn){
13256         this.config[col].renderer = fn;
13257     },
13258
13259     /**
13260      * Returns the width for the specified column.
13261      * @param {Number} col The column index
13262      * @return {Number}
13263      */
13264     getColumnWidth : function(col){
13265         return this.config[col].width * 1 || this.defaultWidth;
13266     },
13267
13268     /**
13269      * Sets the width for a column.
13270      * @param {Number} col The column index
13271      * @param {Number} width The new width
13272      */
13273     setColumnWidth : function(col, width, suppressEvent){
13274         this.config[col].width = width;
13275         this.totalWidth = null;
13276         if(!suppressEvent){
13277              this.fireEvent("widthchange", this, col, width);
13278         }
13279     },
13280
13281     /**
13282      * Returns the total width of all columns.
13283      * @param {Boolean} includeHidden True to include hidden column widths
13284      * @return {Number}
13285      */
13286     getTotalWidth : function(includeHidden){
13287         if(!this.totalWidth){
13288             this.totalWidth = 0;
13289             for(var i = 0, len = this.config.length; i < len; i++){
13290                 if(includeHidden || !this.isHidden(i)){
13291                     this.totalWidth += this.getColumnWidth(i);
13292                 }
13293             }
13294         }
13295         return this.totalWidth;
13296     },
13297
13298     /**
13299      * Returns the header for the specified column.
13300      * @param {Number} col The column index
13301      * @return {String}
13302      */
13303     getColumnHeader : function(col){
13304         return this.config[col].header;
13305     },
13306
13307     /**
13308      * Sets the header for a column.
13309      * @param {Number} col The column index
13310      * @param {String} header The new header
13311      */
13312     setColumnHeader : function(col, header){
13313         this.config[col].header = header;
13314         this.fireEvent("headerchange", this, col, header);
13315     },
13316
13317     /**
13318      * Returns the tooltip for the specified column.
13319      * @param {Number} col The column index
13320      * @return {String}
13321      */
13322     getColumnTooltip : function(col){
13323             return this.config[col].tooltip;
13324     },
13325     /**
13326      * Sets the tooltip for a column.
13327      * @param {Number} col The column index
13328      * @param {String} tooltip The new tooltip
13329      */
13330     setColumnTooltip : function(col, tooltip){
13331             this.config[col].tooltip = tooltip;
13332     },
13333
13334     /**
13335      * Returns the dataIndex for the specified column.
13336      * @param {Number} col The column index
13337      * @return {Number}
13338      */
13339     getDataIndex : function(col){
13340         return this.config[col].dataIndex;
13341     },
13342
13343     /**
13344      * Sets the dataIndex for a column.
13345      * @param {Number} col The column index
13346      * @param {Number} dataIndex The new dataIndex
13347      */
13348     setDataIndex : function(col, dataIndex){
13349         this.config[col].dataIndex = dataIndex;
13350     },
13351
13352     
13353     
13354     /**
13355      * Returns true if the cell is editable.
13356      * @param {Number} colIndex The column index
13357      * @param {Number} rowIndex The row index
13358      * @return {Boolean}
13359      */
13360     isCellEditable : function(colIndex, rowIndex){
13361         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
13362     },
13363
13364     /**
13365      * Returns the editor defined for the cell/column.
13366      * return false or null to disable editing.
13367      * @param {Number} colIndex The column index
13368      * @param {Number} rowIndex The row index
13369      * @return {Object}
13370      */
13371     getCellEditor : function(colIndex, rowIndex){
13372         return this.config[colIndex].editor;
13373     },
13374
13375     /**
13376      * Sets if a column is editable.
13377      * @param {Number} col The column index
13378      * @param {Boolean} editable True if the column is editable
13379      */
13380     setEditable : function(col, editable){
13381         this.config[col].editable = editable;
13382     },
13383
13384
13385     /**
13386      * Returns true if the column is hidden.
13387      * @param {Number} colIndex The column index
13388      * @return {Boolean}
13389      */
13390     isHidden : function(colIndex){
13391         return this.config[colIndex].hidden;
13392     },
13393
13394
13395     /**
13396      * Returns true if the column width cannot be changed
13397      */
13398     isFixed : function(colIndex){
13399         return this.config[colIndex].fixed;
13400     },
13401
13402     /**
13403      * Returns true if the column can be resized
13404      * @return {Boolean}
13405      */
13406     isResizable : function(colIndex){
13407         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
13408     },
13409     /**
13410      * Sets if a column is hidden.
13411      * @param {Number} colIndex The column index
13412      * @param {Boolean} hidden True if the column is hidden
13413      */
13414     setHidden : function(colIndex, hidden){
13415         this.config[colIndex].hidden = hidden;
13416         this.totalWidth = null;
13417         this.fireEvent("hiddenchange", this, colIndex, hidden);
13418     },
13419
13420     /**
13421      * Sets the editor for a column.
13422      * @param {Number} col The column index
13423      * @param {Object} editor The editor object
13424      */
13425     setEditor : function(col, editor){
13426         this.config[col].editor = editor;
13427     }
13428 });
13429
13430 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
13431         if(typeof value == "string" && value.length < 1){
13432             return "&#160;";
13433         }
13434         return value;
13435 };
13436
13437 // Alias for backwards compatibility
13438 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
13439
13440 /**
13441  * @extends Roo.bootstrap.Table.AbstractSelectionModel
13442  * @class Roo.bootstrap.Table.RowSelectionModel
13443  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
13444  * It supports multiple selections and keyboard selection/navigation. 
13445  * @constructor
13446  * @param {Object} config
13447  */
13448
13449 Roo.bootstrap.Table.RowSelectionModel = function(config){
13450     Roo.apply(this, config);
13451     this.selections = new Roo.util.MixedCollection(false, function(o){
13452         return o.id;
13453     });
13454
13455     this.last = false;
13456     this.lastActive = false;
13457
13458     this.addEvents({
13459         /**
13460              * @event selectionchange
13461              * Fires when the selection changes
13462              * @param {SelectionModel} this
13463              */
13464             "selectionchange" : true,
13465         /**
13466              * @event afterselectionchange
13467              * Fires after the selection changes (eg. by key press or clicking)
13468              * @param {SelectionModel} this
13469              */
13470             "afterselectionchange" : true,
13471         /**
13472              * @event beforerowselect
13473              * Fires when a row is selected being selected, return false to cancel.
13474              * @param {SelectionModel} this
13475              * @param {Number} rowIndex The selected index
13476              * @param {Boolean} keepExisting False if other selections will be cleared
13477              */
13478             "beforerowselect" : true,
13479         /**
13480              * @event rowselect
13481              * Fires when a row is selected.
13482              * @param {SelectionModel} this
13483              * @param {Number} rowIndex The selected index
13484              * @param {Roo.data.Record} r The record
13485              */
13486             "rowselect" : true,
13487         /**
13488              * @event rowdeselect
13489              * Fires when a row is deselected.
13490              * @param {SelectionModel} this
13491              * @param {Number} rowIndex The selected index
13492              */
13493         "rowdeselect" : true
13494     });
13495     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
13496     this.locked = false;
13497 };
13498
13499 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
13500     /**
13501      * @cfg {Boolean} singleSelect
13502      * True to allow selection of only one row at a time (defaults to false)
13503      */
13504     singleSelect : false,
13505
13506     // private
13507     initEvents : function(){
13508
13509         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
13510             this.grid.on("mousedown", this.handleMouseDown, this);
13511         }else{ // allow click to work like normal
13512             this.grid.on("rowclick", this.handleDragableRowClick, this);
13513         }
13514
13515         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
13516             "up" : function(e){
13517                 if(!e.shiftKey){
13518                     this.selectPrevious(e.shiftKey);
13519                 }else if(this.last !== false && this.lastActive !== false){
13520                     var last = this.last;
13521                     this.selectRange(this.last,  this.lastActive-1);
13522                     this.grid.getView().focusRow(this.lastActive);
13523                     if(last !== false){
13524                         this.last = last;
13525                     }
13526                 }else{
13527                     this.selectFirstRow();
13528                 }
13529                 this.fireEvent("afterselectionchange", this);
13530             },
13531             "down" : function(e){
13532                 if(!e.shiftKey){
13533                     this.selectNext(e.shiftKey);
13534                 }else if(this.last !== false && this.lastActive !== false){
13535                     var last = this.last;
13536                     this.selectRange(this.last,  this.lastActive+1);
13537                     this.grid.getView().focusRow(this.lastActive);
13538                     if(last !== false){
13539                         this.last = last;
13540                     }
13541                 }else{
13542                     this.selectFirstRow();
13543                 }
13544                 this.fireEvent("afterselectionchange", this);
13545             },
13546             scope: this
13547         });
13548
13549         var view = this.grid.view;
13550         view.on("refresh", this.onRefresh, this);
13551         view.on("rowupdated", this.onRowUpdated, this);
13552         view.on("rowremoved", this.onRemove, this);
13553     },
13554
13555     // private
13556     onRefresh : function(){
13557         var ds = this.grid.dataSource, i, v = this.grid.view;
13558         var s = this.selections;
13559         s.each(function(r){
13560             if((i = ds.indexOfId(r.id)) != -1){
13561                 v.onRowSelect(i);
13562             }else{
13563                 s.remove(r);
13564             }
13565         });
13566     },
13567
13568     // private
13569     onRemove : function(v, index, r){
13570         this.selections.remove(r);
13571     },
13572
13573     // private
13574     onRowUpdated : function(v, index, r){
13575         if(this.isSelected(r)){
13576             v.onRowSelect(index);
13577         }
13578     },
13579
13580     /**
13581      * Select records.
13582      * @param {Array} records The records to select
13583      * @param {Boolean} keepExisting (optional) True to keep existing selections
13584      */
13585     selectRecords : function(records, keepExisting){
13586         if(!keepExisting){
13587             this.clearSelections();
13588         }
13589         var ds = this.grid.dataSource;
13590         for(var i = 0, len = records.length; i < len; i++){
13591             this.selectRow(ds.indexOf(records[i]), true);
13592         }
13593     },
13594
13595     /**
13596      * Gets the number of selected rows.
13597      * @return {Number}
13598      */
13599     getCount : function(){
13600         return this.selections.length;
13601     },
13602
13603     /**
13604      * Selects the first row in the grid.
13605      */
13606     selectFirstRow : function(){
13607         this.selectRow(0);
13608     },
13609
13610     /**
13611      * Select the last row.
13612      * @param {Boolean} keepExisting (optional) True to keep existing selections
13613      */
13614     selectLastRow : function(keepExisting){
13615         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
13616     },
13617
13618     /**
13619      * Selects the row immediately following the last selected row.
13620      * @param {Boolean} keepExisting (optional) True to keep existing selections
13621      */
13622     selectNext : function(keepExisting){
13623         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
13624             this.selectRow(this.last+1, keepExisting);
13625             this.grid.getView().focusRow(this.last);
13626         }
13627     },
13628
13629     /**
13630      * Selects the row that precedes the last selected row.
13631      * @param {Boolean} keepExisting (optional) True to keep existing selections
13632      */
13633     selectPrevious : function(keepExisting){
13634         if(this.last){
13635             this.selectRow(this.last-1, keepExisting);
13636             this.grid.getView().focusRow(this.last);
13637         }
13638     },
13639
13640     /**
13641      * Returns the selected records
13642      * @return {Array} Array of selected records
13643      */
13644     getSelections : function(){
13645         return [].concat(this.selections.items);
13646     },
13647
13648     /**
13649      * Returns the first selected record.
13650      * @return {Record}
13651      */
13652     getSelected : function(){
13653         return this.selections.itemAt(0);
13654     },
13655
13656
13657     /**
13658      * Clears all selections.
13659      */
13660     clearSelections : function(fast){
13661         if(this.locked) return;
13662         if(fast !== true){
13663             var ds = this.grid.dataSource;
13664             var s = this.selections;
13665             s.each(function(r){
13666                 this.deselectRow(ds.indexOfId(r.id));
13667             }, this);
13668             s.clear();
13669         }else{
13670             this.selections.clear();
13671         }
13672         this.last = false;
13673     },
13674
13675
13676     /**
13677      * Selects all rows.
13678      */
13679     selectAll : function(){
13680         if(this.locked) return;
13681         this.selections.clear();
13682         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
13683             this.selectRow(i, true);
13684         }
13685     },
13686
13687     /**
13688      * Returns True if there is a selection.
13689      * @return {Boolean}
13690      */
13691     hasSelection : function(){
13692         return this.selections.length > 0;
13693     },
13694
13695     /**
13696      * Returns True if the specified row is selected.
13697      * @param {Number/Record} record The record or index of the record to check
13698      * @return {Boolean}
13699      */
13700     isSelected : function(index){
13701         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
13702         return (r && this.selections.key(r.id) ? true : false);
13703     },
13704
13705     /**
13706      * Returns True if the specified record id is selected.
13707      * @param {String} id The id of record to check
13708      * @return {Boolean}
13709      */
13710     isIdSelected : function(id){
13711         return (this.selections.key(id) ? true : false);
13712     },
13713
13714     // private
13715     handleMouseDown : function(e, t){
13716         var view = this.grid.getView(), rowIndex;
13717         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
13718             return;
13719         };
13720         if(e.shiftKey && this.last !== false){
13721             var last = this.last;
13722             this.selectRange(last, rowIndex, e.ctrlKey);
13723             this.last = last; // reset the last
13724             view.focusRow(rowIndex);
13725         }else{
13726             var isSelected = this.isSelected(rowIndex);
13727             if(e.button !== 0 && isSelected){
13728                 view.focusRow(rowIndex);
13729             }else if(e.ctrlKey && isSelected){
13730                 this.deselectRow(rowIndex);
13731             }else if(!isSelected){
13732                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
13733                 view.focusRow(rowIndex);
13734             }
13735         }
13736         this.fireEvent("afterselectionchange", this);
13737     },
13738     // private
13739     handleDragableRowClick :  function(grid, rowIndex, e) 
13740     {
13741         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
13742             this.selectRow(rowIndex, false);
13743             grid.view.focusRow(rowIndex);
13744              this.fireEvent("afterselectionchange", this);
13745         }
13746     },
13747     
13748     /**
13749      * Selects multiple rows.
13750      * @param {Array} rows Array of the indexes of the row to select
13751      * @param {Boolean} keepExisting (optional) True to keep existing selections
13752      */
13753     selectRows : function(rows, keepExisting){
13754         if(!keepExisting){
13755             this.clearSelections();
13756         }
13757         for(var i = 0, len = rows.length; i < len; i++){
13758             this.selectRow(rows[i], true);
13759         }
13760     },
13761
13762     /**
13763      * Selects a range of rows. All rows in between startRow and endRow are also selected.
13764      * @param {Number} startRow The index of the first row in the range
13765      * @param {Number} endRow The index of the last row in the range
13766      * @param {Boolean} keepExisting (optional) True to retain existing selections
13767      */
13768     selectRange : function(startRow, endRow, keepExisting){
13769         if(this.locked) return;
13770         if(!keepExisting){
13771             this.clearSelections();
13772         }
13773         if(startRow <= endRow){
13774             for(var i = startRow; i <= endRow; i++){
13775                 this.selectRow(i, true);
13776             }
13777         }else{
13778             for(var i = startRow; i >= endRow; i--){
13779                 this.selectRow(i, true);
13780             }
13781         }
13782     },
13783
13784     /**
13785      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
13786      * @param {Number} startRow The index of the first row in the range
13787      * @param {Number} endRow The index of the last row in the range
13788      */
13789     deselectRange : function(startRow, endRow, preventViewNotify){
13790         if(this.locked) return;
13791         for(var i = startRow; i <= endRow; i++){
13792             this.deselectRow(i, preventViewNotify);
13793         }
13794     },
13795
13796     /**
13797      * Selects a row.
13798      * @param {Number} row The index of the row to select
13799      * @param {Boolean} keepExisting (optional) True to keep existing selections
13800      */
13801     selectRow : function(index, keepExisting, preventViewNotify){
13802         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
13803         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
13804             if(!keepExisting || this.singleSelect){
13805                 this.clearSelections();
13806             }
13807             var r = this.grid.dataSource.getAt(index);
13808             this.selections.add(r);
13809             this.last = this.lastActive = index;
13810             if(!preventViewNotify){
13811                 this.grid.getView().onRowSelect(index);
13812             }
13813             this.fireEvent("rowselect", this, index, r);
13814             this.fireEvent("selectionchange", this);
13815         }
13816     },
13817
13818     /**
13819      * Deselects a row.
13820      * @param {Number} row The index of the row to deselect
13821      */
13822     deselectRow : function(index, preventViewNotify){
13823         if(this.locked) return;
13824         if(this.last == index){
13825             this.last = false;
13826         }
13827         if(this.lastActive == index){
13828             this.lastActive = false;
13829         }
13830         var r = this.grid.dataSource.getAt(index);
13831         this.selections.remove(r);
13832         if(!preventViewNotify){
13833             this.grid.getView().onRowDeselect(index);
13834         }
13835         this.fireEvent("rowdeselect", this, index);
13836         this.fireEvent("selectionchange", this);
13837     },
13838
13839     // private
13840     restoreLast : function(){
13841         if(this._last){
13842             this.last = this._last;
13843         }
13844     },
13845
13846     // private
13847     acceptsNav : function(row, col, cm){
13848         return !cm.isHidden(col) && cm.isCellEditable(col, row);
13849     },
13850
13851     // private
13852     onEditorKey : function(field, e){
13853         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
13854         if(k == e.TAB){
13855             e.stopEvent();
13856             ed.completeEdit();
13857             if(e.shiftKey){
13858                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
13859             }else{
13860                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
13861             }
13862         }else if(k == e.ENTER && !e.ctrlKey){
13863             e.stopEvent();
13864             ed.completeEdit();
13865             if(e.shiftKey){
13866                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
13867             }else{
13868                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
13869             }
13870         }else if(k == e.ESC){
13871             ed.cancelEdit();
13872         }
13873         if(newCell){
13874             g.startEditing(newCell[0], newCell[1]);
13875         }
13876     }
13877 });