Roo/bootstrap/Button.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @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
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035    
2036     
2037 };
2038
2039 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2040     
2041     sidebar: false,
2042     
2043     bar: false,
2044     brand: '',
2045     inverse: false,
2046     position: '',
2047     align : false,
2048     type: 'nav',
2049     arrangement: '',
2050     brand_href: false,
2051     main : false,
2052     loadMask : false,
2053     
2054     
2055     // private
2056     navItems : false,
2057     
2058     getAutoCreate : function(){
2059         var cfg = {
2060             cls : 'navbar'
2061         };
2062         
2063         if (this.sidebar === true) {
2064             cfg = {
2065                 tag: 'div',
2066                 cls: 'sidebar-nav'
2067             };
2068             return cfg;
2069         }
2070         
2071         if (this.bar === true) {
2072             cfg = {
2073                 tag: 'nav',
2074                 cls: 'navbar',
2075                 role: 'navigation',
2076                 cn: [
2077                     {
2078                         tag: 'div',
2079                         cls: 'navbar-header',
2080                         cn: [
2081                             {
2082                             tag: 'button',
2083                             type: 'button',
2084                             cls: 'navbar-toggle',
2085                             'data-toggle': 'collapse',
2086                             cn: [
2087                                 {
2088                                     tag: 'span',
2089                                     cls: 'sr-only',
2090                                     html: 'Toggle navigation'
2091                                 },
2092                                 {
2093                                     tag: 'span',
2094                                     cls: 'icon-bar'
2095                                 },
2096                                 {
2097                                     tag: 'span',
2098                                     cls: 'icon-bar'
2099                                 },
2100                                 {
2101                                     tag: 'span',
2102                                     cls: 'icon-bar'
2103                                 }
2104                             ]
2105                             }
2106                         ]
2107                     },
2108                     {
2109                     tag: 'div',
2110                     cls: 'collapse navbar-collapse'
2111                     }
2112                 ]
2113             };
2114             
2115             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2116             
2117             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2118                 cfg.cls += ' navbar-' + this.position;
2119                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2120             }
2121             
2122             if (this.brand !== '') {
2123                 cfg.cn[0].cn.push({
2124                     tag: 'a',
2125                     href: this.brand_href ? this.brand_href : '#',
2126                     cls: 'navbar-brand',
2127                     cn: [
2128                     this.brand
2129                     ]
2130                 });
2131             }
2132             
2133             if(this.main){
2134                 cfg.cls += ' main-nav';
2135             }
2136             
2137             
2138             return cfg;
2139         
2140         } else if (this.bar === false) {
2141             
2142         } else {
2143             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2144         }
2145         
2146         cfg.cn = [
2147             {
2148                 cls: 'nav',
2149                 tag : 'ul'
2150             }
2151         ];
2152         
2153         if (['tabs','pills'].indexOf(this.type)!==-1) {
2154             cfg.cn[0].cls += ' nav-' + this.type
2155         } else {
2156             if (this.type!=='nav') {
2157             Roo.log('nav type must be nav/tabs/pills')
2158             }
2159             cfg.cn[0].cls += ' navbar-nav'
2160         }
2161         
2162         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2163             cfg.cn[0].cls += ' nav-' + this.arrangement;
2164         }
2165         
2166         if (this.align === 'right') {
2167             cfg.cn[0].cls += ' navbar-right';
2168         }
2169         if (this.inverse) {
2170             cfg.cls += ' navbar-inverse';
2171             
2172         }
2173         
2174         
2175         return cfg;
2176     },
2177     
2178     initEvents :function ()
2179     {
2180         //Roo.log(this.el.select('.navbar-toggle',true));
2181         this.el.select('.navbar-toggle',true).on('click', function() {
2182            // Roo.log('click');
2183             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2184         }, this);
2185         
2186         var mark = {
2187             tag: "div",
2188             cls:"x-dlg-mask"
2189         }
2190         
2191         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2192         
2193         var size = this.el.getSize();
2194         this.maskEl.setSize(size.width, size.height);
2195         this.maskEl.enableDisplayMode("block");
2196         this.maskEl.hide();
2197         
2198         if(this.loadMask){
2199             this.maskEl.show();
2200         }
2201     },
2202     
2203     
2204     getChildContainer : function()
2205     {
2206         if (this.bar === true) {
2207             return this.el.select('.collapse',true).first();
2208         }
2209         
2210         return this.el;
2211     },
2212     
2213     mask : function()
2214     {
2215         this.maskEl.show();
2216     },
2217     
2218     unmask : function()
2219     {
2220         this.maskEl.hide();
2221     }
2222     
2223     
2224     
2225 });
2226
2227
2228
2229  
2230
2231  /*
2232  * - LGPL
2233  *
2234  * nav group
2235  * 
2236  */
2237
2238 /**
2239  * @class Roo.bootstrap.NavGroup
2240  * @extends Roo.bootstrap.Component
2241  * Bootstrap NavGroup class
2242  * @cfg {String} align left | right
2243  * @cfg {Boolean} inverse false | true
2244  * @cfg {String} type (nav|pills|tab) default nav
2245  * @cfg {String} navId - reference Id for navbar.
2246
2247  * 
2248  * @constructor
2249  * Create a new nav group
2250  * @param {Object} config The config object
2251  */
2252
2253 Roo.bootstrap.NavGroup = function(config){
2254     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2255     this.navItems = [];
2256     Roo.bootstrap.NavGroup.register(this);
2257      this.addEvents({
2258         /**
2259              * @event changed
2260              * Fires when the active item changes
2261              * @param {Roo.bootstrap.NavGroup} this
2262              * @param {Roo.bootstrap.Navbar.Item} item The item selected
2263              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
2264          */
2265         'changed': true
2266      });
2267     
2268 };
2269
2270 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2271     
2272     align: '',
2273     inverse: false,
2274     form: false,
2275     type: 'nav',
2276     navId : '',
2277     // private
2278     
2279     navItems : false,
2280     
2281     getAutoCreate : function()
2282     {
2283         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2284         
2285         cfg = {
2286             tag : 'ul',
2287             cls: 'nav' 
2288         }
2289         
2290         if (['tabs','pills'].indexOf(this.type)!==-1) {
2291             cfg.cls += ' nav-' + this.type
2292         } else {
2293             if (this.type!=='nav') {
2294                 Roo.log('nav type must be nav/tabs/pills')
2295             }
2296             cfg.cls += ' navbar-nav'
2297         }
2298         
2299         if (this.parent().sidebar === true) {
2300             cfg = {
2301                 tag: 'ul',
2302                 cls: 'dashboard-menu'
2303             }
2304             
2305             return cfg;
2306         }
2307         
2308         if (this.form === true) {
2309             cfg = {
2310                 tag: 'form',
2311                 cls: 'navbar-form'
2312             }
2313             
2314             if (this.align === 'right') {
2315                 cfg.cls += ' navbar-right';
2316             } else {
2317                 cfg.cls += ' navbar-left';
2318             }
2319         }
2320         
2321         if (this.align === 'right') {
2322             cfg.cls += ' navbar-right';
2323         }
2324         
2325         if (this.inverse) {
2326             cfg.cls += ' navbar-inverse';
2327             
2328         }
2329         
2330         
2331         return cfg;
2332     },
2333     
2334     setActiveItem : function(item)
2335     {
2336         var prev = false;
2337         Roo.each(this.navItems, function(v){
2338             if (v == item) {
2339                 return ;
2340             }
2341             if (v.isActive()) {
2342                 v.setActive(false, true);
2343                 prev = v;
2344                 
2345             }
2346             
2347         });
2348
2349         item.setActive(true, true);
2350         this.fireEvent('changed', this, item, prev);
2351         
2352         
2353     },
2354     
2355     
2356     register : function(item)
2357     {
2358         this.navItems.push( item);
2359         item.navId = this.navId;
2360     
2361     },
2362     getNavItem: function(tabId)
2363     {
2364         var ret = false;
2365         Roo.each(this.navItems, function(e) {
2366             if (e.tabId == tabId) {
2367                ret =  e;
2368                return false;
2369             }
2370             return true;
2371             
2372         });
2373         return ret;
2374     }
2375 });
2376
2377  
2378 Roo.apply(Roo.bootstrap.NavGroup, {
2379     
2380     groups: {},
2381     
2382     register : function(navgrp)
2383     {
2384         this.groups[navgrp.navId] = navgrp;
2385         
2386     },
2387     get: function(navId) {
2388         return this.groups[navId];
2389     }
2390     
2391     
2392     
2393 });
2394
2395  /*
2396  * - LGPL
2397  *
2398  * row
2399  * 
2400  */
2401
2402 /**
2403  * @class Roo.bootstrap.Navbar.Item
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap Navbar.Button class
2406  * @cfg {String} href  link to
2407  * @cfg {String} html content of button
2408  * @cfg {String} badge text inside badge
2409  * @cfg {String} glyphicon name of glyphicon
2410  * @cfg {String} icon name of font awesome icon
2411  * @cfg {Boolean} active Is item active
2412  * @cfg {Boolean} preventDefault (true | false) default false
2413  * @cfg {String} tabId the tab that this item activates.
2414   
2415  * @constructor
2416  * Create a new Navbar Button
2417  * @param {Object} config The config object
2418  */
2419 Roo.bootstrap.Navbar.Item = function(config){
2420     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2421     this.addEvents({
2422         // raw events
2423         /**
2424          * @event click
2425          * The raw click event for the entire grid.
2426          * @param {Roo.EventObject} e
2427          */
2428         "click" : true,
2429          /**
2430             * @event changed
2431             * Fires when the active item active state changes
2432             * @param {Roo.bootstrap.Navbar.Item} this
2433             * @param {boolean} state the new state
2434              
2435          */
2436         'changed': true
2437     });
2438    
2439 };
2440
2441 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2442     
2443     href: false,
2444     html: '',
2445     badge: '',
2446     icon: false,
2447     glyphicon: false,
2448     active: false,
2449     preventDefault : false,
2450     tabId : false,
2451     
2452     getAutoCreate : function(){
2453         
2454         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2455         
2456         if (this.parent().parent().sidebar === true) {
2457             cfg = {
2458                 tag: 'li',
2459                 cls: '',
2460                 cn: [
2461                     {
2462                         tag: 'p',
2463                         cls: ''
2464                     }
2465                 ]
2466             }
2467             
2468             if (this.html) {
2469                 cfg.cn[0].html = this.html;
2470             }
2471             
2472             if (this.active) {
2473                 this.cls += ' active';
2474             }
2475             
2476             if (this.menu) {
2477                 cfg.cn[0].cls += ' dropdown-toggle';
2478                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2479             }
2480             
2481             if (this.href) {
2482                 cfg.cn[0].tag = 'a',
2483                 cfg.cn[0].href = this.href;
2484             }
2485             
2486             if (this.glyphicon) {
2487                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2488             }
2489             
2490             if (this.icon) {
2491                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2492             }
2493             
2494             return cfg;
2495         }
2496         
2497         cfg = {
2498             tag: 'li',
2499             cls: 'nav-item'
2500         }
2501         
2502         if (this.active) {
2503             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2504         }
2505             
2506         cfg.cn = [
2507             {
2508                 tag: 'p',
2509                 html: 'Text'
2510             }
2511         ];
2512         
2513         if (this.glyphicon) {
2514             if(cfg.html){cfg.html = ' ' + this.html};
2515             cfg.cn=[
2516                 {
2517                     tag: 'span',
2518                     cls: 'glyphicon glyphicon-' + this.glyphicon
2519                 }
2520             ];
2521         }
2522         
2523         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2524         
2525         if (this.menu) {
2526             cfg.cn[0].tag='a';
2527             cfg.cn[0].href='#';
2528             cfg.cn[0].html += " <span class='caret'></span>";
2529         //}else if (!this.href) {
2530         //    cfg.cn[0].tag='p';
2531         //    cfg.cn[0].cls='navbar-text';
2532         } else {
2533             cfg.cn[0].tag='a';
2534             cfg.cn[0].href=this.href||'#';
2535             cfg.cn[0].html=this.html;
2536         }
2537         
2538         if (this.badge !== '') {
2539             
2540             cfg.cn[0].cn=[
2541                 cfg.cn[0].html + ' ',
2542                 {
2543                     tag: 'span',
2544                     cls: 'badge',
2545                     html: this.badge
2546                 }
2547             ];
2548             cfg.cn[0].html=''
2549         }
2550          
2551         if (this.icon) {
2552             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2553         }
2554         
2555         return cfg;
2556     },
2557     initEvents: function() {
2558        // Roo.log('init events?');
2559        // Roo.log(this.el.dom);
2560         this.el.select('a',true).on('click', this.onClick, this);
2561         // at this point parent should be available..
2562         this.parent().register(this);
2563     },
2564     
2565     onClick : function(e)
2566     {
2567         if(this.preventDefault){
2568             e.preventDefault();
2569         }
2570         
2571         if(this.fireEvent('click', this, e) === false){
2572             return;
2573         };
2574         
2575         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2576              if (typeof(this.parent().setActiveItem) !== 'undefined') {
2577                 this.parent().setActiveItem(this);
2578             }
2579             
2580             
2581             
2582         } 
2583     },
2584     
2585     isActive: function () {
2586         return this.active
2587     },
2588     setActive : function(state, fire)
2589     {
2590         this.active = state;
2591         if (!state ) {
2592             this.el.removeClass('active');
2593         } else if (!this.el.hasClass('active')) {
2594             this.el.addClass('active');
2595         }
2596         if (fire) {
2597             this.fireEvent('changed', this, state);
2598         }
2599         
2600         
2601     }
2602      // this should not be here...
2603  
2604 });
2605  
2606
2607  /*
2608  * - LGPL
2609  *
2610  * row
2611  * 
2612  */
2613
2614 /**
2615  * @class Roo.bootstrap.Row
2616  * @extends Roo.bootstrap.Component
2617  * Bootstrap Row class (contains columns...)
2618  * 
2619  * @constructor
2620  * Create a new Row
2621  * @param {Object} config The config object
2622  */
2623
2624 Roo.bootstrap.Row = function(config){
2625     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2626 };
2627
2628 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2629     
2630     getAutoCreate : function(){
2631        return {
2632             cls: 'row clearfix'
2633        };
2634     }
2635     
2636     
2637 });
2638
2639  
2640
2641  /*
2642  * - LGPL
2643  *
2644  * element
2645  * 
2646  */
2647
2648 /**
2649  * @class Roo.bootstrap.Element
2650  * @extends Roo.bootstrap.Component
2651  * Bootstrap Element class
2652  * @cfg {String} html contents of the element
2653  * @cfg {String} tag tag of the element
2654  * @cfg {String} cls class of the element
2655  * 
2656  * @constructor
2657  * Create a new Element
2658  * @param {Object} config The config object
2659  */
2660
2661 Roo.bootstrap.Element = function(config){
2662     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2663 };
2664
2665 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2666     
2667     tag: 'div',
2668     cls: '',
2669     html: '',
2670      
2671     
2672     getAutoCreate : function(){
2673         
2674         var cfg = {
2675             tag: this.tag,
2676             cls: this.cls,
2677             html: this.html
2678         }
2679         
2680         
2681         
2682         return cfg;
2683     }
2684    
2685 });
2686
2687  
2688
2689  /*
2690  * - LGPL
2691  *
2692  * pagination
2693  * 
2694  */
2695
2696 /**
2697  * @class Roo.bootstrap.Pagination
2698  * @extends Roo.bootstrap.Component
2699  * Bootstrap Pagination class
2700  * @cfg {String} size xs | sm | md | lg
2701  * @cfg {Boolean} inverse false | true
2702  * 
2703  * @constructor
2704  * Create a new Pagination
2705  * @param {Object} config The config object
2706  */
2707
2708 Roo.bootstrap.Pagination = function(config){
2709     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2710 };
2711
2712 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2713     
2714     cls: false,
2715     size: false,
2716     inverse: false,
2717     
2718     getAutoCreate : function(){
2719         var cfg = {
2720             tag: 'ul',
2721                 cls: 'pagination'
2722         };
2723         if (this.inverse) {
2724             cfg.cls += ' inverse';
2725         }
2726         if (this.html) {
2727             cfg.html=this.html;
2728         }
2729         if (this.cls) {
2730             cfg.cls += " " + this.cls;
2731         }
2732         return cfg;
2733     }
2734    
2735 });
2736
2737  
2738
2739  /*
2740  * - LGPL
2741  *
2742  * Pagination item
2743  * 
2744  */
2745
2746
2747 /**
2748  * @class Roo.bootstrap.PaginationItem
2749  * @extends Roo.bootstrap.Component
2750  * Bootstrap PaginationItem class
2751  * @cfg {String} html text
2752  * @cfg {String} href the link
2753  * @cfg {Boolean} preventDefault (true | false) default true
2754  * @cfg {Boolean} active (true | false) default false
2755  * 
2756  * 
2757  * @constructor
2758  * Create a new PaginationItem
2759  * @param {Object} config The config object
2760  */
2761
2762
2763 Roo.bootstrap.PaginationItem = function(config){
2764     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2765     this.addEvents({
2766         // raw events
2767         /**
2768          * @event click
2769          * The raw click event for the entire grid.
2770          * @param {Roo.EventObject} e
2771          */
2772         "click" : true
2773     });
2774 };
2775
2776 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2777     
2778     href : false,
2779     html : false,
2780     preventDefault: true,
2781     active : false,
2782     cls : false,
2783     
2784     getAutoCreate : function(){
2785         var cfg= {
2786             tag: 'li',
2787             cn: [
2788                 {
2789                     tag : 'a',
2790                     href : this.href ? this.href : '#',
2791                     html : this.html ? this.html : ''
2792                 }
2793             ]
2794         };
2795         
2796         if(this.cls){
2797             cfg.cls = this.cls;
2798         }
2799         
2800         if(this.active){
2801             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2802         }
2803         
2804         return cfg;
2805     },
2806     
2807     initEvents: function() {
2808         
2809         this.el.on('click', this.onClick, this);
2810         
2811     },
2812     onClick : function(e)
2813     {
2814         Roo.log('PaginationItem on click ');
2815         if(this.preventDefault){
2816             e.preventDefault();
2817         }
2818         
2819         this.fireEvent('click', this, e);
2820     }
2821    
2822 });
2823
2824  
2825
2826  /*
2827  * - LGPL
2828  *
2829  * slider
2830  * 
2831  */
2832
2833
2834 /**
2835  * @class Roo.bootstrap.Slider
2836  * @extends Roo.bootstrap.Component
2837  * Bootstrap Slider class
2838  *    
2839  * @constructor
2840  * Create a new Slider
2841  * @param {Object} config The config object
2842  */
2843
2844 Roo.bootstrap.Slider = function(config){
2845     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2846 };
2847
2848 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2849     
2850     getAutoCreate : function(){
2851         
2852         var cfg = {
2853             tag: 'div',
2854             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2855             cn: [
2856                 {
2857                     tag: 'a',
2858                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2859                 }
2860             ]
2861         }
2862         
2863         return cfg;
2864     }
2865    
2866 });
2867
2868  /*
2869  * - LGPL
2870  *
2871  * table
2872  * 
2873  */
2874
2875 /**
2876  * @class Roo.bootstrap.Table
2877  * @extends Roo.bootstrap.Component
2878  * Bootstrap Table class
2879  * @cfg {String} cls table class
2880  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2881  * @cfg {String} bgcolor Specifies the background color for a table
2882  * @cfg {Number} border Specifies whether the table cells should have borders or not
2883  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2884  * @cfg {Number} cellspacing Specifies the space between cells
2885  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2886  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2887  * @cfg {String} sortable Specifies that the table should be sortable
2888  * @cfg {String} summary Specifies a summary of the content of a table
2889  * @cfg {Number} width Specifies the width of a table
2890  * 
2891  * @cfg {boolean} striped Should the rows be alternative striped
2892  * @cfg {boolean} bordered Add borders to the table
2893  * @cfg {boolean} hover Add hover highlighting
2894  * @cfg {boolean} condensed Format condensed
2895  * @cfg {boolean} responsive Format condensed
2896  *
2897  
2898  
2899  * 
2900  * @constructor
2901  * Create a new Table
2902  * @param {Object} config The config object
2903  */
2904
2905 Roo.bootstrap.Table = function(config){
2906     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2907     
2908     if (this.sm) {
2909         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2910         this.sm = this.selModel;
2911         this.sm.xmodule = this.xmodule || false;
2912     }
2913     if (this.cm && typeof(this.cm.config) == 'undefined') {
2914         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2915         this.cm = this.colModel;
2916         this.cm.xmodule = this.xmodule || false;
2917     }
2918     if (this.store) {
2919         this.store= Roo.factory(this.store, Roo.data);
2920         this.ds = this.store;
2921         this.ds.xmodule = this.xmodule || false;
2922          
2923     }
2924 };
2925
2926 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2927     
2928     cls: false,
2929     align: false,
2930     bgcolor: false,
2931     border: false,
2932     cellpadding: false,
2933     cellspacing: false,
2934     frame: false,
2935     rules: false,
2936     sortable: false,
2937     summary: false,
2938     width: false,
2939     striped : false,
2940     bordered: false,
2941     hover:  false,
2942     condensed : false,
2943     responsive : false,
2944     sm : false,
2945     cm : false,
2946     store : false,
2947     
2948     getAutoCreate : function(){
2949         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2950         
2951         cfg = {
2952             tag: 'table',
2953             cls : 'table',
2954             cn : []
2955         }
2956             
2957         if (this.striped) {
2958             cfg.cls += ' table-striped';
2959         }
2960         if (this.hover) {
2961             cfg.cls += ' table-hover';
2962         }
2963         if (this.bordered) {
2964             cfg.cls += ' table-bordered';
2965         }
2966         if (this.condensed) {
2967             cfg.cls += ' table-condensed';
2968         }
2969         if (this.responsive) {
2970             cfg.cls += ' table-responsive';
2971         }
2972         
2973           
2974         
2975         
2976         if (this.cls) {
2977             cfg.cls+=  ' ' +this.cls;
2978         }
2979         
2980         // this lot should be simplifed...
2981         
2982         if (this.align) {
2983             cfg.align=this.align;
2984         }
2985         if (this.bgcolor) {
2986             cfg.bgcolor=this.bgcolor;
2987         }
2988         if (this.border) {
2989             cfg.border=this.border;
2990         }
2991         if (this.cellpadding) {
2992             cfg.cellpadding=this.cellpadding;
2993         }
2994         if (this.cellspacing) {
2995             cfg.cellspacing=this.cellspacing;
2996         }
2997         if (this.frame) {
2998             cfg.frame=this.frame;
2999         }
3000         if (this.rules) {
3001             cfg.rules=this.rules;
3002         }
3003         if (this.sortable) {
3004             cfg.sortable=this.sortable;
3005         }
3006         if (this.summary) {
3007             cfg.summary=this.summary;
3008         }
3009         if (this.width) {
3010             cfg.width=this.width;
3011         }
3012         
3013         if(this.store || this.cm){
3014             cfg.cn.push(this.renderHeader());
3015             cfg.cn.push(this.renderBody());
3016             cfg.cn.push(this.renderFooter());
3017             
3018             cfg.cls+=  ' TableGrid';
3019         }
3020         
3021         return cfg;
3022     },
3023 //    
3024 //    initTableGrid : function()
3025 //    {
3026 //        var cfg = {};
3027 //        
3028 //        var header = {
3029 //            tag: 'thead',
3030 //            cn : []
3031 //        };
3032 //        
3033 //        var cm = this.cm;
3034 //        
3035 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3036 //            header.cn.push({
3037 //                tag: 'th',
3038 //                html: cm.getColumnHeader(i)
3039 //            })
3040 //        }
3041 //        
3042 //        cfg.push(header);
3043 //        
3044 //        return cfg;
3045 //        
3046 //        
3047 //    },
3048     
3049     initEvents : function()
3050     {   
3051         if(!this.store || !this.cm){
3052             return;
3053         }
3054         
3055         Roo.log('initEvents with ds!!!!');
3056         
3057         var _this = this;
3058         
3059         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3060             e.on('click', _this.sort, _this);
3061         });
3062 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3063 //        this.maskEl.enableDisplayMode("block");
3064 //        this.maskEl.show();
3065         
3066         this.store.on('load', this.onLoad, this);
3067         this.store.on('beforeload', this.onBeforeLoad, this);
3068         
3069         this.store.load();
3070         
3071         
3072         
3073     },
3074     
3075     sort : function(e,el)
3076     {
3077         var col = Roo.get(el)
3078         
3079         if(!col.hasClass('sortable')){
3080             return;
3081         }
3082         
3083         var sort = col.attr('sort');
3084         var dir = 'ASC';
3085         
3086         if(col.hasClass('glyphicon-arrow-up')){
3087             dir = 'DESC';
3088         }
3089         
3090         this.store.sortInfo = {field : sort, direction : dir};
3091         
3092         this.store.load();
3093     },
3094     
3095     renderHeader : function()
3096     {
3097         var header = {
3098             tag: 'thead',
3099             cn : []
3100         };
3101         
3102         var cm = this.cm;
3103         
3104         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3105             
3106             var config = cm.config[i];
3107             
3108             var c = {
3109                 tag: 'th',
3110                 html: cm.getColumnHeader(i)
3111             };
3112             
3113             if(typeof(config.dataIndex) != 'undefined'){
3114                 c.sort = config.dataIndex;
3115             }
3116             
3117             if(typeof(config.sortable) != 'undefined' && config.sortable){
3118                 c.cls = 'sortable';
3119             }
3120             
3121             if(typeof(config.width) != 'undefined'){
3122                 c.style = 'width:' + config.width + 'px';
3123             }
3124             
3125             header.cn.push(c)
3126         }
3127         
3128         return header;
3129     },
3130     
3131     renderBody : function()
3132     {
3133         var body = {
3134             tag: 'tbody',
3135             cn : []
3136         };
3137         
3138         return body;
3139     },
3140     
3141     renderFooter : function()
3142     {
3143         var footer = {
3144             tag: 'tfoot',
3145             cn : []
3146         };
3147         
3148         return footer;
3149     },
3150     
3151     onLoad : function()
3152     {
3153         Roo.log('ds onload');
3154         
3155         var _this = this;
3156         var cm = this.cm;
3157         
3158         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3159             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3160             
3161             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3162                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3163             }
3164             
3165             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3166                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3167             }
3168         });
3169         
3170         var tbody = this.el.select('tbody', true).first();
3171         
3172         var renders = [];
3173         
3174         if(this.store.getCount() > 0){
3175             this.store.data.each(function(d){
3176                 var row = {
3177                     tag : 'tr',
3178                     cn : []
3179                 };
3180                 
3181                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3182                     var renderer = cm.getRenderer(i);
3183                     var config = cm.config[i];
3184                     var value = '';
3185                     var id = Roo.id();
3186                     
3187                     if(typeof(renderer) !== 'undefined'){
3188                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3189                     }
3190                     
3191                     if(typeof(value) === 'object'){
3192                         renders.push({
3193                             id : id,
3194                             cfg : value 
3195                         })
3196                     }
3197                     
3198                     var td = {
3199                         tag: 'td',
3200                         id: id,
3201                         html: (typeof(value) === 'object') ? '' : value
3202                     };
3203                     
3204                     if(typeof(config.width) != 'undefined'){
3205                         td.style = 'width:' +  config.width + 'px';
3206                     }
3207                     
3208                     row.cn.push(td);
3209                    
3210                 }
3211                 
3212                 tbody.createChild(row);
3213                 
3214             });
3215         }
3216         
3217         
3218         if(renders.length){
3219             var _this = this;
3220             Roo.each(renders, function(r){
3221                 _this.renderColumn(r);
3222             })
3223         }
3224 //        
3225 //        if(this.loadMask){
3226 //            this.maskEl.hide();
3227 //        }
3228     },
3229     
3230     onBeforeLoad : function()
3231     {
3232         Roo.log('ds onBeforeLoad');
3233         
3234         this.clear();
3235         
3236 //        if(this.loadMask){
3237 //            this.maskEl.show();
3238 //        }
3239     },
3240     
3241     clear : function()
3242     {
3243         this.el.select('tbody', true).first().dom.innerHTML = '';
3244     },
3245     
3246     getSelectionModel : function(){
3247         if(!this.selModel){
3248             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3249         }
3250         return this.selModel;
3251     },
3252     
3253     renderColumn : function(r)
3254     {
3255         var _this = this;
3256         r.cfg.render(Roo.get(r.id));
3257         
3258         if(r.cfg.cn){
3259             Roo.each(r.cfg.cn, function(c){
3260                 var child = {
3261                     id: r.id,
3262                     cfg: c
3263                 }
3264                 _this.renderColumn(child);
3265             })
3266         }
3267     }
3268    
3269 });
3270
3271  
3272
3273  /*
3274  * - LGPL
3275  *
3276  * table cell
3277  * 
3278  */
3279
3280 /**
3281  * @class Roo.bootstrap.TableCell
3282  * @extends Roo.bootstrap.Component
3283  * Bootstrap TableCell class
3284  * @cfg {String} html cell contain text
3285  * @cfg {String} cls cell class
3286  * @cfg {String} tag cell tag (td|th) default td
3287  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3288  * @cfg {String} align Aligns the content in a cell
3289  * @cfg {String} axis Categorizes cells
3290  * @cfg {String} bgcolor Specifies the background color of a cell
3291  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3292  * @cfg {Number} colspan Specifies the number of columns a cell should span
3293  * @cfg {String} headers Specifies one or more header cells a cell is related to
3294  * @cfg {Number} height Sets the height of a cell
3295  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3296  * @cfg {Number} rowspan Sets the number of rows a cell should span
3297  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3298  * @cfg {String} valign Vertical aligns the content in a cell
3299  * @cfg {Number} width Specifies the width of a cell
3300  * 
3301  * @constructor
3302  * Create a new TableCell
3303  * @param {Object} config The config object
3304  */
3305
3306 Roo.bootstrap.TableCell = function(config){
3307     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3308 };
3309
3310 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3311     
3312     html: false,
3313     cls: false,
3314     tag: false,
3315     abbr: false,
3316     align: false,
3317     axis: false,
3318     bgcolor: false,
3319     charoff: false,
3320     colspan: false,
3321     headers: false,
3322     height: false,
3323     nowrap: false,
3324     rowspan: false,
3325     scope: false,
3326     valign: false,
3327     width: false,
3328     
3329     
3330     getAutoCreate : function(){
3331         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3332         
3333         cfg = {
3334             tag: 'td'
3335         }
3336         
3337         if(this.tag){
3338             cfg.tag = this.tag;
3339         }
3340         
3341         if (this.html) {
3342             cfg.html=this.html
3343         }
3344         if (this.cls) {
3345             cfg.cls=this.cls
3346         }
3347         if (this.abbr) {
3348             cfg.abbr=this.abbr
3349         }
3350         if (this.align) {
3351             cfg.align=this.align
3352         }
3353         if (this.axis) {
3354             cfg.axis=this.axis
3355         }
3356         if (this.bgcolor) {
3357             cfg.bgcolor=this.bgcolor
3358         }
3359         if (this.charoff) {
3360             cfg.charoff=this.charoff
3361         }
3362         if (this.colspan) {
3363             cfg.colspan=this.colspan
3364         }
3365         if (this.headers) {
3366             cfg.headers=this.headers
3367         }
3368         if (this.height) {
3369             cfg.height=this.height
3370         }
3371         if (this.nowrap) {
3372             cfg.nowrap=this.nowrap
3373         }
3374         if (this.rowspan) {
3375             cfg.rowspan=this.rowspan
3376         }
3377         if (this.scope) {
3378             cfg.scope=this.scope
3379         }
3380         if (this.valign) {
3381             cfg.valign=this.valign
3382         }
3383         if (this.width) {
3384             cfg.width=this.width
3385         }
3386         
3387         
3388         return cfg;
3389     }
3390    
3391 });
3392
3393  
3394
3395  /*
3396  * - LGPL
3397  *
3398  * table row
3399  * 
3400  */
3401
3402 /**
3403  * @class Roo.bootstrap.TableRow
3404  * @extends Roo.bootstrap.Component
3405  * Bootstrap TableRow class
3406  * @cfg {String} cls row class
3407  * @cfg {String} align Aligns the content in a table row
3408  * @cfg {String} bgcolor Specifies a background color for a table row
3409  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3410  * @cfg {String} valign Vertical aligns the content in a table row
3411  * 
3412  * @constructor
3413  * Create a new TableRow
3414  * @param {Object} config The config object
3415  */
3416
3417 Roo.bootstrap.TableRow = function(config){
3418     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3419 };
3420
3421 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3422     
3423     cls: false,
3424     align: false,
3425     bgcolor: false,
3426     charoff: false,
3427     valign: false,
3428     
3429     getAutoCreate : function(){
3430         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3431         
3432         cfg = {
3433             tag: 'tr'
3434         }
3435             
3436         if(this.cls){
3437             cfg.cls = this.cls;
3438         }
3439         if(this.align){
3440             cfg.align = this.align;
3441         }
3442         if(this.bgcolor){
3443             cfg.bgcolor = this.bgcolor;
3444         }
3445         if(this.charoff){
3446             cfg.charoff = this.charoff;
3447         }
3448         if(this.valign){
3449             cfg.valign = this.valign;
3450         }
3451         
3452         return cfg;
3453     }
3454    
3455 });
3456
3457  
3458
3459  /*
3460  * - LGPL
3461  *
3462  * table body
3463  * 
3464  */
3465
3466 /**
3467  * @class Roo.bootstrap.TableBody
3468  * @extends Roo.bootstrap.Component
3469  * Bootstrap TableBody class
3470  * @cfg {String} cls element class
3471  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3472  * @cfg {String} align Aligns the content inside the element
3473  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3474  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3475  * 
3476  * @constructor
3477  * Create a new TableBody
3478  * @param {Object} config The config object
3479  */
3480
3481 Roo.bootstrap.TableBody = function(config){
3482     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3483 };
3484
3485 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3486     
3487     cls: false,
3488     tag: false,
3489     align: false,
3490     charoff: false,
3491     valign: false,
3492     
3493     getAutoCreate : function(){
3494         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3495         
3496         cfg = {
3497             tag: 'tbody'
3498         }
3499             
3500         if (this.cls) {
3501             cfg.cls=this.cls
3502         }
3503         if(this.tag){
3504             cfg.tag = this.tag;
3505         }
3506         
3507         if(this.align){
3508             cfg.align = this.align;
3509         }
3510         if(this.charoff){
3511             cfg.charoff = this.charoff;
3512         }
3513         if(this.valign){
3514             cfg.valign = this.valign;
3515         }
3516         
3517         return cfg;
3518     }
3519     
3520     
3521 //    initEvents : function()
3522 //    {
3523 //        
3524 //        if(!this.store){
3525 //            return;
3526 //        }
3527 //        
3528 //        this.store = Roo.factory(this.store, Roo.data);
3529 //        this.store.on('load', this.onLoad, this);
3530 //        
3531 //        this.store.load();
3532 //        
3533 //    },
3534 //    
3535 //    onLoad: function () 
3536 //    {   
3537 //        this.fireEvent('load', this);
3538 //    }
3539 //    
3540 //   
3541 });
3542
3543  
3544
3545  /*
3546  * Based on:
3547  * Ext JS Library 1.1.1
3548  * Copyright(c) 2006-2007, Ext JS, LLC.
3549  *
3550  * Originally Released Under LGPL - original licence link has changed is not relivant.
3551  *
3552  * Fork - LGPL
3553  * <script type="text/javascript">
3554  */
3555
3556 // as we use this in bootstrap.
3557 Roo.namespace('Roo.form');
3558  /**
3559  * @class Roo.form.Action
3560  * Internal Class used to handle form actions
3561  * @constructor
3562  * @param {Roo.form.BasicForm} el The form element or its id
3563  * @param {Object} config Configuration options
3564  */
3565
3566  
3567  
3568 // define the action interface
3569 Roo.form.Action = function(form, options){
3570     this.form = form;
3571     this.options = options || {};
3572 };
3573 /**
3574  * Client Validation Failed
3575  * @const 
3576  */
3577 Roo.form.Action.CLIENT_INVALID = 'client';
3578 /**
3579  * Server Validation Failed
3580  * @const 
3581  */
3582 Roo.form.Action.SERVER_INVALID = 'server';
3583  /**
3584  * Connect to Server Failed
3585  * @const 
3586  */
3587 Roo.form.Action.CONNECT_FAILURE = 'connect';
3588 /**
3589  * Reading Data from Server Failed
3590  * @const 
3591  */
3592 Roo.form.Action.LOAD_FAILURE = 'load';
3593
3594 Roo.form.Action.prototype = {
3595     type : 'default',
3596     failureType : undefined,
3597     response : undefined,
3598     result : undefined,
3599
3600     // interface method
3601     run : function(options){
3602
3603     },
3604
3605     // interface method
3606     success : function(response){
3607
3608     },
3609
3610     // interface method
3611     handleResponse : function(response){
3612
3613     },
3614
3615     // default connection failure
3616     failure : function(response){
3617         
3618         this.response = response;
3619         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3620         this.form.afterAction(this, false);
3621     },
3622
3623     processResponse : function(response){
3624         this.response = response;
3625         if(!response.responseText){
3626             return true;
3627         }
3628         this.result = this.handleResponse(response);
3629         return this.result;
3630     },
3631
3632     // utility functions used internally
3633     getUrl : function(appendParams){
3634         var url = this.options.url || this.form.url || this.form.el.dom.action;
3635         if(appendParams){
3636             var p = this.getParams();
3637             if(p){
3638                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3639             }
3640         }
3641         return url;
3642     },
3643
3644     getMethod : function(){
3645         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3646     },
3647
3648     getParams : function(){
3649         var bp = this.form.baseParams;
3650         var p = this.options.params;
3651         if(p){
3652             if(typeof p == "object"){
3653                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3654             }else if(typeof p == 'string' && bp){
3655                 p += '&' + Roo.urlEncode(bp);
3656             }
3657         }else if(bp){
3658             p = Roo.urlEncode(bp);
3659         }
3660         return p;
3661     },
3662
3663     createCallback : function(){
3664         return {
3665             success: this.success,
3666             failure: this.failure,
3667             scope: this,
3668             timeout: (this.form.timeout*1000),
3669             upload: this.form.fileUpload ? this.success : undefined
3670         };
3671     }
3672 };
3673
3674 Roo.form.Action.Submit = function(form, options){
3675     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3676 };
3677
3678 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3679     type : 'submit',
3680
3681     haveProgress : false,
3682     uploadComplete : false,
3683     
3684     // uploadProgress indicator.
3685     uploadProgress : function()
3686     {
3687         if (!this.form.progressUrl) {
3688             return;
3689         }
3690         
3691         if (!this.haveProgress) {
3692             Roo.MessageBox.progress("Uploading", "Uploading");
3693         }
3694         if (this.uploadComplete) {
3695            Roo.MessageBox.hide();
3696            return;
3697         }
3698         
3699         this.haveProgress = true;
3700    
3701         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3702         
3703         var c = new Roo.data.Connection();
3704         c.request({
3705             url : this.form.progressUrl,
3706             params: {
3707                 id : uid
3708             },
3709             method: 'GET',
3710             success : function(req){
3711                //console.log(data);
3712                 var rdata = false;
3713                 var edata;
3714                 try  {
3715                    rdata = Roo.decode(req.responseText)
3716                 } catch (e) {
3717                     Roo.log("Invalid data from server..");
3718                     Roo.log(edata);
3719                     return;
3720                 }
3721                 if (!rdata || !rdata.success) {
3722                     Roo.log(rdata);
3723                     Roo.MessageBox.alert(Roo.encode(rdata));
3724                     return;
3725                 }
3726                 var data = rdata.data;
3727                 
3728                 if (this.uploadComplete) {
3729                    Roo.MessageBox.hide();
3730                    return;
3731                 }
3732                    
3733                 if (data){
3734                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3735                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3736                     );
3737                 }
3738                 this.uploadProgress.defer(2000,this);
3739             },
3740        
3741             failure: function(data) {
3742                 Roo.log('progress url failed ');
3743                 Roo.log(data);
3744             },
3745             scope : this
3746         });
3747            
3748     },
3749     
3750     
3751     run : function()
3752     {
3753         // run get Values on the form, so it syncs any secondary forms.
3754         this.form.getValues();
3755         
3756         var o = this.options;
3757         var method = this.getMethod();
3758         var isPost = method == 'POST';
3759         if(o.clientValidation === false || this.form.isValid()){
3760             
3761             if (this.form.progressUrl) {
3762                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3763                     (new Date() * 1) + '' + Math.random());
3764                     
3765             } 
3766             
3767             
3768             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3769                 form:this.form.el.dom,
3770                 url:this.getUrl(!isPost),
3771                 method: method,
3772                 params:isPost ? this.getParams() : null,
3773                 isUpload: this.form.fileUpload
3774             }));
3775             
3776             this.uploadProgress();
3777
3778         }else if (o.clientValidation !== false){ // client validation failed
3779             this.failureType = Roo.form.Action.CLIENT_INVALID;
3780             this.form.afterAction(this, false);
3781         }
3782     },
3783
3784     success : function(response)
3785     {
3786         this.uploadComplete= true;
3787         if (this.haveProgress) {
3788             Roo.MessageBox.hide();
3789         }
3790         
3791         
3792         var result = this.processResponse(response);
3793         if(result === true || result.success){
3794             this.form.afterAction(this, true);
3795             return;
3796         }
3797         if(result.errors){
3798             this.form.markInvalid(result.errors);
3799             this.failureType = Roo.form.Action.SERVER_INVALID;
3800         }
3801         this.form.afterAction(this, false);
3802     },
3803     failure : function(response)
3804     {
3805         this.uploadComplete= true;
3806         if (this.haveProgress) {
3807             Roo.MessageBox.hide();
3808         }
3809         
3810         this.response = response;
3811         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3812         this.form.afterAction(this, false);
3813     },
3814     
3815     handleResponse : function(response){
3816         if(this.form.errorReader){
3817             var rs = this.form.errorReader.read(response);
3818             var errors = [];
3819             if(rs.records){
3820                 for(var i = 0, len = rs.records.length; i < len; i++) {
3821                     var r = rs.records[i];
3822                     errors[i] = r.data;
3823                 }
3824             }
3825             if(errors.length < 1){
3826                 errors = null;
3827             }
3828             return {
3829                 success : rs.success,
3830                 errors : errors
3831             };
3832         }
3833         var ret = false;
3834         try {
3835             ret = Roo.decode(response.responseText);
3836         } catch (e) {
3837             ret = {
3838                 success: false,
3839                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3840                 errors : []
3841             };
3842         }
3843         return ret;
3844         
3845     }
3846 });
3847
3848
3849 Roo.form.Action.Load = function(form, options){
3850     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3851     this.reader = this.form.reader;
3852 };
3853
3854 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3855     type : 'load',
3856
3857     run : function(){
3858         
3859         Roo.Ajax.request(Roo.apply(
3860                 this.createCallback(), {
3861                     method:this.getMethod(),
3862                     url:this.getUrl(false),
3863                     params:this.getParams()
3864         }));
3865     },
3866
3867     success : function(response){
3868         
3869         var result = this.processResponse(response);
3870         if(result === true || !result.success || !result.data){
3871             this.failureType = Roo.form.Action.LOAD_FAILURE;
3872             this.form.afterAction(this, false);
3873             return;
3874         }
3875         this.form.clearInvalid();
3876         this.form.setValues(result.data);
3877         this.form.afterAction(this, true);
3878     },
3879
3880     handleResponse : function(response){
3881         if(this.form.reader){
3882             var rs = this.form.reader.read(response);
3883             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3884             return {
3885                 success : rs.success,
3886                 data : data
3887             };
3888         }
3889         return Roo.decode(response.responseText);
3890     }
3891 });
3892
3893 Roo.form.Action.ACTION_TYPES = {
3894     'load' : Roo.form.Action.Load,
3895     'submit' : Roo.form.Action.Submit
3896 };/*
3897  * - LGPL
3898  *
3899  * form
3900  * 
3901  */
3902
3903 /**
3904  * @class Roo.bootstrap.Form
3905  * @extends Roo.bootstrap.Component
3906  * Bootstrap Form class
3907  * @cfg {String} method  GET | POST (default POST)
3908  * @cfg {String} labelAlign top | left (default top)
3909   * @cfg {String} align left  | right - for navbars
3910
3911  * 
3912  * @constructor
3913  * Create a new Form
3914  * @param {Object} config The config object
3915  */
3916
3917
3918 Roo.bootstrap.Form = function(config){
3919     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3920     this.addEvents({
3921         /**
3922          * @event clientvalidation
3923          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3924          * @param {Form} this
3925          * @param {Boolean} valid true if the form has passed client-side validation
3926          */
3927         clientvalidation: true,
3928         /**
3929          * @event beforeaction
3930          * Fires before any action is performed. Return false to cancel the action.
3931          * @param {Form} this
3932          * @param {Action} action The action to be performed
3933          */
3934         beforeaction: true,
3935         /**
3936          * @event actionfailed
3937          * Fires when an action fails.
3938          * @param {Form} this
3939          * @param {Action} action The action that failed
3940          */
3941         actionfailed : true,
3942         /**
3943          * @event actioncomplete
3944          * Fires when an action is completed.
3945          * @param {Form} this
3946          * @param {Action} action The action that completed
3947          */
3948         actioncomplete : true
3949     });
3950     
3951 };
3952
3953 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3954       
3955      /**
3956      * @cfg {String} method
3957      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3958      */
3959     method : 'POST',
3960     /**
3961      * @cfg {String} url
3962      * The URL to use for form actions if one isn't supplied in the action options.
3963      */
3964     /**
3965      * @cfg {Boolean} fileUpload
3966      * Set to true if this form is a file upload.
3967      */
3968      
3969     /**
3970      * @cfg {Object} baseParams
3971      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3972      */
3973       
3974     /**
3975      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3976      */
3977     timeout: 30,
3978     /**
3979      * @cfg {Sting} align (left|right) for navbar forms
3980      */
3981     align : 'left',
3982
3983     // private
3984     activeAction : null,
3985  
3986     /**
3987      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3988      * element by passing it or its id or mask the form itself by passing in true.
3989      * @type Mixed
3990      */
3991     waitMsgTarget : false,
3992     
3993      
3994     
3995     /**
3996      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3997      * element by passing it or its id or mask the form itself by passing in true.
3998      * @type Mixed
3999      */
4000     
4001     getAutoCreate : function(){
4002         
4003         var cfg = {
4004             tag: 'form',
4005             method : this.method || 'POST',
4006             id : this.id || Roo.id(),
4007             cls : ''
4008         }
4009         if (this.parent().xtype.match(/^Nav/)) {
4010             cfg.cls = 'navbar-form navbar-' + this.align;
4011             
4012         }
4013         
4014         if (this.labelAlign == 'left' ) {
4015             cfg.cls += ' form-horizontal';
4016         }
4017         
4018         
4019         return cfg;
4020     },
4021     initEvents : function()
4022     {
4023         this.el.on('submit', this.onSubmit, this);
4024         
4025         
4026     },
4027     // private
4028     onSubmit : function(e){
4029         e.stopEvent();
4030     },
4031     
4032      /**
4033      * Returns true if client-side validation on the form is successful.
4034      * @return Boolean
4035      */
4036     isValid : function(){
4037         var items = this.getItems();
4038         var valid = true;
4039         items.each(function(f){
4040            if(!f.validate()){
4041                valid = false;
4042                
4043            }
4044         });
4045         return valid;
4046     },
4047     /**
4048      * Returns true if any fields in this form have changed since their original load.
4049      * @return Boolean
4050      */
4051     isDirty : function(){
4052         var dirty = false;
4053         var items = this.getItems();
4054         items.each(function(f){
4055            if(f.isDirty()){
4056                dirty = true;
4057                return false;
4058            }
4059            return true;
4060         });
4061         return dirty;
4062     },
4063      /**
4064      * Performs a predefined action (submit or load) or custom actions you define on this form.
4065      * @param {String} actionName The name of the action type
4066      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
4067      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4068      * accept other config options):
4069      * <pre>
4070 Property          Type             Description
4071 ----------------  ---------------  ----------------------------------------------------------------------------------
4072 url               String           The url for the action (defaults to the form's url)
4073 method            String           The form method to use (defaults to the form's method, or POST if not defined)
4074 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
4075 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
4076                                    validate the form on the client (defaults to false)
4077      * </pre>
4078      * @return {BasicForm} this
4079      */
4080     doAction : function(action, options){
4081         if(typeof action == 'string'){
4082             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4083         }
4084         if(this.fireEvent('beforeaction', this, action) !== false){
4085             this.beforeAction(action);
4086             action.run.defer(100, action);
4087         }
4088         return this;
4089     },
4090     
4091     // private
4092     beforeAction : function(action){
4093         var o = action.options;
4094         
4095         // not really supported yet.. ??
4096         
4097         //if(this.waitMsgTarget === true){
4098             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4099         //}else if(this.waitMsgTarget){
4100         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4101         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4102         //}else {
4103         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4104        // }
4105          
4106     },
4107
4108     // private
4109     afterAction : function(action, success){
4110         this.activeAction = null;
4111         var o = action.options;
4112         
4113         //if(this.waitMsgTarget === true){
4114             this.el.unmask();
4115         //}else if(this.waitMsgTarget){
4116         //    this.waitMsgTarget.unmask();
4117         //}else{
4118         //    Roo.MessageBox.updateProgress(1);
4119         //    Roo.MessageBox.hide();
4120        // }
4121         // 
4122         if(success){
4123             if(o.reset){
4124                 this.reset();
4125             }
4126             Roo.callback(o.success, o.scope, [this, action]);
4127             this.fireEvent('actioncomplete', this, action);
4128             
4129         }else{
4130             
4131             // failure condition..
4132             // we have a scenario where updates need confirming.
4133             // eg. if a locking scenario exists..
4134             // we look for { errors : { needs_confirm : true }} in the response.
4135             if (
4136                 (typeof(action.result) != 'undefined')  &&
4137                 (typeof(action.result.errors) != 'undefined')  &&
4138                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4139            ){
4140                 var _t = this;
4141                 Roo.log("not supported yet");
4142                  /*
4143                 
4144                 Roo.MessageBox.confirm(
4145                     "Change requires confirmation",
4146                     action.result.errorMsg,
4147                     function(r) {
4148                         if (r != 'yes') {
4149                             return;
4150                         }
4151                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4152                     }
4153                     
4154                 );
4155                 */
4156                 
4157                 
4158                 return;
4159             }
4160             
4161             Roo.callback(o.failure, o.scope, [this, action]);
4162             // show an error message if no failed handler is set..
4163             if (!this.hasListener('actionfailed')) {
4164                 Roo.log("need to add dialog support");
4165                 /*
4166                 Roo.MessageBox.alert("Error",
4167                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4168                         action.result.errorMsg :
4169                         "Saving Failed, please check your entries or try again"
4170                 );
4171                 */
4172             }
4173             
4174             this.fireEvent('actionfailed', this, action);
4175         }
4176         
4177     },
4178     /**
4179      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4180      * @param {String} id The value to search for
4181      * @return Field
4182      */
4183     findField : function(id){
4184         var items = this.getItems();
4185         var field = items.get(id);
4186         if(!field){
4187              items.each(function(f){
4188                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4189                     field = f;
4190                     return false;
4191                 }
4192                 return true;
4193             });
4194         }
4195         return field || null;
4196     },
4197      /**
4198      * Mark fields in this form invalid in bulk.
4199      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4200      * @return {BasicForm} this
4201      */
4202     markInvalid : function(errors){
4203         if(errors instanceof Array){
4204             for(var i = 0, len = errors.length; i < len; i++){
4205                 var fieldError = errors[i];
4206                 var f = this.findField(fieldError.id);
4207                 if(f){
4208                     f.markInvalid(fieldError.msg);
4209                 }
4210             }
4211         }else{
4212             var field, id;
4213             for(id in errors){
4214                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4215                     field.markInvalid(errors[id]);
4216                 }
4217             }
4218         }
4219         //Roo.each(this.childForms || [], function (f) {
4220         //    f.markInvalid(errors);
4221         //});
4222         
4223         return this;
4224     },
4225
4226     /**
4227      * Set values for fields in this form in bulk.
4228      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4229      * @return {BasicForm} this
4230      */
4231     setValues : function(values){
4232         if(values instanceof Array){ // array of objects
4233             for(var i = 0, len = values.length; i < len; i++){
4234                 var v = values[i];
4235                 var f = this.findField(v.id);
4236                 if(f){
4237                     f.setValue(v.value);
4238                     if(this.trackResetOnLoad){
4239                         f.originalValue = f.getValue();
4240                     }
4241                 }
4242             }
4243         }else{ // object hash
4244             var field, id;
4245             for(id in values){
4246                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4247                     
4248                     if (field.setFromData && 
4249                         field.valueField && 
4250                         field.displayField &&
4251                         // combos' with local stores can 
4252                         // be queried via setValue()
4253                         // to set their value..
4254                         (field.store && !field.store.isLocal)
4255                         ) {
4256                         // it's a combo
4257                         var sd = { };
4258                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4259                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4260                         field.setFromData(sd);
4261                         
4262                     } else {
4263                         field.setValue(values[id]);
4264                     }
4265                     
4266                     
4267                     if(this.trackResetOnLoad){
4268                         field.originalValue = field.getValue();
4269                     }
4270                 }
4271             }
4272         }
4273          
4274         //Roo.each(this.childForms || [], function (f) {
4275         //    f.setValues(values);
4276         //});
4277                 
4278         return this;
4279     },
4280
4281     /**
4282      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4283      * they are returned as an array.
4284      * @param {Boolean} asString
4285      * @return {Object}
4286      */
4287     getValues : function(asString){
4288         //if (this.childForms) {
4289             // copy values from the child forms
4290         //    Roo.each(this.childForms, function (f) {
4291         //        this.setValues(f.getValues());
4292         //    }, this);
4293         //}
4294         
4295         
4296         
4297         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4298         if(asString === true){
4299             return fs;
4300         }
4301         return Roo.urlDecode(fs);
4302     },
4303     
4304     /**
4305      * Returns the fields in this form as an object with key/value pairs. 
4306      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4307      * @return {Object}
4308      */
4309     getFieldValues : function(with_hidden)
4310     {
4311         var items = this.getItems();
4312         var ret = {};
4313         items.each(function(f){
4314             if (!f.getName()) {
4315                 return;
4316             }
4317             var v = f.getValue();
4318             if (f.inputType =='radio') {
4319                 if (typeof(ret[f.getName()]) == 'undefined') {
4320                     ret[f.getName()] = ''; // empty..
4321                 }
4322                 
4323                 if (!f.el.dom.checked) {
4324                     return;
4325                     
4326                 }
4327                 v = f.el.dom.value;
4328                 
4329             }
4330             
4331             // not sure if this supported any more..
4332             if ((typeof(v) == 'object') && f.getRawValue) {
4333                 v = f.getRawValue() ; // dates..
4334             }
4335             // combo boxes where name != hiddenName...
4336             if (f.name != f.getName()) {
4337                 ret[f.name] = f.getRawValue();
4338             }
4339             ret[f.getName()] = v;
4340         });
4341         
4342         return ret;
4343     },
4344
4345     /**
4346      * Clears all invalid messages in this form.
4347      * @return {BasicForm} this
4348      */
4349     clearInvalid : function(){
4350         var items = this.getItems();
4351         
4352         items.each(function(f){
4353            f.clearInvalid();
4354         });
4355         
4356         
4357         
4358         return this;
4359     },
4360
4361     /**
4362      * Resets this form.
4363      * @return {BasicForm} this
4364      */
4365     reset : function(){
4366         var items = this.getItems();
4367         items.each(function(f){
4368             f.reset();
4369         });
4370         
4371         Roo.each(this.childForms || [], function (f) {
4372             f.reset();
4373         });
4374        
4375         
4376         return this;
4377     },
4378     getItems : function()
4379     {
4380         var r=new Roo.util.MixedCollection(false, function(o){
4381             return o.id || (o.id = Roo.id());
4382         });
4383         var iter = function(el) {
4384             if (el.inputEl) {
4385                 r.add(el);
4386             }
4387             if (!el.items) {
4388                 return;
4389             }
4390             Roo.each(el.items,function(e) {
4391                 iter(e);
4392             });
4393             
4394             
4395         };
4396         iter(this);
4397         return r;
4398         
4399         
4400         
4401         
4402     }
4403     
4404 });
4405
4406  
4407 /*
4408  * Based on:
4409  * Ext JS Library 1.1.1
4410  * Copyright(c) 2006-2007, Ext JS, LLC.
4411  *
4412  * Originally Released Under LGPL - original licence link has changed is not relivant.
4413  *
4414  * Fork - LGPL
4415  * <script type="text/javascript">
4416  */
4417 /**
4418  * @class Roo.form.VTypes
4419  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4420  * @singleton
4421  */
4422 Roo.form.VTypes = function(){
4423     // closure these in so they are only created once.
4424     var alpha = /^[a-zA-Z_]+$/;
4425     var alphanum = /^[a-zA-Z0-9_]+$/;
4426     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4427     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4428
4429     // All these messages and functions are configurable
4430     return {
4431         /**
4432          * The function used to validate email addresses
4433          * @param {String} value The email address
4434          */
4435         'email' : function(v){
4436             return email.test(v);
4437         },
4438         /**
4439          * The error text to display when the email validation function returns false
4440          * @type String
4441          */
4442         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4443         /**
4444          * The keystroke filter mask to be applied on email input
4445          * @type RegExp
4446          */
4447         'emailMask' : /[a-z0-9_\.\-@]/i,
4448
4449         /**
4450          * The function used to validate URLs
4451          * @param {String} value The URL
4452          */
4453         'url' : function(v){
4454             return url.test(v);
4455         },
4456         /**
4457          * The error text to display when the url validation function returns false
4458          * @type String
4459          */
4460         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4461         
4462         /**
4463          * The function used to validate alpha values
4464          * @param {String} value The value
4465          */
4466         'alpha' : function(v){
4467             return alpha.test(v);
4468         },
4469         /**
4470          * The error text to display when the alpha validation function returns false
4471          * @type String
4472          */
4473         'alphaText' : 'This field should only contain letters and _',
4474         /**
4475          * The keystroke filter mask to be applied on alpha input
4476          * @type RegExp
4477          */
4478         'alphaMask' : /[a-z_]/i,
4479
4480         /**
4481          * The function used to validate alphanumeric values
4482          * @param {String} value The value
4483          */
4484         'alphanum' : function(v){
4485             return alphanum.test(v);
4486         },
4487         /**
4488          * The error text to display when the alphanumeric validation function returns false
4489          * @type String
4490          */
4491         'alphanumText' : 'This field should only contain letters, numbers and _',
4492         /**
4493          * The keystroke filter mask to be applied on alphanumeric input
4494          * @type RegExp
4495          */
4496         'alphanumMask' : /[a-z0-9_]/i
4497     };
4498 }();/*
4499  * - LGPL
4500  *
4501  * Input
4502  * 
4503  */
4504
4505 /**
4506  * @class Roo.bootstrap.Input
4507  * @extends Roo.bootstrap.Component
4508  * Bootstrap Input class
4509  * @cfg {Boolean} disabled is it disabled
4510  * @cfg {String} fieldLabel - the label associated
4511  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4512  * @cfg {String} name name of the input
4513  * @cfg {string} fieldLabel - the label associated
4514  * @cfg {string}  inputType - input / file submit ...
4515  * @cfg {string} placeholder - placeholder to put in text.
4516  * @cfg {string}  before - input group add on before
4517  * @cfg {string} after - input group add on after
4518  * @cfg {string} size - (lg|sm) or leave empty..
4519  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4520  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4521  * @cfg {Number} md colspan out of 12 for computer-sized screens
4522  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4523  * @cfg {string} value default value of the input
4524  * @cfg {Number} labelWidth set the width of label (0-12)
4525  * @cfg {String} labelAlign (top|left)
4526  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4527  * 
4528  * 
4529  * @constructor
4530  * Create a new Input
4531  * @param {Object} config The config object
4532  */
4533
4534 Roo.bootstrap.Input = function(config){
4535     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4536    
4537         this.addEvents({
4538             /**
4539              * @event focus
4540              * Fires when this field receives input focus.
4541              * @param {Roo.form.Field} this
4542              */
4543             focus : true,
4544             /**
4545              * @event blur
4546              * Fires when this field loses input focus.
4547              * @param {Roo.form.Field} this
4548              */
4549             blur : true,
4550             /**
4551              * @event specialkey
4552              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4553              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4554              * @param {Roo.form.Field} this
4555              * @param {Roo.EventObject} e The event object
4556              */
4557             specialkey : true,
4558             /**
4559              * @event change
4560              * Fires just before the field blurs if the field value has changed.
4561              * @param {Roo.form.Field} this
4562              * @param {Mixed} newValue The new value
4563              * @param {Mixed} oldValue The original value
4564              */
4565             change : true,
4566             /**
4567              * @event invalid
4568              * Fires after the field has been marked as invalid.
4569              * @param {Roo.form.Field} this
4570              * @param {String} msg The validation message
4571              */
4572             invalid : true,
4573             /**
4574              * @event valid
4575              * Fires after the field has been validated with no errors.
4576              * @param {Roo.form.Field} this
4577              */
4578             valid : true,
4579              /**
4580              * @event keyup
4581              * Fires after the key up
4582              * @param {Roo.form.Field} this
4583              * @param {Roo.EventObject}  e The event Object
4584              */
4585             keyup : true
4586         });
4587 };
4588
4589 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4590      /**
4591      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4592       automatic validation (defaults to "keyup").
4593      */
4594     validationEvent : "keyup",
4595      /**
4596      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4597      */
4598     validateOnBlur : true,
4599     /**
4600      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4601      */
4602     validationDelay : 250,
4603      /**
4604      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4605      */
4606     focusClass : "x-form-focus",  // not needed???
4607     
4608        
4609     /**
4610      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4611      */
4612     invalidClass : "has-error",
4613     
4614     /**
4615      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4616      */
4617     selectOnFocus : false,
4618     
4619      /**
4620      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4621      */
4622     maskRe : null,
4623        /**
4624      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4625      */
4626     vtype : null,
4627     
4628       /**
4629      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4630      */
4631     disableKeyFilter : false,
4632     
4633        /**
4634      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4635      */
4636     disabled : false,
4637      /**
4638      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4639      */
4640     allowBlank : true,
4641     /**
4642      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4643      */
4644     blankText : "This field is required",
4645     
4646      /**
4647      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4648      */
4649     minLength : 0,
4650     /**
4651      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4652      */
4653     maxLength : Number.MAX_VALUE,
4654     /**
4655      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4656      */
4657     minLengthText : "The minimum length for this field is {0}",
4658     /**
4659      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4660      */
4661     maxLengthText : "The maximum length for this field is {0}",
4662   
4663     
4664     /**
4665      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4666      * If available, this function will be called only after the basic validators all return true, and will be passed the
4667      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4668      */
4669     validator : null,
4670     /**
4671      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4672      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4673      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4674      */
4675     regex : null,
4676     /**
4677      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4678      */
4679     regexText : "",
4680     
4681     
4682     
4683     fieldLabel : '',
4684     inputType : 'text',
4685     
4686     name : false,
4687     placeholder: false,
4688     before : false,
4689     after : false,
4690     size : false,
4691     // private
4692     hasFocus : false,
4693     preventMark: false,
4694     isFormField : true,
4695     value : '',
4696     labelWidth : 2,
4697     labelAlign : false,
4698     readOnly : false,
4699     
4700     parentLabelAlign : function()
4701     {
4702         var parent = this;
4703         while (parent.parent()) {
4704             parent = parent.parent();
4705             if (typeof(parent.labelAlign) !='undefined') {
4706                 return parent.labelAlign;
4707             }
4708         }
4709         return 'left';
4710         
4711     },
4712     
4713     getAutoCreate : function(){
4714         
4715         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4716         
4717         var id = Roo.id();
4718         
4719         var cfg = {};
4720         
4721         if(this.inputType != 'hidden'){
4722             cfg.cls = 'form-group' //input-group
4723         }
4724         
4725         var input =  {
4726             tag: 'input',
4727             id : id,
4728             type : this.inputType,
4729             value : this.value,
4730             cls : 'form-control',
4731             placeholder : this.placeholder || ''
4732             
4733         };
4734         
4735         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4736             input.maxLength = this.maxLength;
4737         }
4738         
4739         if (this.disabled) {
4740             input.disabled=true;
4741         }
4742         
4743         if (this.readOnly) {
4744             input.readonly=true;
4745         }
4746         
4747         if (this.name) {
4748             input.name = this.name;
4749         }
4750         if (this.size) {
4751             input.cls += ' input-' + this.size;
4752         }
4753         var settings=this;
4754         ['xs','sm','md','lg'].map(function(size){
4755             if (settings[size]) {
4756                 cfg.cls += ' col-' + size + '-' + settings[size];
4757             }
4758         });
4759         
4760         var inputblock = input;
4761         
4762         if (this.before || this.after) {
4763             
4764             inputblock = {
4765                 cls : 'input-group',
4766                 cn :  [] 
4767             };
4768             if (this.before) {
4769                 inputblock.cn.push({
4770                     tag :'span',
4771                     cls : 'input-group-addon',
4772                     html : this.before
4773                 });
4774             }
4775             inputblock.cn.push(input);
4776             if (this.after) {
4777                 inputblock.cn.push({
4778                     tag :'span',
4779                     cls : 'input-group-addon',
4780                     html : this.after
4781                 });
4782             }
4783             
4784         };
4785         
4786         if (align ==='left' && this.fieldLabel.length) {
4787                 Roo.log("left and has label");
4788                 cfg.cn = [
4789                     
4790                     {
4791                         tag: 'label',
4792                         'for' :  id,
4793                         cls : 'control-label col-sm-' + this.labelWidth,
4794                         html : this.fieldLabel
4795                         
4796                     },
4797                     {
4798                         cls : "col-sm-" + (12 - this.labelWidth), 
4799                         cn: [
4800                             inputblock
4801                         ]
4802                     }
4803                     
4804                 ];
4805         } else if ( this.fieldLabel.length) {
4806                 Roo.log(" label");
4807                  cfg.cn = [
4808                    
4809                     {
4810                         tag: 'label',
4811                         //cls : 'input-group-addon',
4812                         html : this.fieldLabel
4813                         
4814                     },
4815                     
4816                     inputblock
4817                     
4818                 ];
4819
4820         } else {
4821             
4822                 Roo.log(" no label && no align");
4823                 cfg.cn = [
4824                     
4825                         inputblock
4826                     
4827                 ];
4828                 
4829                 
4830         };
4831         Roo.log('input-parentType: ' + this.parentType);
4832         
4833         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4834            cfg.cls += ' navbar-form';
4835            Roo.log(cfg);
4836         }
4837         
4838         return cfg;
4839         
4840     },
4841     /**
4842      * return the real input element.
4843      */
4844     inputEl: function ()
4845     {
4846         return this.el.select('input.form-control',true).first();
4847     },
4848     setDisabled : function(v)
4849     {
4850         var i  = this.inputEl().dom;
4851         if (!v) {
4852             i.removeAttribute('disabled');
4853             return;
4854             
4855         }
4856         i.setAttribute('disabled','true');
4857     },
4858     initEvents : function()
4859     {
4860         
4861         this.inputEl().on("keydown" , this.fireKey,  this);
4862         this.inputEl().on("focus", this.onFocus,  this);
4863         this.inputEl().on("blur", this.onBlur,  this);
4864         
4865         this.inputEl().relayEvent('keyup', this);
4866
4867         // reference to original value for reset
4868         this.originalValue = this.getValue();
4869         //Roo.form.TextField.superclass.initEvents.call(this);
4870         if(this.validationEvent == 'keyup'){
4871             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4872             this.inputEl().on('keyup', this.filterValidation, this);
4873         }
4874         else if(this.validationEvent !== false){
4875             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4876         }
4877         
4878         if(this.selectOnFocus){
4879             this.on("focus", this.preFocus, this);
4880             
4881         }
4882         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4883             this.inputEl().on("keypress", this.filterKeys, this);
4884         }
4885        /* if(this.grow){
4886             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4887             this.el.on("click", this.autoSize,  this);
4888         }
4889         */
4890         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4891             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4892         }
4893         
4894     },
4895     filterValidation : function(e){
4896         if(!e.isNavKeyPress()){
4897             this.validationTask.delay(this.validationDelay);
4898         }
4899     },
4900      /**
4901      * Validates the field value
4902      * @return {Boolean} True if the value is valid, else false
4903      */
4904     validate : function(){
4905         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4906         if(this.disabled || this.validateValue(this.getRawValue())){
4907             this.clearInvalid();
4908             return true;
4909         }
4910         return false;
4911     },
4912     
4913     
4914     /**
4915      * Validates a value according to the field's validation rules and marks the field as invalid
4916      * if the validation fails
4917      * @param {Mixed} value The value to validate
4918      * @return {Boolean} True if the value is valid, else false
4919      */
4920     validateValue : function(value){
4921         if(value.length < 1)  { // if it's blank
4922              if(this.allowBlank){
4923                 this.clearInvalid();
4924                 return true;
4925              }else{
4926                 this.markInvalid(this.blankText);
4927                 return false;
4928              }
4929         }
4930         if(value.length < this.minLength){
4931             this.markInvalid(String.format(this.minLengthText, this.minLength));
4932             return false;
4933         }
4934         if(value.length > this.maxLength){
4935             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4936             return false;
4937         }
4938         if(this.vtype){
4939             var vt = Roo.form.VTypes;
4940             if(!vt[this.vtype](value, this)){
4941                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4942                 return false;
4943             }
4944         }
4945         if(typeof this.validator == "function"){
4946             var msg = this.validator(value);
4947             if(msg !== true){
4948                 this.markInvalid(msg);
4949                 return false;
4950             }
4951         }
4952         if(this.regex && !this.regex.test(value)){
4953             this.markInvalid(this.regexText);
4954             return false;
4955         }
4956         return true;
4957     },
4958
4959     
4960     
4961      // private
4962     fireKey : function(e){
4963         //Roo.log('field ' + e.getKey());
4964         if(e.isNavKeyPress()){
4965             this.fireEvent("specialkey", this, e);
4966         }
4967     },
4968     focus : function (selectText){
4969         if(this.rendered){
4970             this.inputEl().focus();
4971             if(selectText === true){
4972                 this.inputEl().dom.select();
4973             }
4974         }
4975         return this;
4976     } ,
4977     
4978     onFocus : function(){
4979         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4980            // this.el.addClass(this.focusClass);
4981         }
4982         if(!this.hasFocus){
4983             this.hasFocus = true;
4984             this.startValue = this.getValue();
4985             this.fireEvent("focus", this);
4986         }
4987     },
4988     
4989     beforeBlur : Roo.emptyFn,
4990
4991     
4992     // private
4993     onBlur : function(){
4994         this.beforeBlur();
4995         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4996             //this.el.removeClass(this.focusClass);
4997         }
4998         this.hasFocus = false;
4999         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5000             this.validate();
5001         }
5002         var v = this.getValue();
5003         if(String(v) !== String(this.startValue)){
5004             this.fireEvent('change', this, v, this.startValue);
5005         }
5006         this.fireEvent("blur", this);
5007     },
5008     
5009     /**
5010      * Resets the current field value to the originally loaded value and clears any validation messages
5011      */
5012     reset : function(){
5013         this.setValue(this.originalValue);
5014         this.clearInvalid();
5015     },
5016      /**
5017      * Returns the name of the field
5018      * @return {Mixed} name The name field
5019      */
5020     getName: function(){
5021         return this.name;
5022     },
5023      /**
5024      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
5025      * @return {Mixed} value The field value
5026      */
5027     getValue : function(){
5028         return this.inputEl().getValue();
5029     },
5030     /**
5031      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
5032      * @return {Mixed} value The field value
5033      */
5034     getRawValue : function(){
5035         var v = this.inputEl().getValue();
5036         
5037         return v;
5038     },
5039     
5040     /**
5041      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
5042      * @param {Mixed} value The value to set
5043      */
5044     setRawValue : function(v){
5045         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5046     },
5047     
5048     selectText : function(start, end){
5049         var v = this.getRawValue();
5050         if(v.length > 0){
5051             start = start === undefined ? 0 : start;
5052             end = end === undefined ? v.length : end;
5053             var d = this.inputEl().dom;
5054             if(d.setSelectionRange){
5055                 d.setSelectionRange(start, end);
5056             }else if(d.createTextRange){
5057                 var range = d.createTextRange();
5058                 range.moveStart("character", start);
5059                 range.moveEnd("character", v.length-end);
5060                 range.select();
5061             }
5062         }
5063     },
5064     
5065     /**
5066      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
5067      * @param {Mixed} value The value to set
5068      */
5069     setValue : function(v){
5070         this.value = v;
5071         if(this.rendered){
5072             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5073             this.validate();
5074         }
5075     },
5076     
5077     /*
5078     processValue : function(value){
5079         if(this.stripCharsRe){
5080             var newValue = value.replace(this.stripCharsRe, '');
5081             if(newValue !== value){
5082                 this.setRawValue(newValue);
5083                 return newValue;
5084             }
5085         }
5086         return value;
5087     },
5088   */
5089     preFocus : function(){
5090         
5091         if(this.selectOnFocus){
5092             this.inputEl().dom.select();
5093         }
5094     },
5095     filterKeys : function(e){
5096         var k = e.getKey();
5097         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5098             return;
5099         }
5100         var c = e.getCharCode(), cc = String.fromCharCode(c);
5101         if(Roo.isIE && (e.isSpecialKey() || !cc)){
5102             return;
5103         }
5104         if(!this.maskRe.test(cc)){
5105             e.stopEvent();
5106         }
5107     },
5108      /**
5109      * Clear any invalid styles/messages for this field
5110      */
5111     clearInvalid : function(){
5112         
5113         if(!this.el || this.preventMark){ // not rendered
5114             return;
5115         }
5116         this.el.removeClass(this.invalidClass);
5117         /*
5118         switch(this.msgTarget){
5119             case 'qtip':
5120                 this.el.dom.qtip = '';
5121                 break;
5122             case 'title':
5123                 this.el.dom.title = '';
5124                 break;
5125             case 'under':
5126                 if(this.errorEl){
5127                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5128                 }
5129                 break;
5130             case 'side':
5131                 if(this.errorIcon){
5132                     this.errorIcon.dom.qtip = '';
5133                     this.errorIcon.hide();
5134                     this.un('resize', this.alignErrorIcon, this);
5135                 }
5136                 break;
5137             default:
5138                 var t = Roo.getDom(this.msgTarget);
5139                 t.innerHTML = '';
5140                 t.style.display = 'none';
5141                 break;
5142         }
5143         */
5144         this.fireEvent('valid', this);
5145     },
5146      /**
5147      * Mark this field as invalid
5148      * @param {String} msg The validation message
5149      */
5150     markInvalid : function(msg){
5151         if(!this.el  || this.preventMark){ // not rendered
5152             return;
5153         }
5154         this.el.addClass(this.invalidClass);
5155         /*
5156         msg = msg || this.invalidText;
5157         switch(this.msgTarget){
5158             case 'qtip':
5159                 this.el.dom.qtip = msg;
5160                 this.el.dom.qclass = 'x-form-invalid-tip';
5161                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5162                     Roo.QuickTips.enable();
5163                 }
5164                 break;
5165             case 'title':
5166                 this.el.dom.title = msg;
5167                 break;
5168             case 'under':
5169                 if(!this.errorEl){
5170                     var elp = this.el.findParent('.x-form-element', 5, true);
5171                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5172                     this.errorEl.setWidth(elp.getWidth(true)-20);
5173                 }
5174                 this.errorEl.update(msg);
5175                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5176                 break;
5177             case 'side':
5178                 if(!this.errorIcon){
5179                     var elp = this.el.findParent('.x-form-element', 5, true);
5180                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5181                 }
5182                 this.alignErrorIcon();
5183                 this.errorIcon.dom.qtip = msg;
5184                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5185                 this.errorIcon.show();
5186                 this.on('resize', this.alignErrorIcon, this);
5187                 break;
5188             default:
5189                 var t = Roo.getDom(this.msgTarget);
5190                 t.innerHTML = msg;
5191                 t.style.display = this.msgDisplay;
5192                 break;
5193         }
5194         */
5195         this.fireEvent('invalid', this, msg);
5196     },
5197     // private
5198     SafariOnKeyDown : function(event)
5199     {
5200         // this is a workaround for a password hang bug on chrome/ webkit.
5201         
5202         var isSelectAll = false;
5203         
5204         if(this.inputEl().dom.selectionEnd > 0){
5205             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5206         }
5207         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5208             event.preventDefault();
5209             this.setValue('');
5210             return;
5211         }
5212         
5213         if(isSelectAll){ // backspace and delete key
5214             
5215             event.preventDefault();
5216             // this is very hacky as keydown always get's upper case.
5217             //
5218             var cc = String.fromCharCode(event.getCharCode());
5219             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5220             
5221         }
5222     },
5223     adjustWidth : function(tag, w){
5224         tag = tag.toLowerCase();
5225         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5226             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5227                 if(tag == 'input'){
5228                     return w + 2;
5229                 }
5230                 if(tag == 'textarea'){
5231                     return w-2;
5232                 }
5233             }else if(Roo.isOpera){
5234                 if(tag == 'input'){
5235                     return w + 2;
5236                 }
5237                 if(tag == 'textarea'){
5238                     return w-2;
5239                 }
5240             }
5241         }
5242         return w;
5243     }
5244     
5245 });
5246
5247  
5248 /*
5249  * - LGPL
5250  *
5251  * Input
5252  * 
5253  */
5254
5255 /**
5256  * @class Roo.bootstrap.TextArea
5257  * @extends Roo.bootstrap.Input
5258  * Bootstrap TextArea class
5259  * @cfg {Number} cols Specifies the visible width of a text area
5260  * @cfg {Number} rows Specifies the visible number of lines in a text area
5261  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5262  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5263  * @cfg {string} html text
5264  * 
5265  * @constructor
5266  * Create a new TextArea
5267  * @param {Object} config The config object
5268  */
5269
5270 Roo.bootstrap.TextArea = function(config){
5271     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5272    
5273 };
5274
5275 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5276      
5277     cols : false,
5278     rows : 5,
5279     readOnly : false,
5280     warp : 'soft',
5281     resize : false,
5282     value: false,
5283     html: false,
5284     
5285     getAutoCreate : function(){
5286         
5287         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5288         
5289         var id = Roo.id();
5290         
5291         var cfg = {};
5292         
5293         var input =  {
5294             tag: 'textarea',
5295             id : id,
5296             warp : this.warp,
5297             rows : this.rows,
5298             value : this.value || '',
5299             html: this.html || '',
5300             cls : 'form-control',
5301             placeholder : this.placeholder || '' 
5302             
5303         };
5304         
5305         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5306             input.maxLength = this.maxLength;
5307         }
5308         
5309         if(this.resize){
5310             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5311         }
5312         
5313         if(this.cols){
5314             input.cols = this.cols;
5315         }
5316         
5317         if (this.readOnly) {
5318             input.readonly = true;
5319         }
5320         
5321         if (this.name) {
5322             input.name = this.name;
5323         }
5324         
5325         if (this.size) {
5326             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5327         }
5328         
5329         var settings=this;
5330         ['xs','sm','md','lg'].map(function(size){
5331             if (settings[size]) {
5332                 cfg.cls += ' col-' + size + '-' + settings[size];
5333             }
5334         });
5335         
5336         var inputblock = input;
5337         
5338         if (this.before || this.after) {
5339             
5340             inputblock = {
5341                 cls : 'input-group',
5342                 cn :  [] 
5343             };
5344             if (this.before) {
5345                 inputblock.cn.push({
5346                     tag :'span',
5347                     cls : 'input-group-addon',
5348                     html : this.before
5349                 });
5350             }
5351             inputblock.cn.push(input);
5352             if (this.after) {
5353                 inputblock.cn.push({
5354                     tag :'span',
5355                     cls : 'input-group-addon',
5356                     html : this.after
5357                 });
5358             }
5359             
5360         }
5361         
5362         if (align ==='left' && this.fieldLabel.length) {
5363                 Roo.log("left and has label");
5364                 cfg.cn = [
5365                     
5366                     {
5367                         tag: 'label',
5368                         'for' :  id,
5369                         cls : 'control-label col-sm-' + this.labelWidth,
5370                         html : this.fieldLabel
5371                         
5372                     },
5373                     {
5374                         cls : "col-sm-" + (12 - this.labelWidth), 
5375                         cn: [
5376                             inputblock
5377                         ]
5378                     }
5379                     
5380                 ];
5381         } else if ( this.fieldLabel.length) {
5382                 Roo.log(" label");
5383                  cfg.cn = [
5384                    
5385                     {
5386                         tag: 'label',
5387                         //cls : 'input-group-addon',
5388                         html : this.fieldLabel
5389                         
5390                     },
5391                     
5392                     inputblock
5393                     
5394                 ];
5395
5396         } else {
5397             
5398                    Roo.log(" no label && no align");
5399                 cfg.cn = [
5400                     
5401                         inputblock
5402                     
5403                 ];
5404                 
5405                 
5406         }
5407         
5408         if (this.disabled) {
5409             input.disabled=true;
5410         }
5411         
5412         return cfg;
5413         
5414     },
5415     /**
5416      * return the real textarea element.
5417      */
5418     inputEl: function ()
5419     {
5420         return this.el.select('textarea.form-control',true).first();
5421     }
5422 });
5423
5424  
5425 /*
5426  * - LGPL
5427  *
5428  * trigger field - base class for combo..
5429  * 
5430  */
5431  
5432 /**
5433  * @class Roo.bootstrap.TriggerField
5434  * @extends Roo.bootstrap.Input
5435  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5436  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5437  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5438  * for which you can provide a custom implementation.  For example:
5439  * <pre><code>
5440 var trigger = new Roo.bootstrap.TriggerField();
5441 trigger.onTriggerClick = myTriggerFn;
5442 trigger.applyTo('my-field');
5443 </code></pre>
5444  *
5445  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5446  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5447  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5448  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5449  * @constructor
5450  * Create a new TriggerField.
5451  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5452  * to the base TextField)
5453  */
5454 Roo.bootstrap.TriggerField = function(config){
5455     this.mimicing = false;
5456     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5457 };
5458
5459 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5460     /**
5461      * @cfg {String} triggerClass A CSS class to apply to the trigger
5462      */
5463      /**
5464      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5465      */
5466     hideTrigger:false,
5467
5468     /** @cfg {Boolean} grow @hide */
5469     /** @cfg {Number} growMin @hide */
5470     /** @cfg {Number} growMax @hide */
5471
5472     /**
5473      * @hide 
5474      * @method
5475      */
5476     autoSize: Roo.emptyFn,
5477     // private
5478     monitorTab : true,
5479     // private
5480     deferHeight : true,
5481
5482     
5483     actionMode : 'wrap',
5484     
5485     
5486     
5487     getAutoCreate : function(){
5488        
5489         var parent = this.parent();
5490         
5491         var align = this.parentLabelAlign();
5492         
5493         var id = Roo.id();
5494         
5495         var cfg = {
5496             cls: 'form-group' //input-group
5497         };
5498         
5499         
5500         var input =  {
5501             tag: 'input',
5502             id : id,
5503             type : this.inputType,
5504             cls : 'form-control',
5505             autocomplete: 'off',
5506             placeholder : this.placeholder || '' 
5507             
5508         };
5509         if (this.name) {
5510             input.name = this.name;
5511         }
5512         if (this.size) {
5513             input.cls += ' input-' + this.size;
5514         }
5515         
5516         if (this.disabled) {
5517             input.disabled=true;
5518         }
5519         
5520         var inputblock = input;
5521         
5522         if (this.before || this.after) {
5523             
5524             inputblock = {
5525                 cls : 'input-group',
5526                 cn :  [] 
5527             };
5528             if (this.before) {
5529                 inputblock.cn.push({
5530                     tag :'span',
5531                     cls : 'input-group-addon',
5532                     html : this.before
5533                 });
5534             }
5535             inputblock.cn.push(input);
5536             if (this.after) {
5537                 inputblock.cn.push({
5538                     tag :'span',
5539                     cls : 'input-group-addon',
5540                     html : this.after
5541                 });
5542             }
5543             
5544         };
5545         
5546         var box = {
5547             tag: 'div',
5548             cn: [
5549                 {
5550                     tag: 'input',
5551                     type : 'hidden',
5552                     cls: 'form-hidden-field'
5553                 },
5554                 inputblock
5555             ]
5556             
5557         };
5558         
5559         if(this.multiple){
5560             Roo.log('multiple');
5561             
5562             box = {
5563                 tag: 'div',
5564                 cn: [
5565                     {
5566                         tag: 'input',
5567                         type : 'hidden',
5568                         cls: 'form-hidden-field'
5569                     },
5570                     {
5571                         tag: 'ul',
5572                         cls: 'select2-choices',
5573                         cn:[
5574                             {
5575                                 tag: 'li',
5576                                 cls: 'select2-search-field',
5577                                 cn: [
5578
5579                                     inputblock
5580                                 ]
5581                             }
5582                         ]
5583                     }
5584                 ]
5585             }
5586         };
5587         
5588         var combobox = {
5589             cls: 'select2-container input-group',
5590             cn: [
5591                 box,
5592                 {
5593                     tag: 'ul',
5594                     cls: 'typeahead typeahead-long dropdown-menu',
5595                     style: 'display:none'
5596                 }
5597             ]
5598         };
5599         
5600         if(!this.multiple){
5601             combobox.cn.push({
5602                 tag :'span',
5603                 cls : 'input-group-addon btn dropdown-toggle',
5604                 cn : [
5605                     {
5606                         tag: 'span',
5607                         cls: 'caret'
5608                     },
5609                     {
5610                         tag: 'span',
5611                         cls: 'combobox-clear',
5612                         cn  : [
5613                             {
5614                                 tag : 'i',
5615                                 cls: 'icon-remove'
5616                             }
5617                         ]
5618                     }
5619                 ]
5620
5621             })
5622         }
5623         
5624         if(this.multiple){
5625             combobox.cls += ' select2-container-multi';
5626         }
5627         
5628         if (align ==='left' && this.fieldLabel.length) {
5629             
5630                 Roo.log("left and has label");
5631                 cfg.cn = [
5632                     
5633                     {
5634                         tag: 'label',
5635                         'for' :  id,
5636                         cls : 'control-label col-sm-' + this.labelWidth,
5637                         html : this.fieldLabel
5638                         
5639                     },
5640                     {
5641                         cls : "col-sm-" + (12 - this.labelWidth), 
5642                         cn: [
5643                             combobox
5644                         ]
5645                     }
5646                     
5647                 ];
5648         } else if ( this.fieldLabel.length) {
5649                 Roo.log(" label");
5650                  cfg.cn = [
5651                    
5652                     {
5653                         tag: 'label',
5654                         //cls : 'input-group-addon',
5655                         html : this.fieldLabel
5656                         
5657                     },
5658                     
5659                     combobox
5660                     
5661                 ];
5662
5663         } else {
5664             
5665                 Roo.log(" no label && no align");
5666                 cfg = combobox
5667                      
5668                 
5669         }
5670          
5671         var settings=this;
5672         ['xs','sm','md','lg'].map(function(size){
5673             if (settings[size]) {
5674                 cfg.cls += ' col-' + size + '-' + settings[size];
5675             }
5676         });
5677         
5678         return cfg;
5679         
5680     },
5681     
5682     
5683     
5684     // private
5685     onResize : function(w, h){
5686 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5687 //        if(typeof w == 'number'){
5688 //            var x = w - this.trigger.getWidth();
5689 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5690 //            this.trigger.setStyle('left', x+'px');
5691 //        }
5692     },
5693
5694     // private
5695     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5696
5697     // private
5698     getResizeEl : function(){
5699         return this.inputEl();
5700     },
5701
5702     // private
5703     getPositionEl : function(){
5704         return this.inputEl();
5705     },
5706
5707     // private
5708     alignErrorIcon : function(){
5709         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5710     },
5711
5712     // private
5713     initEvents : function(){
5714         
5715         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5716         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5717         if(!this.multiple){
5718             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5719             if(this.hideTrigger){
5720                 this.trigger.setDisplayed(false);
5721             }
5722             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5723         }
5724         
5725         if(this.multiple){
5726             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5727         }
5728         
5729         //this.trigger.addClassOnOver('x-form-trigger-over');
5730         //this.trigger.addClassOnClick('x-form-trigger-click');
5731         
5732         //if(!this.width){
5733         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5734         //}
5735     },
5736
5737     // private
5738     initTrigger : function(){
5739        
5740     },
5741
5742     // private
5743     onDestroy : function(){
5744         if(this.trigger){
5745             this.trigger.removeAllListeners();
5746           //  this.trigger.remove();
5747         }
5748         //if(this.wrap){
5749         //    this.wrap.remove();
5750         //}
5751         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5752     },
5753
5754     // private
5755     onFocus : function(){
5756         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5757         /*
5758         if(!this.mimicing){
5759             this.wrap.addClass('x-trigger-wrap-focus');
5760             this.mimicing = true;
5761             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5762             if(this.monitorTab){
5763                 this.el.on("keydown", this.checkTab, this);
5764             }
5765         }
5766         */
5767     },
5768
5769     // private
5770     checkTab : function(e){
5771         if(e.getKey() == e.TAB){
5772             this.triggerBlur();
5773         }
5774     },
5775
5776     // private
5777     onBlur : function(){
5778         // do nothing
5779     },
5780
5781     // private
5782     mimicBlur : function(e, t){
5783         /*
5784         if(!this.wrap.contains(t) && this.validateBlur()){
5785             this.triggerBlur();
5786         }
5787         */
5788     },
5789
5790     // private
5791     triggerBlur : function(){
5792         this.mimicing = false;
5793         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5794         if(this.monitorTab){
5795             this.el.un("keydown", this.checkTab, this);
5796         }
5797         //this.wrap.removeClass('x-trigger-wrap-focus');
5798         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5799     },
5800
5801     // private
5802     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5803     validateBlur : function(e, t){
5804         return true;
5805     },
5806
5807     // private
5808     onDisable : function(){
5809         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5810         //if(this.wrap){
5811         //    this.wrap.addClass('x-item-disabled');
5812         //}
5813     },
5814
5815     // private
5816     onEnable : function(){
5817         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5818         //if(this.wrap){
5819         //    this.el.removeClass('x-item-disabled');
5820         //}
5821     },
5822
5823     // private
5824     onShow : function(){
5825         var ae = this.getActionEl();
5826         
5827         if(ae){
5828             ae.dom.style.display = '';
5829             ae.dom.style.visibility = 'visible';
5830         }
5831     },
5832
5833     // private
5834     
5835     onHide : function(){
5836         var ae = this.getActionEl();
5837         ae.dom.style.display = 'none';
5838     },
5839
5840     /**
5841      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5842      * by an implementing function.
5843      * @method
5844      * @param {EventObject} e
5845      */
5846     onTriggerClick : Roo.emptyFn
5847 });
5848  /*
5849  * Based on:
5850  * Ext JS Library 1.1.1
5851  * Copyright(c) 2006-2007, Ext JS, LLC.
5852  *
5853  * Originally Released Under LGPL - original licence link has changed is not relivant.
5854  *
5855  * Fork - LGPL
5856  * <script type="text/javascript">
5857  */
5858
5859
5860 /**
5861  * @class Roo.data.SortTypes
5862  * @singleton
5863  * Defines the default sorting (casting?) comparison functions used when sorting data.
5864  */
5865 Roo.data.SortTypes = {
5866     /**
5867      * Default sort that does nothing
5868      * @param {Mixed} s The value being converted
5869      * @return {Mixed} The comparison value
5870      */
5871     none : function(s){
5872         return s;
5873     },
5874     
5875     /**
5876      * The regular expression used to strip tags
5877      * @type {RegExp}
5878      * @property
5879      */
5880     stripTagsRE : /<\/?[^>]+>/gi,
5881     
5882     /**
5883      * Strips all HTML tags to sort on text only
5884      * @param {Mixed} s The value being converted
5885      * @return {String} The comparison value
5886      */
5887     asText : function(s){
5888         return String(s).replace(this.stripTagsRE, "");
5889     },
5890     
5891     /**
5892      * Strips all HTML tags to sort on text only - Case insensitive
5893      * @param {Mixed} s The value being converted
5894      * @return {String} The comparison value
5895      */
5896     asUCText : function(s){
5897         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5898     },
5899     
5900     /**
5901      * Case insensitive string
5902      * @param {Mixed} s The value being converted
5903      * @return {String} The comparison value
5904      */
5905     asUCString : function(s) {
5906         return String(s).toUpperCase();
5907     },
5908     
5909     /**
5910      * Date sorting
5911      * @param {Mixed} s The value being converted
5912      * @return {Number} The comparison value
5913      */
5914     asDate : function(s) {
5915         if(!s){
5916             return 0;
5917         }
5918         if(s instanceof Date){
5919             return s.getTime();
5920         }
5921         return Date.parse(String(s));
5922     },
5923     
5924     /**
5925      * Float sorting
5926      * @param {Mixed} s The value being converted
5927      * @return {Float} The comparison value
5928      */
5929     asFloat : function(s) {
5930         var val = parseFloat(String(s).replace(/,/g, ""));
5931         if(isNaN(val)) val = 0;
5932         return val;
5933     },
5934     
5935     /**
5936      * Integer sorting
5937      * @param {Mixed} s The value being converted
5938      * @return {Number} The comparison value
5939      */
5940     asInt : function(s) {
5941         var val = parseInt(String(s).replace(/,/g, ""));
5942         if(isNaN(val)) val = 0;
5943         return val;
5944     }
5945 };/*
5946  * Based on:
5947  * Ext JS Library 1.1.1
5948  * Copyright(c) 2006-2007, Ext JS, LLC.
5949  *
5950  * Originally Released Under LGPL - original licence link has changed is not relivant.
5951  *
5952  * Fork - LGPL
5953  * <script type="text/javascript">
5954  */
5955
5956 /**
5957 * @class Roo.data.Record
5958  * Instances of this class encapsulate both record <em>definition</em> information, and record
5959  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5960  * to access Records cached in an {@link Roo.data.Store} object.<br>
5961  * <p>
5962  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5963  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5964  * objects.<br>
5965  * <p>
5966  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5967  * @constructor
5968  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5969  * {@link #create}. The parameters are the same.
5970  * @param {Array} data An associative Array of data values keyed by the field name.
5971  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5972  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5973  * not specified an integer id is generated.
5974  */
5975 Roo.data.Record = function(data, id){
5976     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5977     this.data = data;
5978 };
5979
5980 /**
5981  * Generate a constructor for a specific record layout.
5982  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5983  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5984  * Each field definition object may contain the following properties: <ul>
5985  * <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,
5986  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5987  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5988  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5989  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5990  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5991  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5992  * this may be omitted.</p></li>
5993  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5994  * <ul><li>auto (Default, implies no conversion)</li>
5995  * <li>string</li>
5996  * <li>int</li>
5997  * <li>float</li>
5998  * <li>boolean</li>
5999  * <li>date</li></ul></p></li>
6000  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6001  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6002  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6003  * by the Reader into an object that will be stored in the Record. It is passed the
6004  * following parameters:<ul>
6005  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6006  * </ul></p></li>
6007  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6008  * </ul>
6009  * <br>usage:<br><pre><code>
6010 var TopicRecord = Roo.data.Record.create(
6011     {name: 'title', mapping: 'topic_title'},
6012     {name: 'author', mapping: 'username'},
6013     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6014     {name: 'lastPost', mapping: 'post_time', type: 'date'},
6015     {name: 'lastPoster', mapping: 'user2'},
6016     {name: 'excerpt', mapping: 'post_text'}
6017 );
6018
6019 var myNewRecord = new TopicRecord({
6020     title: 'Do my job please',
6021     author: 'noobie',
6022     totalPosts: 1,
6023     lastPost: new Date(),
6024     lastPoster: 'Animal',
6025     excerpt: 'No way dude!'
6026 });
6027 myStore.add(myNewRecord);
6028 </code></pre>
6029  * @method create
6030  * @static
6031  */
6032 Roo.data.Record.create = function(o){
6033     var f = function(){
6034         f.superclass.constructor.apply(this, arguments);
6035     };
6036     Roo.extend(f, Roo.data.Record);
6037     var p = f.prototype;
6038     p.fields = new Roo.util.MixedCollection(false, function(field){
6039         return field.name;
6040     });
6041     for(var i = 0, len = o.length; i < len; i++){
6042         p.fields.add(new Roo.data.Field(o[i]));
6043     }
6044     f.getField = function(name){
6045         return p.fields.get(name);  
6046     };
6047     return f;
6048 };
6049
6050 Roo.data.Record.AUTO_ID = 1000;
6051 Roo.data.Record.EDIT = 'edit';
6052 Roo.data.Record.REJECT = 'reject';
6053 Roo.data.Record.COMMIT = 'commit';
6054
6055 Roo.data.Record.prototype = {
6056     /**
6057      * Readonly flag - true if this record has been modified.
6058      * @type Boolean
6059      */
6060     dirty : false,
6061     editing : false,
6062     error: null,
6063     modified: null,
6064
6065     // private
6066     join : function(store){
6067         this.store = store;
6068     },
6069
6070     /**
6071      * Set the named field to the specified value.
6072      * @param {String} name The name of the field to set.
6073      * @param {Object} value The value to set the field to.
6074      */
6075     set : function(name, value){
6076         if(this.data[name] == value){
6077             return;
6078         }
6079         this.dirty = true;
6080         if(!this.modified){
6081             this.modified = {};
6082         }
6083         if(typeof this.modified[name] == 'undefined'){
6084             this.modified[name] = this.data[name];
6085         }
6086         this.data[name] = value;
6087         if(!this.editing && this.store){
6088             this.store.afterEdit(this);
6089         }       
6090     },
6091
6092     /**
6093      * Get the value of the named field.
6094      * @param {String} name The name of the field to get the value of.
6095      * @return {Object} The value of the field.
6096      */
6097     get : function(name){
6098         return this.data[name]; 
6099     },
6100
6101     // private
6102     beginEdit : function(){
6103         this.editing = true;
6104         this.modified = {}; 
6105     },
6106
6107     // private
6108     cancelEdit : function(){
6109         this.editing = false;
6110         delete this.modified;
6111     },
6112
6113     // private
6114     endEdit : function(){
6115         this.editing = false;
6116         if(this.dirty && this.store){
6117             this.store.afterEdit(this);
6118         }
6119     },
6120
6121     /**
6122      * Usually called by the {@link Roo.data.Store} which owns the Record.
6123      * Rejects all changes made to the Record since either creation, or the last commit operation.
6124      * Modified fields are reverted to their original values.
6125      * <p>
6126      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6127      * of reject operations.
6128      */
6129     reject : function(){
6130         var m = this.modified;
6131         for(var n in m){
6132             if(typeof m[n] != "function"){
6133                 this.data[n] = m[n];
6134             }
6135         }
6136         this.dirty = false;
6137         delete this.modified;
6138         this.editing = false;
6139         if(this.store){
6140             this.store.afterReject(this);
6141         }
6142     },
6143
6144     /**
6145      * Usually called by the {@link Roo.data.Store} which owns the Record.
6146      * Commits all changes made to the Record since either creation, or the last commit operation.
6147      * <p>
6148      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6149      * of commit operations.
6150      */
6151     commit : function(){
6152         this.dirty = false;
6153         delete this.modified;
6154         this.editing = false;
6155         if(this.store){
6156             this.store.afterCommit(this);
6157         }
6158     },
6159
6160     // private
6161     hasError : function(){
6162         return this.error != null;
6163     },
6164
6165     // private
6166     clearError : function(){
6167         this.error = null;
6168     },
6169
6170     /**
6171      * Creates a copy of this record.
6172      * @param {String} id (optional) A new record id if you don't want to use this record's id
6173      * @return {Record}
6174      */
6175     copy : function(newId) {
6176         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6177     }
6178 };/*
6179  * Based on:
6180  * Ext JS Library 1.1.1
6181  * Copyright(c) 2006-2007, Ext JS, LLC.
6182  *
6183  * Originally Released Under LGPL - original licence link has changed is not relivant.
6184  *
6185  * Fork - LGPL
6186  * <script type="text/javascript">
6187  */
6188
6189
6190
6191 /**
6192  * @class Roo.data.Store
6193  * @extends Roo.util.Observable
6194  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6195  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6196  * <p>
6197  * 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
6198  * has no knowledge of the format of the data returned by the Proxy.<br>
6199  * <p>
6200  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6201  * instances from the data object. These records are cached and made available through accessor functions.
6202  * @constructor
6203  * Creates a new Store.
6204  * @param {Object} config A config object containing the objects needed for the Store to access data,
6205  * and read the data into Records.
6206  */
6207 Roo.data.Store = function(config){
6208     this.data = new Roo.util.MixedCollection(false);
6209     this.data.getKey = function(o){
6210         return o.id;
6211     };
6212     this.baseParams = {};
6213     // private
6214     this.paramNames = {
6215         "start" : "start",
6216         "limit" : "limit",
6217         "sort" : "sort",
6218         "dir" : "dir",
6219         "multisort" : "_multisort"
6220     };
6221
6222     if(config && config.data){
6223         this.inlineData = config.data;
6224         delete config.data;
6225     }
6226
6227     Roo.apply(this, config);
6228     
6229     if(this.reader){ // reader passed
6230         this.reader = Roo.factory(this.reader, Roo.data);
6231         this.reader.xmodule = this.xmodule || false;
6232         if(!this.recordType){
6233             this.recordType = this.reader.recordType;
6234         }
6235         if(this.reader.onMetaChange){
6236             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6237         }
6238     }
6239
6240     if(this.recordType){
6241         this.fields = this.recordType.prototype.fields;
6242     }
6243     this.modified = [];
6244
6245     this.addEvents({
6246         /**
6247          * @event datachanged
6248          * Fires when the data cache has changed, and a widget which is using this Store
6249          * as a Record cache should refresh its view.
6250          * @param {Store} this
6251          */
6252         datachanged : true,
6253         /**
6254          * @event metachange
6255          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6256          * @param {Store} this
6257          * @param {Object} meta The JSON metadata
6258          */
6259         metachange : true,
6260         /**
6261          * @event add
6262          * Fires when Records have been added to the Store
6263          * @param {Store} this
6264          * @param {Roo.data.Record[]} records The array of Records added
6265          * @param {Number} index The index at which the record(s) were added
6266          */
6267         add : true,
6268         /**
6269          * @event remove
6270          * Fires when a Record has been removed from the Store
6271          * @param {Store} this
6272          * @param {Roo.data.Record} record The Record that was removed
6273          * @param {Number} index The index at which the record was removed
6274          */
6275         remove : true,
6276         /**
6277          * @event update
6278          * Fires when a Record has been updated
6279          * @param {Store} this
6280          * @param {Roo.data.Record} record The Record that was updated
6281          * @param {String} operation The update operation being performed.  Value may be one of:
6282          * <pre><code>
6283  Roo.data.Record.EDIT
6284  Roo.data.Record.REJECT
6285  Roo.data.Record.COMMIT
6286          * </code></pre>
6287          */
6288         update : true,
6289         /**
6290          * @event clear
6291          * Fires when the data cache has been cleared.
6292          * @param {Store} this
6293          */
6294         clear : true,
6295         /**
6296          * @event beforeload
6297          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6298          * the load action will be canceled.
6299          * @param {Store} this
6300          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6301          */
6302         beforeload : true,
6303         /**
6304          * @event beforeloadadd
6305          * Fires after a new set of Records has been loaded.
6306          * @param {Store} this
6307          * @param {Roo.data.Record[]} records The Records that were loaded
6308          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6309          */
6310         beforeloadadd : true,
6311         /**
6312          * @event load
6313          * Fires after a new set of Records has been loaded, before they are added to the store.
6314          * @param {Store} this
6315          * @param {Roo.data.Record[]} records The Records that were loaded
6316          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6317          * @params {Object} return from reader
6318          */
6319         load : true,
6320         /**
6321          * @event loadexception
6322          * Fires if an exception occurs in the Proxy during loading.
6323          * Called with the signature of the Proxy's "loadexception" event.
6324          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6325          * 
6326          * @param {Proxy} 
6327          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6328          * @param {Object} load options 
6329          * @param {Object} jsonData from your request (normally this contains the Exception)
6330          */
6331         loadexception : true
6332     });
6333     
6334     if(this.proxy){
6335         this.proxy = Roo.factory(this.proxy, Roo.data);
6336         this.proxy.xmodule = this.xmodule || false;
6337         this.relayEvents(this.proxy,  ["loadexception"]);
6338     }
6339     this.sortToggle = {};
6340     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6341
6342     Roo.data.Store.superclass.constructor.call(this);
6343
6344     if(this.inlineData){
6345         this.loadData(this.inlineData);
6346         delete this.inlineData;
6347     }
6348 };
6349
6350 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6351      /**
6352     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6353     * without a remote query - used by combo/forms at present.
6354     */
6355     
6356     /**
6357     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6358     */
6359     /**
6360     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6361     */
6362     /**
6363     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6364     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6365     */
6366     /**
6367     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6368     * on any HTTP request
6369     */
6370     /**
6371     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6372     */
6373     /**
6374     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6375     */
6376     multiSort: false,
6377     /**
6378     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6379     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6380     */
6381     remoteSort : false,
6382
6383     /**
6384     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6385      * loaded or when a record is removed. (defaults to false).
6386     */
6387     pruneModifiedRecords : false,
6388
6389     // private
6390     lastOptions : null,
6391
6392     /**
6393      * Add Records to the Store and fires the add event.
6394      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6395      */
6396     add : function(records){
6397         records = [].concat(records);
6398         for(var i = 0, len = records.length; i < len; i++){
6399             records[i].join(this);
6400         }
6401         var index = this.data.length;
6402         this.data.addAll(records);
6403         this.fireEvent("add", this, records, index);
6404     },
6405
6406     /**
6407      * Remove a Record from the Store and fires the remove event.
6408      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6409      */
6410     remove : function(record){
6411         var index = this.data.indexOf(record);
6412         this.data.removeAt(index);
6413         if(this.pruneModifiedRecords){
6414             this.modified.remove(record);
6415         }
6416         this.fireEvent("remove", this, record, index);
6417     },
6418
6419     /**
6420      * Remove all Records from the Store and fires the clear event.
6421      */
6422     removeAll : function(){
6423         this.data.clear();
6424         if(this.pruneModifiedRecords){
6425             this.modified = [];
6426         }
6427         this.fireEvent("clear", this);
6428     },
6429
6430     /**
6431      * Inserts Records to the Store at the given index and fires the add event.
6432      * @param {Number} index The start index at which to insert the passed Records.
6433      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6434      */
6435     insert : function(index, records){
6436         records = [].concat(records);
6437         for(var i = 0, len = records.length; i < len; i++){
6438             this.data.insert(index, records[i]);
6439             records[i].join(this);
6440         }
6441         this.fireEvent("add", this, records, index);
6442     },
6443
6444     /**
6445      * Get the index within the cache of the passed Record.
6446      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6447      * @return {Number} The index of the passed Record. Returns -1 if not found.
6448      */
6449     indexOf : function(record){
6450         return this.data.indexOf(record);
6451     },
6452
6453     /**
6454      * Get the index within the cache of the Record with the passed id.
6455      * @param {String} id The id of the Record to find.
6456      * @return {Number} The index of the Record. Returns -1 if not found.
6457      */
6458     indexOfId : function(id){
6459         return this.data.indexOfKey(id);
6460     },
6461
6462     /**
6463      * Get the Record with the specified id.
6464      * @param {String} id The id of the Record to find.
6465      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6466      */
6467     getById : function(id){
6468         return this.data.key(id);
6469     },
6470
6471     /**
6472      * Get the Record at the specified index.
6473      * @param {Number} index The index of the Record to find.
6474      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6475      */
6476     getAt : function(index){
6477         return this.data.itemAt(index);
6478     },
6479
6480     /**
6481      * Returns a range of Records between specified indices.
6482      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6483      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6484      * @return {Roo.data.Record[]} An array of Records
6485      */
6486     getRange : function(start, end){
6487         return this.data.getRange(start, end);
6488     },
6489
6490     // private
6491     storeOptions : function(o){
6492         o = Roo.apply({}, o);
6493         delete o.callback;
6494         delete o.scope;
6495         this.lastOptions = o;
6496     },
6497
6498     /**
6499      * Loads the Record cache from the configured Proxy using the configured Reader.
6500      * <p>
6501      * If using remote paging, then the first load call must specify the <em>start</em>
6502      * and <em>limit</em> properties in the options.params property to establish the initial
6503      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6504      * <p>
6505      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6506      * and this call will return before the new data has been loaded. Perform any post-processing
6507      * in a callback function, or in a "load" event handler.</strong>
6508      * <p>
6509      * @param {Object} options An object containing properties which control loading options:<ul>
6510      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6511      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6512      * passed the following arguments:<ul>
6513      * <li>r : Roo.data.Record[]</li>
6514      * <li>options: Options object from the load call</li>
6515      * <li>success: Boolean success indicator</li></ul></li>
6516      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6517      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6518      * </ul>
6519      */
6520     load : function(options){
6521         options = options || {};
6522         if(this.fireEvent("beforeload", this, options) !== false){
6523             this.storeOptions(options);
6524             var p = Roo.apply(options.params || {}, this.baseParams);
6525             // if meta was not loaded from remote source.. try requesting it.
6526             if (!this.reader.metaFromRemote) {
6527                 p._requestMeta = 1;
6528             }
6529             if(this.sortInfo && this.remoteSort){
6530                 var pn = this.paramNames;
6531                 p[pn["sort"]] = this.sortInfo.field;
6532                 p[pn["dir"]] = this.sortInfo.direction;
6533             }
6534             if (this.multiSort) {
6535                 var pn = this.paramNames;
6536                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6537             }
6538             
6539             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6540         }
6541     },
6542
6543     /**
6544      * Reloads the Record cache from the configured Proxy using the configured Reader and
6545      * the options from the last load operation performed.
6546      * @param {Object} options (optional) An object containing properties which may override the options
6547      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6548      * the most recently used options are reused).
6549      */
6550     reload : function(options){
6551         this.load(Roo.applyIf(options||{}, this.lastOptions));
6552     },
6553
6554     // private
6555     // Called as a callback by the Reader during a load operation.
6556     loadRecords : function(o, options, success){
6557         if(!o || success === false){
6558             if(success !== false){
6559                 this.fireEvent("load", this, [], options, o);
6560             }
6561             if(options.callback){
6562                 options.callback.call(options.scope || this, [], options, false);
6563             }
6564             return;
6565         }
6566         // if data returned failure - throw an exception.
6567         if (o.success === false) {
6568             // show a message if no listener is registered.
6569             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6570                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6571             }
6572             // loadmask wil be hooked into this..
6573             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6574             return;
6575         }
6576         var r = o.records, t = o.totalRecords || r.length;
6577         
6578         this.fireEvent("beforeloadadd", this, r, options, o);
6579         
6580         if(!options || options.add !== true){
6581             if(this.pruneModifiedRecords){
6582                 this.modified = [];
6583             }
6584             for(var i = 0, len = r.length; i < len; i++){
6585                 r[i].join(this);
6586             }
6587             if(this.snapshot){
6588                 this.data = this.snapshot;
6589                 delete this.snapshot;
6590             }
6591             this.data.clear();
6592             this.data.addAll(r);
6593             this.totalLength = t;
6594             this.applySort();
6595             this.fireEvent("datachanged", this);
6596         }else{
6597             this.totalLength = Math.max(t, this.data.length+r.length);
6598             this.add(r);
6599         }
6600         this.fireEvent("load", this, r, options, o);
6601         if(options.callback){
6602             options.callback.call(options.scope || this, r, options, true);
6603         }
6604     },
6605
6606
6607     /**
6608      * Loads data from a passed data block. A Reader which understands the format of the data
6609      * must have been configured in the constructor.
6610      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6611      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6612      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6613      */
6614     loadData : function(o, append){
6615         var r = this.reader.readRecords(o);
6616         this.loadRecords(r, {add: append}, true);
6617     },
6618
6619     /**
6620      * Gets the number of cached records.
6621      * <p>
6622      * <em>If using paging, this may not be the total size of the dataset. If the data object
6623      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6624      * the data set size</em>
6625      */
6626     getCount : function(){
6627         return this.data.length || 0;
6628     },
6629
6630     /**
6631      * Gets the total number of records in the dataset as returned by the server.
6632      * <p>
6633      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6634      * the dataset size</em>
6635      */
6636     getTotalCount : function(){
6637         return this.totalLength || 0;
6638     },
6639
6640     /**
6641      * Returns the sort state of the Store as an object with two properties:
6642      * <pre><code>
6643  field {String} The name of the field by which the Records are sorted
6644  direction {String} The sort order, "ASC" or "DESC"
6645      * </code></pre>
6646      */
6647     getSortState : function(){
6648         return this.sortInfo;
6649     },
6650
6651     // private
6652     applySort : function(){
6653         if(this.sortInfo && !this.remoteSort){
6654             var s = this.sortInfo, f = s.field;
6655             var st = this.fields.get(f).sortType;
6656             var fn = function(r1, r2){
6657                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6658                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6659             };
6660             this.data.sort(s.direction, fn);
6661             if(this.snapshot && this.snapshot != this.data){
6662                 this.snapshot.sort(s.direction, fn);
6663             }
6664         }
6665     },
6666
6667     /**
6668      * Sets the default sort column and order to be used by the next load operation.
6669      * @param {String} fieldName The name of the field to sort by.
6670      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6671      */
6672     setDefaultSort : function(field, dir){
6673         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6674     },
6675
6676     /**
6677      * Sort the Records.
6678      * If remote sorting is used, the sort is performed on the server, and the cache is
6679      * reloaded. If local sorting is used, the cache is sorted internally.
6680      * @param {String} fieldName The name of the field to sort by.
6681      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6682      */
6683     sort : function(fieldName, dir){
6684         var f = this.fields.get(fieldName);
6685         if(!dir){
6686             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6687             
6688             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6689                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6690             }else{
6691                 dir = f.sortDir;
6692             }
6693         }
6694         this.sortToggle[f.name] = dir;
6695         this.sortInfo = {field: f.name, direction: dir};
6696         if(!this.remoteSort){
6697             this.applySort();
6698             this.fireEvent("datachanged", this);
6699         }else{
6700             this.load(this.lastOptions);
6701         }
6702     },
6703
6704     /**
6705      * Calls the specified function for each of the Records in the cache.
6706      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6707      * Returning <em>false</em> aborts and exits the iteration.
6708      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6709      */
6710     each : function(fn, scope){
6711         this.data.each(fn, scope);
6712     },
6713
6714     /**
6715      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6716      * (e.g., during paging).
6717      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6718      */
6719     getModifiedRecords : function(){
6720         return this.modified;
6721     },
6722
6723     // private
6724     createFilterFn : function(property, value, anyMatch){
6725         if(!value.exec){ // not a regex
6726             value = String(value);
6727             if(value.length == 0){
6728                 return false;
6729             }
6730             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6731         }
6732         return function(r){
6733             return value.test(r.data[property]);
6734         };
6735     },
6736
6737     /**
6738      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6739      * @param {String} property A field on your records
6740      * @param {Number} start The record index to start at (defaults to 0)
6741      * @param {Number} end The last record index to include (defaults to length - 1)
6742      * @return {Number} The sum
6743      */
6744     sum : function(property, start, end){
6745         var rs = this.data.items, v = 0;
6746         start = start || 0;
6747         end = (end || end === 0) ? end : rs.length-1;
6748
6749         for(var i = start; i <= end; i++){
6750             v += (rs[i].data[property] || 0);
6751         }
6752         return v;
6753     },
6754
6755     /**
6756      * Filter the records by a specified property.
6757      * @param {String} field A field on your records
6758      * @param {String/RegExp} value Either a string that the field
6759      * should start with or a RegExp to test against the field
6760      * @param {Boolean} anyMatch True to match any part not just the beginning
6761      */
6762     filter : function(property, value, anyMatch){
6763         var fn = this.createFilterFn(property, value, anyMatch);
6764         return fn ? this.filterBy(fn) : this.clearFilter();
6765     },
6766
6767     /**
6768      * Filter by a function. The specified function will be called with each
6769      * record in this data source. If the function returns true the record is included,
6770      * otherwise it is filtered.
6771      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6772      * @param {Object} scope (optional) The scope of the function (defaults to this)
6773      */
6774     filterBy : function(fn, scope){
6775         this.snapshot = this.snapshot || this.data;
6776         this.data = this.queryBy(fn, scope||this);
6777         this.fireEvent("datachanged", this);
6778     },
6779
6780     /**
6781      * Query the records by a specified property.
6782      * @param {String} field A field on your records
6783      * @param {String/RegExp} value Either a string that the field
6784      * should start with or a RegExp to test against the field
6785      * @param {Boolean} anyMatch True to match any part not just the beginning
6786      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6787      */
6788     query : function(property, value, anyMatch){
6789         var fn = this.createFilterFn(property, value, anyMatch);
6790         return fn ? this.queryBy(fn) : this.data.clone();
6791     },
6792
6793     /**
6794      * Query by a function. The specified function will be called with each
6795      * record in this data source. If the function returns true the record is included
6796      * in the results.
6797      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6798      * @param {Object} scope (optional) The scope of the function (defaults to this)
6799       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6800      **/
6801     queryBy : function(fn, scope){
6802         var data = this.snapshot || this.data;
6803         return data.filterBy(fn, scope||this);
6804     },
6805
6806     /**
6807      * Collects unique values for a particular dataIndex from this store.
6808      * @param {String} dataIndex The property to collect
6809      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6810      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6811      * @return {Array} An array of the unique values
6812      **/
6813     collect : function(dataIndex, allowNull, bypassFilter){
6814         var d = (bypassFilter === true && this.snapshot) ?
6815                 this.snapshot.items : this.data.items;
6816         var v, sv, r = [], l = {};
6817         for(var i = 0, len = d.length; i < len; i++){
6818             v = d[i].data[dataIndex];
6819             sv = String(v);
6820             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6821                 l[sv] = true;
6822                 r[r.length] = v;
6823             }
6824         }
6825         return r;
6826     },
6827
6828     /**
6829      * Revert to a view of the Record cache with no filtering applied.
6830      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6831      */
6832     clearFilter : function(suppressEvent){
6833         if(this.snapshot && this.snapshot != this.data){
6834             this.data = this.snapshot;
6835             delete this.snapshot;
6836             if(suppressEvent !== true){
6837                 this.fireEvent("datachanged", this);
6838             }
6839         }
6840     },
6841
6842     // private
6843     afterEdit : function(record){
6844         if(this.modified.indexOf(record) == -1){
6845             this.modified.push(record);
6846         }
6847         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6848     },
6849     
6850     // private
6851     afterReject : function(record){
6852         this.modified.remove(record);
6853         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6854     },
6855
6856     // private
6857     afterCommit : function(record){
6858         this.modified.remove(record);
6859         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6860     },
6861
6862     /**
6863      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6864      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6865      */
6866     commitChanges : function(){
6867         var m = this.modified.slice(0);
6868         this.modified = [];
6869         for(var i = 0, len = m.length; i < len; i++){
6870             m[i].commit();
6871         }
6872     },
6873
6874     /**
6875      * Cancel outstanding changes on all changed records.
6876      */
6877     rejectChanges : function(){
6878         var m = this.modified.slice(0);
6879         this.modified = [];
6880         for(var i = 0, len = m.length; i < len; i++){
6881             m[i].reject();
6882         }
6883     },
6884
6885     onMetaChange : function(meta, rtype, o){
6886         this.recordType = rtype;
6887         this.fields = rtype.prototype.fields;
6888         delete this.snapshot;
6889         this.sortInfo = meta.sortInfo || this.sortInfo;
6890         this.modified = [];
6891         this.fireEvent('metachange', this, this.reader.meta);
6892     },
6893     
6894     moveIndex : function(data, type)
6895     {
6896         var index = this.indexOf(data);
6897         
6898         var newIndex = index + type;
6899         
6900         this.remove(data);
6901         
6902         this.insert(newIndex, data);
6903         
6904     }
6905 });/*
6906  * Based on:
6907  * Ext JS Library 1.1.1
6908  * Copyright(c) 2006-2007, Ext JS, LLC.
6909  *
6910  * Originally Released Under LGPL - original licence link has changed is not relivant.
6911  *
6912  * Fork - LGPL
6913  * <script type="text/javascript">
6914  */
6915
6916 /**
6917  * @class Roo.data.SimpleStore
6918  * @extends Roo.data.Store
6919  * Small helper class to make creating Stores from Array data easier.
6920  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6921  * @cfg {Array} fields An array of field definition objects, or field name strings.
6922  * @cfg {Array} data The multi-dimensional array of data
6923  * @constructor
6924  * @param {Object} config
6925  */
6926 Roo.data.SimpleStore = function(config){
6927     Roo.data.SimpleStore.superclass.constructor.call(this, {
6928         isLocal : true,
6929         reader: new Roo.data.ArrayReader({
6930                 id: config.id
6931             },
6932             Roo.data.Record.create(config.fields)
6933         ),
6934         proxy : new Roo.data.MemoryProxy(config.data)
6935     });
6936     this.load();
6937 };
6938 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6939  * Based on:
6940  * Ext JS Library 1.1.1
6941  * Copyright(c) 2006-2007, Ext JS, LLC.
6942  *
6943  * Originally Released Under LGPL - original licence link has changed is not relivant.
6944  *
6945  * Fork - LGPL
6946  * <script type="text/javascript">
6947  */
6948
6949 /**
6950 /**
6951  * @extends Roo.data.Store
6952  * @class Roo.data.JsonStore
6953  * Small helper class to make creating Stores for JSON data easier. <br/>
6954 <pre><code>
6955 var store = new Roo.data.JsonStore({
6956     url: 'get-images.php',
6957     root: 'images',
6958     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6959 });
6960 </code></pre>
6961  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6962  * JsonReader and HttpProxy (unless inline data is provided).</b>
6963  * @cfg {Array} fields An array of field definition objects, or field name strings.
6964  * @constructor
6965  * @param {Object} config
6966  */
6967 Roo.data.JsonStore = function(c){
6968     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6969         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6970         reader: new Roo.data.JsonReader(c, c.fields)
6971     }));
6972 };
6973 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983
6984  
6985 Roo.data.Field = function(config){
6986     if(typeof config == "string"){
6987         config = {name: config};
6988     }
6989     Roo.apply(this, config);
6990     
6991     if(!this.type){
6992         this.type = "auto";
6993     }
6994     
6995     var st = Roo.data.SortTypes;
6996     // named sortTypes are supported, here we look them up
6997     if(typeof this.sortType == "string"){
6998         this.sortType = st[this.sortType];
6999     }
7000     
7001     // set default sortType for strings and dates
7002     if(!this.sortType){
7003         switch(this.type){
7004             case "string":
7005                 this.sortType = st.asUCString;
7006                 break;
7007             case "date":
7008                 this.sortType = st.asDate;
7009                 break;
7010             default:
7011                 this.sortType = st.none;
7012         }
7013     }
7014
7015     // define once
7016     var stripRe = /[\$,%]/g;
7017
7018     // prebuilt conversion function for this field, instead of
7019     // switching every time we're reading a value
7020     if(!this.convert){
7021         var cv, dateFormat = this.dateFormat;
7022         switch(this.type){
7023             case "":
7024             case "auto":
7025             case undefined:
7026                 cv = function(v){ return v; };
7027                 break;
7028             case "string":
7029                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7030                 break;
7031             case "int":
7032                 cv = function(v){
7033                     return v !== undefined && v !== null && v !== '' ?
7034                            parseInt(String(v).replace(stripRe, ""), 10) : '';
7035                     };
7036                 break;
7037             case "float":
7038                 cv = function(v){
7039                     return v !== undefined && v !== null && v !== '' ?
7040                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
7041                     };
7042                 break;
7043             case "bool":
7044             case "boolean":
7045                 cv = function(v){ return v === true || v === "true" || v == 1; };
7046                 break;
7047             case "date":
7048                 cv = function(v){
7049                     if(!v){
7050                         return '';
7051                     }
7052                     if(v instanceof Date){
7053                         return v;
7054                     }
7055                     if(dateFormat){
7056                         if(dateFormat == "timestamp"){
7057                             return new Date(v*1000);
7058                         }
7059                         return Date.parseDate(v, dateFormat);
7060                     }
7061                     var parsed = Date.parse(v);
7062                     return parsed ? new Date(parsed) : null;
7063                 };
7064              break;
7065             
7066         }
7067         this.convert = cv;
7068     }
7069 };
7070
7071 Roo.data.Field.prototype = {
7072     dateFormat: null,
7073     defaultValue: "",
7074     mapping: null,
7075     sortType : null,
7076     sortDir : "ASC"
7077 };/*
7078  * Based on:
7079  * Ext JS Library 1.1.1
7080  * Copyright(c) 2006-2007, Ext JS, LLC.
7081  *
7082  * Originally Released Under LGPL - original licence link has changed is not relivant.
7083  *
7084  * Fork - LGPL
7085  * <script type="text/javascript">
7086  */
7087  
7088 // Base class for reading structured data from a data source.  This class is intended to be
7089 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7090
7091 /**
7092  * @class Roo.data.DataReader
7093  * Base class for reading structured data from a data source.  This class is intended to be
7094  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7095  */
7096
7097 Roo.data.DataReader = function(meta, recordType){
7098     
7099     this.meta = meta;
7100     
7101     this.recordType = recordType instanceof Array ? 
7102         Roo.data.Record.create(recordType) : recordType;
7103 };
7104
7105 Roo.data.DataReader.prototype = {
7106      /**
7107      * Create an empty record
7108      * @param {Object} data (optional) - overlay some values
7109      * @return {Roo.data.Record} record created.
7110      */
7111     newRow :  function(d) {
7112         var da =  {};
7113         this.recordType.prototype.fields.each(function(c) {
7114             switch( c.type) {
7115                 case 'int' : da[c.name] = 0; break;
7116                 case 'date' : da[c.name] = new Date(); break;
7117                 case 'float' : da[c.name] = 0.0; break;
7118                 case 'boolean' : da[c.name] = false; break;
7119                 default : da[c.name] = ""; break;
7120             }
7121             
7122         });
7123         return new this.recordType(Roo.apply(da, d));
7124     }
7125     
7126 };/*
7127  * Based on:
7128  * Ext JS Library 1.1.1
7129  * Copyright(c) 2006-2007, Ext JS, LLC.
7130  *
7131  * Originally Released Under LGPL - original licence link has changed is not relivant.
7132  *
7133  * Fork - LGPL
7134  * <script type="text/javascript">
7135  */
7136
7137 /**
7138  * @class Roo.data.DataProxy
7139  * @extends Roo.data.Observable
7140  * This class is an abstract base class for implementations which provide retrieval of
7141  * unformatted data objects.<br>
7142  * <p>
7143  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7144  * (of the appropriate type which knows how to parse the data object) to provide a block of
7145  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7146  * <p>
7147  * Custom implementations must implement the load method as described in
7148  * {@link Roo.data.HttpProxy#load}.
7149  */
7150 Roo.data.DataProxy = function(){
7151     this.addEvents({
7152         /**
7153          * @event beforeload
7154          * Fires before a network request is made to retrieve a data object.
7155          * @param {Object} This DataProxy object.
7156          * @param {Object} params The params parameter to the load function.
7157          */
7158         beforeload : true,
7159         /**
7160          * @event load
7161          * Fires before the load method's callback is called.
7162          * @param {Object} This DataProxy object.
7163          * @param {Object} o The data object.
7164          * @param {Object} arg The callback argument object passed to the load function.
7165          */
7166         load : true,
7167         /**
7168          * @event loadexception
7169          * Fires if an Exception occurs during data retrieval.
7170          * @param {Object} This DataProxy object.
7171          * @param {Object} o The data object.
7172          * @param {Object} arg The callback argument object passed to the load function.
7173          * @param {Object} e The Exception.
7174          */
7175         loadexception : true
7176     });
7177     Roo.data.DataProxy.superclass.constructor.call(this);
7178 };
7179
7180 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7181
7182     /**
7183      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7184      */
7185 /*
7186  * Based on:
7187  * Ext JS Library 1.1.1
7188  * Copyright(c) 2006-2007, Ext JS, LLC.
7189  *
7190  * Originally Released Under LGPL - original licence link has changed is not relivant.
7191  *
7192  * Fork - LGPL
7193  * <script type="text/javascript">
7194  */
7195 /**
7196  * @class Roo.data.MemoryProxy
7197  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7198  * to the Reader when its load method is called.
7199  * @constructor
7200  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7201  */
7202 Roo.data.MemoryProxy = function(data){
7203     if (data.data) {
7204         data = data.data;
7205     }
7206     Roo.data.MemoryProxy.superclass.constructor.call(this);
7207     this.data = data;
7208 };
7209
7210 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7211     /**
7212      * Load data from the requested source (in this case an in-memory
7213      * data object passed to the constructor), read the data object into
7214      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7215      * process that block using the passed callback.
7216      * @param {Object} params This parameter is not used by the MemoryProxy class.
7217      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7218      * object into a block of Roo.data.Records.
7219      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7220      * The function must be passed <ul>
7221      * <li>The Record block object</li>
7222      * <li>The "arg" argument from the load function</li>
7223      * <li>A boolean success indicator</li>
7224      * </ul>
7225      * @param {Object} scope The scope in which to call the callback
7226      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7227      */
7228     load : function(params, reader, callback, scope, arg){
7229         params = params || {};
7230         var result;
7231         try {
7232             result = reader.readRecords(this.data);
7233         }catch(e){
7234             this.fireEvent("loadexception", this, arg, null, e);
7235             callback.call(scope, null, arg, false);
7236             return;
7237         }
7238         callback.call(scope, result, arg, true);
7239     },
7240     
7241     // private
7242     update : function(params, records){
7243         
7244     }
7245 });/*
7246  * Based on:
7247  * Ext JS Library 1.1.1
7248  * Copyright(c) 2006-2007, Ext JS, LLC.
7249  *
7250  * Originally Released Under LGPL - original licence link has changed is not relivant.
7251  *
7252  * Fork - LGPL
7253  * <script type="text/javascript">
7254  */
7255 /**
7256  * @class Roo.data.HttpProxy
7257  * @extends Roo.data.DataProxy
7258  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7259  * configured to reference a certain URL.<br><br>
7260  * <p>
7261  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7262  * from which the running page was served.<br><br>
7263  * <p>
7264  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7265  * <p>
7266  * Be aware that to enable the browser to parse an XML document, the server must set
7267  * the Content-Type header in the HTTP response to "text/xml".
7268  * @constructor
7269  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7270  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7271  * will be used to make the request.
7272  */
7273 Roo.data.HttpProxy = function(conn){
7274     Roo.data.HttpProxy.superclass.constructor.call(this);
7275     // is conn a conn config or a real conn?
7276     this.conn = conn;
7277     this.useAjax = !conn || !conn.events;
7278   
7279 };
7280
7281 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7282     // thse are take from connection...
7283     
7284     /**
7285      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7286      */
7287     /**
7288      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7289      * extra parameters to each request made by this object. (defaults to undefined)
7290      */
7291     /**
7292      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7293      *  to each request made by this object. (defaults to undefined)
7294      */
7295     /**
7296      * @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)
7297      */
7298     /**
7299      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7300      */
7301      /**
7302      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7303      * @type Boolean
7304      */
7305   
7306
7307     /**
7308      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7309      * @type Boolean
7310      */
7311     /**
7312      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7313      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7314      * a finer-grained basis than the DataProxy events.
7315      */
7316     getConnection : function(){
7317         return this.useAjax ? Roo.Ajax : this.conn;
7318     },
7319
7320     /**
7321      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7322      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7323      * process that block using the passed callback.
7324      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7325      * for the request to the remote server.
7326      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7327      * object into a block of Roo.data.Records.
7328      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7329      * The function must be passed <ul>
7330      * <li>The Record block object</li>
7331      * <li>The "arg" argument from the load function</li>
7332      * <li>A boolean success indicator</li>
7333      * </ul>
7334      * @param {Object} scope The scope in which to call the callback
7335      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7336      */
7337     load : function(params, reader, callback, scope, arg){
7338         if(this.fireEvent("beforeload", this, params) !== false){
7339             var  o = {
7340                 params : params || {},
7341                 request: {
7342                     callback : callback,
7343                     scope : scope,
7344                     arg : arg
7345                 },
7346                 reader: reader,
7347                 callback : this.loadResponse,
7348                 scope: this
7349             };
7350             if(this.useAjax){
7351                 Roo.applyIf(o, this.conn);
7352                 if(this.activeRequest){
7353                     Roo.Ajax.abort(this.activeRequest);
7354                 }
7355                 this.activeRequest = Roo.Ajax.request(o);
7356             }else{
7357                 this.conn.request(o);
7358             }
7359         }else{
7360             callback.call(scope||this, null, arg, false);
7361         }
7362     },
7363
7364     // private
7365     loadResponse : function(o, success, response){
7366         delete this.activeRequest;
7367         if(!success){
7368             this.fireEvent("loadexception", this, o, response);
7369             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7370             return;
7371         }
7372         var result;
7373         try {
7374             result = o.reader.read(response);
7375         }catch(e){
7376             this.fireEvent("loadexception", this, o, response, e);
7377             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7378             return;
7379         }
7380         
7381         this.fireEvent("load", this, o, o.request.arg);
7382         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7383     },
7384
7385     // private
7386     update : function(dataSet){
7387
7388     },
7389
7390     // private
7391     updateResponse : function(dataSet){
7392
7393     }
7394 });/*
7395  * Based on:
7396  * Ext JS Library 1.1.1
7397  * Copyright(c) 2006-2007, Ext JS, LLC.
7398  *
7399  * Originally Released Under LGPL - original licence link has changed is not relivant.
7400  *
7401  * Fork - LGPL
7402  * <script type="text/javascript">
7403  */
7404
7405 /**
7406  * @class Roo.data.ScriptTagProxy
7407  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7408  * other than the originating domain of the running page.<br><br>
7409  * <p>
7410  * <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
7411  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7412  * <p>
7413  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7414  * source code that is used as the source inside a &lt;script> tag.<br><br>
7415  * <p>
7416  * In order for the browser to process the returned data, the server must wrap the data object
7417  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7418  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7419  * depending on whether the callback name was passed:
7420  * <p>
7421  * <pre><code>
7422 boolean scriptTag = false;
7423 String cb = request.getParameter("callback");
7424 if (cb != null) {
7425     scriptTag = true;
7426     response.setContentType("text/javascript");
7427 } else {
7428     response.setContentType("application/x-json");
7429 }
7430 Writer out = response.getWriter();
7431 if (scriptTag) {
7432     out.write(cb + "(");
7433 }
7434 out.print(dataBlock.toJsonString());
7435 if (scriptTag) {
7436     out.write(");");
7437 }
7438 </pre></code>
7439  *
7440  * @constructor
7441  * @param {Object} config A configuration object.
7442  */
7443 Roo.data.ScriptTagProxy = function(config){
7444     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7445     Roo.apply(this, config);
7446     this.head = document.getElementsByTagName("head")[0];
7447 };
7448
7449 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7450
7451 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7452     /**
7453      * @cfg {String} url The URL from which to request the data object.
7454      */
7455     /**
7456      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7457      */
7458     timeout : 30000,
7459     /**
7460      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7461      * the server the name of the callback function set up by the load call to process the returned data object.
7462      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7463      * javascript output which calls this named function passing the data object as its only parameter.
7464      */
7465     callbackParam : "callback",
7466     /**
7467      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7468      * name to the request.
7469      */
7470     nocache : true,
7471
7472     /**
7473      * Load data from the configured URL, read the data object into
7474      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7475      * process that block using the passed callback.
7476      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7477      * for the request to the remote server.
7478      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7479      * object into a block of Roo.data.Records.
7480      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7481      * The function must be passed <ul>
7482      * <li>The Record block object</li>
7483      * <li>The "arg" argument from the load function</li>
7484      * <li>A boolean success indicator</li>
7485      * </ul>
7486      * @param {Object} scope The scope in which to call the callback
7487      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7488      */
7489     load : function(params, reader, callback, scope, arg){
7490         if(this.fireEvent("beforeload", this, params) !== false){
7491
7492             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7493
7494             var url = this.url;
7495             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7496             if(this.nocache){
7497                 url += "&_dc=" + (new Date().getTime());
7498             }
7499             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7500             var trans = {
7501                 id : transId,
7502                 cb : "stcCallback"+transId,
7503                 scriptId : "stcScript"+transId,
7504                 params : params,
7505                 arg : arg,
7506                 url : url,
7507                 callback : callback,
7508                 scope : scope,
7509                 reader : reader
7510             };
7511             var conn = this;
7512
7513             window[trans.cb] = function(o){
7514                 conn.handleResponse(o, trans);
7515             };
7516
7517             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7518
7519             if(this.autoAbort !== false){
7520                 this.abort();
7521             }
7522
7523             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7524
7525             var script = document.createElement("script");
7526             script.setAttribute("src", url);
7527             script.setAttribute("type", "text/javascript");
7528             script.setAttribute("id", trans.scriptId);
7529             this.head.appendChild(script);
7530
7531             this.trans = trans;
7532         }else{
7533             callback.call(scope||this, null, arg, false);
7534         }
7535     },
7536
7537     // private
7538     isLoading : function(){
7539         return this.trans ? true : false;
7540     },
7541
7542     /**
7543      * Abort the current server request.
7544      */
7545     abort : function(){
7546         if(this.isLoading()){
7547             this.destroyTrans(this.trans);
7548         }
7549     },
7550
7551     // private
7552     destroyTrans : function(trans, isLoaded){
7553         this.head.removeChild(document.getElementById(trans.scriptId));
7554         clearTimeout(trans.timeoutId);
7555         if(isLoaded){
7556             window[trans.cb] = undefined;
7557             try{
7558                 delete window[trans.cb];
7559             }catch(e){}
7560         }else{
7561             // if hasn't been loaded, wait for load to remove it to prevent script error
7562             window[trans.cb] = function(){
7563                 window[trans.cb] = undefined;
7564                 try{
7565                     delete window[trans.cb];
7566                 }catch(e){}
7567             };
7568         }
7569     },
7570
7571     // private
7572     handleResponse : function(o, trans){
7573         this.trans = false;
7574         this.destroyTrans(trans, true);
7575         var result;
7576         try {
7577             result = trans.reader.readRecords(o);
7578         }catch(e){
7579             this.fireEvent("loadexception", this, o, trans.arg, e);
7580             trans.callback.call(trans.scope||window, null, trans.arg, false);
7581             return;
7582         }
7583         this.fireEvent("load", this, o, trans.arg);
7584         trans.callback.call(trans.scope||window, result, trans.arg, true);
7585     },
7586
7587     // private
7588     handleFailure : function(trans){
7589         this.trans = false;
7590         this.destroyTrans(trans, false);
7591         this.fireEvent("loadexception", this, null, trans.arg);
7592         trans.callback.call(trans.scope||window, null, trans.arg, false);
7593     }
7594 });/*
7595  * Based on:
7596  * Ext JS Library 1.1.1
7597  * Copyright(c) 2006-2007, Ext JS, LLC.
7598  *
7599  * Originally Released Under LGPL - original licence link has changed is not relivant.
7600  *
7601  * Fork - LGPL
7602  * <script type="text/javascript">
7603  */
7604
7605 /**
7606  * @class Roo.data.JsonReader
7607  * @extends Roo.data.DataReader
7608  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7609  * based on mappings in a provided Roo.data.Record constructor.
7610  * 
7611  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7612  * in the reply previously. 
7613  * 
7614  * <p>
7615  * Example code:
7616  * <pre><code>
7617 var RecordDef = Roo.data.Record.create([
7618     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7619     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7620 ]);
7621 var myReader = new Roo.data.JsonReader({
7622     totalProperty: "results",    // The property which contains the total dataset size (optional)
7623     root: "rows",                // The property which contains an Array of row objects
7624     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7625 }, RecordDef);
7626 </code></pre>
7627  * <p>
7628  * This would consume a JSON file like this:
7629  * <pre><code>
7630 { 'results': 2, 'rows': [
7631     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7632     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7633 }
7634 </code></pre>
7635  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7636  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7637  * paged from the remote server.
7638  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7639  * @cfg {String} root name of the property which contains the Array of row objects.
7640  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7641  * @constructor
7642  * Create a new JsonReader
7643  * @param {Object} meta Metadata configuration options
7644  * @param {Object} recordType Either an Array of field definition objects,
7645  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7646  */
7647 Roo.data.JsonReader = function(meta, recordType){
7648     
7649     meta = meta || {};
7650     // set some defaults:
7651     Roo.applyIf(meta, {
7652         totalProperty: 'total',
7653         successProperty : 'success',
7654         root : 'data',
7655         id : 'id'
7656     });
7657     
7658     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7659 };
7660 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7661     
7662     /**
7663      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7664      * Used by Store query builder to append _requestMeta to params.
7665      * 
7666      */
7667     metaFromRemote : false,
7668     /**
7669      * This method is only used by a DataProxy which has retrieved data from a remote server.
7670      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7671      * @return {Object} data A data block which is used by an Roo.data.Store object as
7672      * a cache of Roo.data.Records.
7673      */
7674     read : function(response){
7675         var json = response.responseText;
7676        
7677         var o = /* eval:var:o */ eval("("+json+")");
7678         if(!o) {
7679             throw {message: "JsonReader.read: Json object not found"};
7680         }
7681         
7682         if(o.metaData){
7683             
7684             delete this.ef;
7685             this.metaFromRemote = true;
7686             this.meta = o.metaData;
7687             this.recordType = Roo.data.Record.create(o.metaData.fields);
7688             this.onMetaChange(this.meta, this.recordType, o);
7689         }
7690         return this.readRecords(o);
7691     },
7692
7693     // private function a store will implement
7694     onMetaChange : function(meta, recordType, o){
7695
7696     },
7697
7698     /**
7699          * @ignore
7700          */
7701     simpleAccess: function(obj, subsc) {
7702         return obj[subsc];
7703     },
7704
7705         /**
7706          * @ignore
7707          */
7708     getJsonAccessor: function(){
7709         var re = /[\[\.]/;
7710         return function(expr) {
7711             try {
7712                 return(re.test(expr))
7713                     ? new Function("obj", "return obj." + expr)
7714                     : function(obj){
7715                         return obj[expr];
7716                     };
7717             } catch(e){}
7718             return Roo.emptyFn;
7719         };
7720     }(),
7721
7722     /**
7723      * Create a data block containing Roo.data.Records from an XML document.
7724      * @param {Object} o An object which contains an Array of row objects in the property specified
7725      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7726      * which contains the total size of the dataset.
7727      * @return {Object} data A data block which is used by an Roo.data.Store object as
7728      * a cache of Roo.data.Records.
7729      */
7730     readRecords : function(o){
7731         /**
7732          * After any data loads, the raw JSON data is available for further custom processing.
7733          * @type Object
7734          */
7735         this.o = o;
7736         var s = this.meta, Record = this.recordType,
7737             f = Record.prototype.fields, fi = f.items, fl = f.length;
7738
7739 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7740         if (!this.ef) {
7741             if(s.totalProperty) {
7742                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7743                 }
7744                 if(s.successProperty) {
7745                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7746                 }
7747                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7748                 if (s.id) {
7749                         var g = this.getJsonAccessor(s.id);
7750                         this.getId = function(rec) {
7751                                 var r = g(rec);
7752                                 return (r === undefined || r === "") ? null : r;
7753                         };
7754                 } else {
7755                         this.getId = function(){return null;};
7756                 }
7757             this.ef = [];
7758             for(var jj = 0; jj < fl; jj++){
7759                 f = fi[jj];
7760                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7761                 this.ef[jj] = this.getJsonAccessor(map);
7762             }
7763         }
7764
7765         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7766         if(s.totalProperty){
7767             var vt = parseInt(this.getTotal(o), 10);
7768             if(!isNaN(vt)){
7769                 totalRecords = vt;
7770             }
7771         }
7772         if(s.successProperty){
7773             var vs = this.getSuccess(o);
7774             if(vs === false || vs === 'false'){
7775                 success = false;
7776             }
7777         }
7778         var records = [];
7779             for(var i = 0; i < c; i++){
7780                     var n = root[i];
7781                 var values = {};
7782                 var id = this.getId(n);
7783                 for(var j = 0; j < fl; j++){
7784                     f = fi[j];
7785                 var v = this.ef[j](n);
7786                 if (!f.convert) {
7787                     Roo.log('missing convert for ' + f.name);
7788                     Roo.log(f);
7789                     continue;
7790                 }
7791                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7792                 }
7793                 var record = new Record(values, id);
7794                 record.json = n;
7795                 records[i] = record;
7796             }
7797             return {
7798             raw : o,
7799                 success : success,
7800                 records : records,
7801                 totalRecords : totalRecords
7802             };
7803     }
7804 });/*
7805  * Based on:
7806  * Ext JS Library 1.1.1
7807  * Copyright(c) 2006-2007, Ext JS, LLC.
7808  *
7809  * Originally Released Under LGPL - original licence link has changed is not relivant.
7810  *
7811  * Fork - LGPL
7812  * <script type="text/javascript">
7813  */
7814
7815 /**
7816  * @class Roo.data.ArrayReader
7817  * @extends Roo.data.DataReader
7818  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7819  * Each element of that Array represents a row of data fields. The
7820  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7821  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7822  * <p>
7823  * Example code:.
7824  * <pre><code>
7825 var RecordDef = Roo.data.Record.create([
7826     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7827     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7828 ]);
7829 var myReader = new Roo.data.ArrayReader({
7830     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7831 }, RecordDef);
7832 </code></pre>
7833  * <p>
7834  * This would consume an Array like this:
7835  * <pre><code>
7836 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7837   </code></pre>
7838  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7839  * @constructor
7840  * Create a new JsonReader
7841  * @param {Object} meta Metadata configuration options.
7842  * @param {Object} recordType Either an Array of field definition objects
7843  * as specified to {@link Roo.data.Record#create},
7844  * or an {@link Roo.data.Record} object
7845  * created using {@link Roo.data.Record#create}.
7846  */
7847 Roo.data.ArrayReader = function(meta, recordType){
7848     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7849 };
7850
7851 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7852     /**
7853      * Create a data block containing Roo.data.Records from an XML document.
7854      * @param {Object} o An Array of row objects which represents the dataset.
7855      * @return {Object} data A data block which is used by an Roo.data.Store object as
7856      * a cache of Roo.data.Records.
7857      */
7858     readRecords : function(o){
7859         var sid = this.meta ? this.meta.id : null;
7860         var recordType = this.recordType, fields = recordType.prototype.fields;
7861         var records = [];
7862         var root = o;
7863             for(var i = 0; i < root.length; i++){
7864                     var n = root[i];
7865                 var values = {};
7866                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7867                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7868                 var f = fields.items[j];
7869                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7870                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7871                 v = f.convert(v);
7872                 values[f.name] = v;
7873             }
7874                 var record = new recordType(values, id);
7875                 record.json = n;
7876                 records[records.length] = record;
7877             }
7878             return {
7879                 records : records,
7880                 totalRecords : records.length
7881             };
7882     }
7883 });/*
7884  * - LGPL
7885  * * 
7886  */
7887
7888 /**
7889  * @class Roo.bootstrap.ComboBox
7890  * @extends Roo.bootstrap.TriggerField
7891  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7892  * @cfg {Boolean} append (true|false) default false
7893  * @constructor
7894  * Create a new ComboBox.
7895  * @param {Object} config Configuration options
7896  */
7897 Roo.bootstrap.ComboBox = function(config){
7898     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7899     this.addEvents({
7900         /**
7901          * @event expand
7902          * Fires when the dropdown list is expanded
7903              * @param {Roo.bootstrap.ComboBox} combo This combo box
7904              */
7905         'expand' : true,
7906         /**
7907          * @event collapse
7908          * Fires when the dropdown list is collapsed
7909              * @param {Roo.bootstrap.ComboBox} combo This combo box
7910              */
7911         'collapse' : true,
7912         /**
7913          * @event beforeselect
7914          * Fires before a list item is selected. Return false to cancel the selection.
7915              * @param {Roo.bootstrap.ComboBox} combo This combo box
7916              * @param {Roo.data.Record} record The data record returned from the underlying store
7917              * @param {Number} index The index of the selected item in the dropdown list
7918              */
7919         'beforeselect' : true,
7920         /**
7921          * @event select
7922          * Fires when a list item is selected
7923              * @param {Roo.bootstrap.ComboBox} combo This combo box
7924              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7925              * @param {Number} index The index of the selected item in the dropdown list
7926              */
7927         'select' : true,
7928         /**
7929          * @event beforequery
7930          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7931          * The event object passed has these properties:
7932              * @param {Roo.bootstrap.ComboBox} combo This combo box
7933              * @param {String} query The query
7934              * @param {Boolean} forceAll true to force "all" query
7935              * @param {Boolean} cancel true to cancel the query
7936              * @param {Object} e The query event object
7937              */
7938         'beforequery': true,
7939          /**
7940          * @event add
7941          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7942              * @param {Roo.bootstrap.ComboBox} combo This combo box
7943              */
7944         'add' : true,
7945         /**
7946          * @event edit
7947          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7948              * @param {Roo.bootstrap.ComboBox} combo This combo box
7949              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7950              */
7951         'edit' : true,
7952         /**
7953          * @event remove
7954          * Fires when the remove value from the combobox array
7955              * @param {Roo.bootstrap.ComboBox} combo This combo box
7956              */
7957         'remove' : true
7958         
7959     });
7960     
7961     
7962     this.selectedIndex = -1;
7963     if(this.mode == 'local'){
7964         if(config.queryDelay === undefined){
7965             this.queryDelay = 10;
7966         }
7967         if(config.minChars === undefined){
7968             this.minChars = 0;
7969         }
7970     }
7971 };
7972
7973 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7974      
7975     /**
7976      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7977      * rendering into an Roo.Editor, defaults to false)
7978      */
7979     /**
7980      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7981      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7982      */
7983     /**
7984      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7985      */
7986     /**
7987      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7988      * the dropdown list (defaults to undefined, with no header element)
7989      */
7990
7991      /**
7992      * @cfg {String/Roo.Template} tpl The template to use to render the output
7993      */
7994      
7995      /**
7996      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7997      */
7998     listWidth: undefined,
7999     /**
8000      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8001      * mode = 'remote' or 'text' if mode = 'local')
8002      */
8003     displayField: undefined,
8004     /**
8005      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8006      * mode = 'remote' or 'value' if mode = 'local'). 
8007      * Note: use of a valueField requires the user make a selection
8008      * in order for a value to be mapped.
8009      */
8010     valueField: undefined,
8011     
8012     
8013     /**
8014      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8015      * field's data value (defaults to the underlying DOM element's name)
8016      */
8017     hiddenName: undefined,
8018     /**
8019      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8020      */
8021     listClass: '',
8022     /**
8023      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8024      */
8025     selectedClass: 'active',
8026     
8027     /**
8028      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8029      */
8030     shadow:'sides',
8031     /**
8032      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8033      * anchor positions (defaults to 'tl-bl')
8034      */
8035     listAlign: 'tl-bl?',
8036     /**
8037      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8038      */
8039     maxHeight: 300,
8040     /**
8041      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
8042      * query specified by the allQuery config option (defaults to 'query')
8043      */
8044     triggerAction: 'query',
8045     /**
8046      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8047      * (defaults to 4, does not apply if editable = false)
8048      */
8049     minChars : 4,
8050     /**
8051      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8052      * delay (typeAheadDelay) if it matches a known value (defaults to false)
8053      */
8054     typeAhead: false,
8055     /**
8056      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8057      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8058      */
8059     queryDelay: 500,
8060     /**
8061      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8062      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
8063      */
8064     pageSize: 0,
8065     /**
8066      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
8067      * when editable = true (defaults to false)
8068      */
8069     selectOnFocus:false,
8070     /**
8071      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8072      */
8073     queryParam: 'query',
8074     /**
8075      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
8076      * when mode = 'remote' (defaults to 'Loading...')
8077      */
8078     loadingText: 'Loading...',
8079     /**
8080      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8081      */
8082     resizable: false,
8083     /**
8084      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8085      */
8086     handleHeight : 8,
8087     /**
8088      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8089      * traditional select (defaults to true)
8090      */
8091     editable: true,
8092     /**
8093      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8094      */
8095     allQuery: '',
8096     /**
8097      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8098      */
8099     mode: 'remote',
8100     /**
8101      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8102      * listWidth has a higher value)
8103      */
8104     minListWidth : 70,
8105     /**
8106      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8107      * allow the user to set arbitrary text into the field (defaults to false)
8108      */
8109     forceSelection:false,
8110     /**
8111      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8112      * if typeAhead = true (defaults to 250)
8113      */
8114     typeAheadDelay : 250,
8115     /**
8116      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8117      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8118      */
8119     valueNotFoundText : undefined,
8120     /**
8121      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8122      */
8123     blockFocus : false,
8124     
8125     /**
8126      * @cfg {Boolean} disableClear Disable showing of clear button.
8127      */
8128     disableClear : false,
8129     /**
8130      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8131      */
8132     alwaysQuery : false,
8133     
8134     /**
8135      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8136      */
8137     multiple : false,
8138     
8139     //private
8140     addicon : false,
8141     editicon: false,
8142     
8143     page: 0,
8144     hasQuery: false,
8145     append: false,
8146     loadNext: false,
8147     item: [],
8148     
8149     // element that contains real text value.. (when hidden is used..)
8150      
8151     // private
8152     initEvents: function(){
8153         
8154         if (!this.store) {
8155             throw "can not find store for combo";
8156         }
8157         this.store = Roo.factory(this.store, Roo.data);
8158         
8159         
8160         
8161         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8162         
8163         
8164         if(this.hiddenName){
8165             
8166             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8167             
8168             this.hiddenField.dom.value =
8169                 this.hiddenValue !== undefined ? this.hiddenValue :
8170                 this.value !== undefined ? this.value : '';
8171
8172             // prevent input submission
8173             this.el.dom.removeAttribute('name');
8174             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8175              
8176              
8177         }
8178         //if(Roo.isGecko){
8179         //    this.el.dom.setAttribute('autocomplete', 'off');
8180         //}
8181
8182         var cls = 'x-combo-list';
8183         this.list = this.el.select('ul.dropdown-menu',true).first();
8184
8185         //this.list = new Roo.Layer({
8186         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8187         //});
8188         
8189         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8190         this.list.setWidth(lw);
8191         
8192         this.list.on('mouseover', this.onViewOver, this);
8193         this.list.on('mousemove', this.onViewMove, this);
8194         
8195         this.list.on('scroll', this.onViewScroll, this);
8196         
8197         /*
8198         this.list.swallowEvent('mousewheel');
8199         this.assetHeight = 0;
8200
8201         if(this.title){
8202             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8203             this.assetHeight += this.header.getHeight();
8204         }
8205
8206         this.innerList = this.list.createChild({cls:cls+'-inner'});
8207         this.innerList.on('mouseover', this.onViewOver, this);
8208         this.innerList.on('mousemove', this.onViewMove, this);
8209         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8210         
8211         if(this.allowBlank && !this.pageSize && !this.disableClear){
8212             this.footer = this.list.createChild({cls:cls+'-ft'});
8213             this.pageTb = new Roo.Toolbar(this.footer);
8214            
8215         }
8216         if(this.pageSize){
8217             this.footer = this.list.createChild({cls:cls+'-ft'});
8218             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8219                     {pageSize: this.pageSize});
8220             
8221         }
8222         
8223         if (this.pageTb && this.allowBlank && !this.disableClear) {
8224             var _this = this;
8225             this.pageTb.add(new Roo.Toolbar.Fill(), {
8226                 cls: 'x-btn-icon x-btn-clear',
8227                 text: '&#160;',
8228                 handler: function()
8229                 {
8230                     _this.collapse();
8231                     _this.clearValue();
8232                     _this.onSelect(false, -1);
8233                 }
8234             });
8235         }
8236         if (this.footer) {
8237             this.assetHeight += this.footer.getHeight();
8238         }
8239         */
8240             
8241         if(!this.tpl){
8242             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8243         }
8244
8245         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8246             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8247         });
8248         //this.view.wrapEl.setDisplayed(false);
8249         this.view.on('click', this.onViewClick, this);
8250         
8251         
8252         
8253         this.store.on('beforeload', this.onBeforeLoad, this);
8254         this.store.on('load', this.onLoad, this);
8255         this.store.on('loadexception', this.onLoadException, this);
8256         /*
8257         if(this.resizable){
8258             this.resizer = new Roo.Resizable(this.list,  {
8259                pinned:true, handles:'se'
8260             });
8261             this.resizer.on('resize', function(r, w, h){
8262                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8263                 this.listWidth = w;
8264                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8265                 this.restrictHeight();
8266             }, this);
8267             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8268         }
8269         */
8270         if(!this.editable){
8271             this.editable = true;
8272             this.setEditable(false);
8273         }
8274         
8275         /*
8276         
8277         if (typeof(this.events.add.listeners) != 'undefined') {
8278             
8279             this.addicon = this.wrap.createChild(
8280                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8281        
8282             this.addicon.on('click', function(e) {
8283                 this.fireEvent('add', this);
8284             }, this);
8285         }
8286         if (typeof(this.events.edit.listeners) != 'undefined') {
8287             
8288             this.editicon = this.wrap.createChild(
8289                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8290             if (this.addicon) {
8291                 this.editicon.setStyle('margin-left', '40px');
8292             }
8293             this.editicon.on('click', function(e) {
8294                 
8295                 // we fire even  if inothing is selected..
8296                 this.fireEvent('edit', this, this.lastData );
8297                 
8298             }, this);
8299         }
8300         */
8301         
8302         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8303             "up" : function(e){
8304                 this.inKeyMode = true;
8305                 this.selectPrev();
8306             },
8307
8308             "down" : function(e){
8309                 if(!this.isExpanded()){
8310                     this.onTriggerClick();
8311                 }else{
8312                     this.inKeyMode = true;
8313                     this.selectNext();
8314                 }
8315             },
8316
8317             "enter" : function(e){
8318                 this.onViewClick();
8319                 //return true;
8320             },
8321
8322             "esc" : function(e){
8323                 this.collapse();
8324             },
8325
8326             "tab" : function(e){
8327                 this.collapse();
8328                 
8329                 if(this.fireEvent("specialkey", this, e)){
8330                     this.onViewClick(false);
8331                 }
8332                 
8333                 return true;
8334             },
8335
8336             scope : this,
8337
8338             doRelay : function(foo, bar, hname){
8339                 if(hname == 'down' || this.scope.isExpanded()){
8340                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8341                 }
8342                 return true;
8343             },
8344
8345             forceKeyDown: true
8346         });
8347         
8348         
8349         this.queryDelay = Math.max(this.queryDelay || 10,
8350                 this.mode == 'local' ? 10 : 250);
8351         
8352         
8353         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8354         
8355         if(this.typeAhead){
8356             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8357         }
8358         if(this.editable !== false){
8359             this.inputEl().on("keyup", this.onKeyUp, this);
8360         }
8361         if(this.forceSelection){
8362             this.on('blur', this.doForce, this);
8363         }
8364         
8365         if(this.multiple){
8366             this.choices = this.el.select('ul.select2-choices', true).first();
8367             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8368         }
8369     },
8370
8371     onDestroy : function(){
8372         if(this.view){
8373             this.view.setStore(null);
8374             this.view.el.removeAllListeners();
8375             this.view.el.remove();
8376             this.view.purgeListeners();
8377         }
8378         if(this.list){
8379             this.list.dom.innerHTML  = '';
8380         }
8381         if(this.store){
8382             this.store.un('beforeload', this.onBeforeLoad, this);
8383             this.store.un('load', this.onLoad, this);
8384             this.store.un('loadexception', this.onLoadException, this);
8385         }
8386         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8387     },
8388
8389     // private
8390     fireKey : function(e){
8391         if(e.isNavKeyPress() && !this.list.isVisible()){
8392             this.fireEvent("specialkey", this, e);
8393         }
8394     },
8395
8396     // private
8397     onResize: function(w, h){
8398 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8399 //        
8400 //        if(typeof w != 'number'){
8401 //            // we do not handle it!?!?
8402 //            return;
8403 //        }
8404 //        var tw = this.trigger.getWidth();
8405 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8406 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8407 //        var x = w - tw;
8408 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8409 //            
8410 //        //this.trigger.setStyle('left', x+'px');
8411 //        
8412 //        if(this.list && this.listWidth === undefined){
8413 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8414 //            this.list.setWidth(lw);
8415 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8416 //        }
8417         
8418     
8419         
8420     },
8421
8422     /**
8423      * Allow or prevent the user from directly editing the field text.  If false is passed,
8424      * the user will only be able to select from the items defined in the dropdown list.  This method
8425      * is the runtime equivalent of setting the 'editable' config option at config time.
8426      * @param {Boolean} value True to allow the user to directly edit the field text
8427      */
8428     setEditable : function(value){
8429         if(value == this.editable){
8430             return;
8431         }
8432         this.editable = value;
8433         if(!value){
8434             this.inputEl().dom.setAttribute('readOnly', true);
8435             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8436             this.inputEl().addClass('x-combo-noedit');
8437         }else{
8438             this.inputEl().dom.setAttribute('readOnly', false);
8439             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8440             this.inputEl().removeClass('x-combo-noedit');
8441         }
8442     },
8443
8444     // private
8445     
8446     onBeforeLoad : function(combo,opts){
8447         if(!this.hasFocus){
8448             return;
8449         }
8450          if (!opts.add) {
8451             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8452          }
8453         this.restrictHeight();
8454         this.selectedIndex = -1;
8455     },
8456
8457     // private
8458     onLoad : function(){
8459         
8460         this.hasQuery = false;
8461         
8462         if(!this.hasFocus){
8463             return;
8464         }
8465         
8466         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8467             this.loading.hide();
8468         }
8469         
8470         if(this.store.getCount() > 0){
8471             this.expand();
8472             this.restrictHeight();
8473             if(this.lastQuery == this.allQuery){
8474                 if(this.editable){
8475                     this.inputEl().dom.select();
8476                 }
8477                 if(!this.selectByValue(this.value, true)){
8478                     this.select(0, true);
8479                 }
8480             }else{
8481                 this.selectNext();
8482                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8483                     this.taTask.delay(this.typeAheadDelay);
8484                 }
8485             }
8486         }else{
8487             this.onEmptyResults();
8488         }
8489         
8490         //this.el.focus();
8491     },
8492     // private
8493     onLoadException : function()
8494     {
8495         this.hasQuery = false;
8496         
8497         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8498             this.loading.hide();
8499         }
8500         
8501         this.collapse();
8502         Roo.log(this.store.reader.jsonData);
8503         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8504             // fixme
8505             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8506         }
8507         
8508         
8509     },
8510     // private
8511     onTypeAhead : function(){
8512         if(this.store.getCount() > 0){
8513             var r = this.store.getAt(0);
8514             var newValue = r.data[this.displayField];
8515             var len = newValue.length;
8516             var selStart = this.getRawValue().length;
8517             
8518             if(selStart != len){
8519                 this.setRawValue(newValue);
8520                 this.selectText(selStart, newValue.length);
8521             }
8522         }
8523     },
8524
8525     // private
8526     onSelect : function(record, index){
8527         
8528         if(this.fireEvent('beforeselect', this, record, index) !== false){
8529         
8530             this.setFromData(index > -1 ? record.data : false);
8531             
8532             this.collapse();
8533             this.fireEvent('select', this, record, index);
8534         }
8535     },
8536
8537     /**
8538      * Returns the currently selected field value or empty string if no value is set.
8539      * @return {String} value The selected value
8540      */
8541     getValue : function(){
8542         
8543         if(this.multiple){
8544             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8545         }
8546         
8547         if(this.valueField){
8548             return typeof this.value != 'undefined' ? this.value : '';
8549         }else{
8550             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8551         }
8552     },
8553
8554     /**
8555      * Clears any text/value currently set in the field
8556      */
8557     clearValue : function(){
8558         if(this.hiddenField){
8559             this.hiddenField.dom.value = '';
8560         }
8561         this.value = '';
8562         this.setRawValue('');
8563         this.lastSelectionText = '';
8564         
8565     },
8566
8567     /**
8568      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8569      * will be displayed in the field.  If the value does not match the data value of an existing item,
8570      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8571      * Otherwise the field will be blank (although the value will still be set).
8572      * @param {String} value The value to match
8573      */
8574     setValue : function(v){
8575         if(this.multiple){
8576             this.syncValue();
8577             return;
8578         }
8579         
8580         var text = v;
8581         if(this.valueField){
8582             var r = this.findRecord(this.valueField, v);
8583             if(r){
8584                 text = r.data[this.displayField];
8585             }else if(this.valueNotFoundText !== undefined){
8586                 text = this.valueNotFoundText;
8587             }
8588         }
8589         this.lastSelectionText = text;
8590         if(this.hiddenField){
8591             this.hiddenField.dom.value = v;
8592         }
8593         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8594         this.value = v;
8595     },
8596     /**
8597      * @property {Object} the last set data for the element
8598      */
8599     
8600     lastData : false,
8601     /**
8602      * Sets the value of the field based on a object which is related to the record format for the store.
8603      * @param {Object} value the value to set as. or false on reset?
8604      */
8605     setFromData : function(o){
8606         
8607         if(this.multiple){
8608             this.addItem(o);
8609             return;
8610         }
8611             
8612         var dv = ''; // display value
8613         var vv = ''; // value value..
8614         this.lastData = o;
8615         if (this.displayField) {
8616             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8617         } else {
8618             // this is an error condition!!!
8619             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8620         }
8621         
8622         if(this.valueField){
8623             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8624         }
8625         
8626         if(this.hiddenField){
8627             this.hiddenField.dom.value = vv;
8628             
8629             this.lastSelectionText = dv;
8630             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8631             this.value = vv;
8632             return;
8633         }
8634         // no hidden field.. - we store the value in 'value', but still display
8635         // display field!!!!
8636         this.lastSelectionText = dv;
8637         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8638         this.value = vv;
8639         
8640         
8641     },
8642     // private
8643     reset : function(){
8644         // overridden so that last data is reset..
8645         this.setValue(this.originalValue);
8646         this.clearInvalid();
8647         this.lastData = false;
8648         if (this.view) {
8649             this.view.clearSelections();
8650         }
8651     },
8652     // private
8653     findRecord : function(prop, value){
8654         var record;
8655         if(this.store.getCount() > 0){
8656             this.store.each(function(r){
8657                 if(r.data[prop] == value){
8658                     record = r;
8659                     return false;
8660                 }
8661                 return true;
8662             });
8663         }
8664         return record;
8665     },
8666     
8667     getName: function()
8668     {
8669         // returns hidden if it's set..
8670         if (!this.rendered) {return ''};
8671         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8672         
8673     },
8674     // private
8675     onViewMove : function(e, t){
8676         this.inKeyMode = false;
8677     },
8678
8679     // private
8680     onViewOver : function(e, t){
8681         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8682             return;
8683         }
8684         var item = this.view.findItemFromChild(t);
8685         if(item){
8686             var index = this.view.indexOf(item);
8687             this.select(index, false);
8688         }
8689     },
8690
8691     // private
8692     onViewClick : function(doFocus)
8693     {
8694         var index = this.view.getSelectedIndexes()[0];
8695         var r = this.store.getAt(index);
8696         if(r){
8697             this.onSelect(r, index);
8698         }
8699         if(doFocus !== false && !this.blockFocus){
8700             this.inputEl().focus();
8701         }
8702     },
8703
8704     // private
8705     restrictHeight : function(){
8706         //this.innerList.dom.style.height = '';
8707         //var inner = this.innerList.dom;
8708         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8709         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8710         //this.list.beginUpdate();
8711         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8712         this.list.alignTo(this.inputEl(), this.listAlign);
8713         //this.list.endUpdate();
8714     },
8715
8716     // private
8717     onEmptyResults : function(){
8718         this.collapse();
8719     },
8720
8721     /**
8722      * Returns true if the dropdown list is expanded, else false.
8723      */
8724     isExpanded : function(){
8725         return this.list.isVisible();
8726     },
8727
8728     /**
8729      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8730      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8731      * @param {String} value The data value of the item to select
8732      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8733      * selected item if it is not currently in view (defaults to true)
8734      * @return {Boolean} True if the value matched an item in the list, else false
8735      */
8736     selectByValue : function(v, scrollIntoView){
8737         if(v !== undefined && v !== null){
8738             var r = this.findRecord(this.valueField || this.displayField, v);
8739             if(r){
8740                 this.select(this.store.indexOf(r), scrollIntoView);
8741                 return true;
8742             }
8743         }
8744         return false;
8745     },
8746
8747     /**
8748      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8749      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8750      * @param {Number} index The zero-based index of the list item to select
8751      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8752      * selected item if it is not currently in view (defaults to true)
8753      */
8754     select : function(index, scrollIntoView){
8755         this.selectedIndex = index;
8756         this.view.select(index);
8757         if(scrollIntoView !== false){
8758             var el = this.view.getNode(index);
8759             if(el){
8760                 //this.innerList.scrollChildIntoView(el, false);
8761                 
8762             }
8763         }
8764     },
8765
8766     // private
8767     selectNext : function(){
8768         var ct = this.store.getCount();
8769         if(ct > 0){
8770             if(this.selectedIndex == -1){
8771                 this.select(0);
8772             }else if(this.selectedIndex < ct-1){
8773                 this.select(this.selectedIndex+1);
8774             }
8775         }
8776     },
8777
8778     // private
8779     selectPrev : function(){
8780         var ct = this.store.getCount();
8781         if(ct > 0){
8782             if(this.selectedIndex == -1){
8783                 this.select(0);
8784             }else if(this.selectedIndex != 0){
8785                 this.select(this.selectedIndex-1);
8786             }
8787         }
8788     },
8789
8790     // private
8791     onKeyUp : function(e){
8792         if(this.editable !== false && !e.isSpecialKey()){
8793             this.lastKey = e.getKey();
8794             this.dqTask.delay(this.queryDelay);
8795         }
8796     },
8797
8798     // private
8799     validateBlur : function(){
8800         return !this.list || !this.list.isVisible();   
8801     },
8802
8803     // private
8804     initQuery : function(){
8805         this.doQuery(this.getRawValue());
8806     },
8807
8808     // private
8809     doForce : function(){
8810         if(this.el.dom.value.length > 0){
8811             this.el.dom.value =
8812                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8813              
8814         }
8815     },
8816
8817     /**
8818      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8819      * query allowing the query action to be canceled if needed.
8820      * @param {String} query The SQL query to execute
8821      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8822      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8823      * saved in the current store (defaults to false)
8824      */
8825     doQuery : function(q, forceAll){
8826         
8827         if(q === undefined || q === null){
8828             q = '';
8829         }
8830         var qe = {
8831             query: q,
8832             forceAll: forceAll,
8833             combo: this,
8834             cancel:false
8835         };
8836         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8837             return false;
8838         }
8839         q = qe.query;
8840         
8841         forceAll = qe.forceAll;
8842         if(forceAll === true || (q.length >= this.minChars)){
8843             
8844             this.hasQuery = true;
8845             
8846             if(this.lastQuery != q || this.alwaysQuery){
8847                 this.lastQuery = q;
8848                 if(this.mode == 'local'){
8849                     this.selectedIndex = -1;
8850                     if(forceAll){
8851                         this.store.clearFilter();
8852                     }else{
8853                         this.store.filter(this.displayField, q);
8854                     }
8855                     this.onLoad();
8856                 }else{
8857                     this.store.baseParams[this.queryParam] = q;
8858                     
8859                     var options = {params : this.getParams(q)};
8860                     
8861                     if(this.loadNext){
8862                         options.add = true;
8863                         options.params.start = this.page * this.pageSize;
8864                     }
8865                     
8866                     this.store.load(options);
8867                     this.expand();
8868                 }
8869             }else{
8870                 this.selectedIndex = -1;
8871                 this.onLoad();   
8872             }
8873         }
8874         
8875         this.loadNext = false;
8876     },
8877
8878     // private
8879     getParams : function(q){
8880         var p = {};
8881         //p[this.queryParam] = q;
8882         
8883         if(this.pageSize){
8884             p.start = 0;
8885             p.limit = this.pageSize;
8886         }
8887         return p;
8888     },
8889
8890     /**
8891      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8892      */
8893     collapse : function(){
8894         if(!this.isExpanded()){
8895             return;
8896         }
8897         
8898         this.list.hide();
8899         Roo.get(document).un('mousedown', this.collapseIf, this);
8900         Roo.get(document).un('mousewheel', this.collapseIf, this);
8901         if (!this.editable) {
8902             Roo.get(document).un('keydown', this.listKeyPress, this);
8903         }
8904         this.fireEvent('collapse', this);
8905     },
8906
8907     // private
8908     collapseIf : function(e){
8909         var in_combo  = e.within(this.el);
8910         var in_list =  e.within(this.list);
8911         
8912         if (in_combo || in_list) {
8913             //e.stopPropagation();
8914             return;
8915         }
8916
8917         this.collapse();
8918         
8919     },
8920
8921     /**
8922      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8923      */
8924     expand : function(){
8925        
8926         if(this.isExpanded() || !this.hasFocus){
8927             return;
8928         }
8929          Roo.log('expand');
8930         this.list.alignTo(this.inputEl(), this.listAlign);
8931         this.list.show();
8932         Roo.get(document).on('mousedown', this.collapseIf, this);
8933         Roo.get(document).on('mousewheel', this.collapseIf, this);
8934         if (!this.editable) {
8935             Roo.get(document).on('keydown', this.listKeyPress, this);
8936         }
8937         
8938         this.fireEvent('expand', this);
8939     },
8940
8941     // private
8942     // Implements the default empty TriggerField.onTriggerClick function
8943     onTriggerClick : function()
8944     {
8945         Roo.log('trigger click');
8946         
8947         if(this.disabled){
8948             return;
8949         }
8950         
8951         this.page = 0;
8952         this.loadNext = false;
8953         
8954         if(this.isExpanded()){
8955             this.collapse();
8956             if (!this.blockFocus) {
8957                 this.inputEl().focus();
8958             }
8959             
8960         }else {
8961             this.hasFocus = true;
8962             if(this.triggerAction == 'all') {
8963                 this.doQuery(this.allQuery, true);
8964             } else {
8965                 this.doQuery(this.getRawValue());
8966             }
8967             if (!this.blockFocus) {
8968                 this.inputEl().focus();
8969             }
8970         }
8971     },
8972     listKeyPress : function(e)
8973     {
8974         //Roo.log('listkeypress');
8975         // scroll to first matching element based on key pres..
8976         if (e.isSpecialKey()) {
8977             return false;
8978         }
8979         var k = String.fromCharCode(e.getKey()).toUpperCase();
8980         //Roo.log(k);
8981         var match  = false;
8982         var csel = this.view.getSelectedNodes();
8983         var cselitem = false;
8984         if (csel.length) {
8985             var ix = this.view.indexOf(csel[0]);
8986             cselitem  = this.store.getAt(ix);
8987             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8988                 cselitem = false;
8989             }
8990             
8991         }
8992         
8993         this.store.each(function(v) { 
8994             if (cselitem) {
8995                 // start at existing selection.
8996                 if (cselitem.id == v.id) {
8997                     cselitem = false;
8998                 }
8999                 return true;
9000             }
9001                 
9002             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9003                 match = this.store.indexOf(v);
9004                 return false;
9005             }
9006             return true;
9007         }, this);
9008         
9009         if (match === false) {
9010             return true; // no more action?
9011         }
9012         // scroll to?
9013         this.view.select(match);
9014         var sn = Roo.get(this.view.getSelectedNodes()[0])
9015         //sn.scrollIntoView(sn.dom.parentNode, false);
9016     },
9017     
9018     onViewScroll : function(e, t){
9019         
9020         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9021             return;
9022         }
9023         
9024         this.hasQuery = true;
9025         
9026         this.loading = this.list.select('.loading', true).first();
9027         
9028         if(this.loading === null){
9029             this.list.createChild({
9030                 tag: 'div',
9031                 cls: 'loading select2-more-results select2-active',
9032                 html: 'Loading more results...'
9033             })
9034             
9035             this.loading = this.list.select('.loading', true).first();
9036             
9037             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9038             
9039             this.loading.hide();
9040         }
9041         
9042         this.loading.show();
9043         
9044         var _combo = this;
9045         
9046         this.page++;
9047         this.loadNext = true;
9048         
9049         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9050         
9051         return;
9052     },
9053     
9054     addItem : function(o)
9055     {   
9056         var dv = ''; // display value
9057         
9058         if (this.displayField) {
9059             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9060         } else {
9061             // this is an error condition!!!
9062             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9063         }
9064         
9065         if(!dv.length){
9066             return;
9067         }
9068         
9069         var choice = this.choices.createChild({
9070             tag: 'li',
9071             cls: 'select2-search-choice',
9072             cn: [
9073                 {
9074                     tag: 'div',
9075                     html: dv
9076                 },
9077                 {
9078                     tag: 'a',
9079                     href: '#',
9080                     cls: 'select2-search-choice-close',
9081                     tabindex: '-1'
9082                 }
9083             ]
9084             
9085         }, this.searchField);
9086         
9087         var close = choice.select('a.select2-search-choice-close', true).first()
9088         
9089         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9090         
9091         this.item.push(o);
9092         this.lastData = o;
9093         
9094         this.syncValue();
9095         
9096         this.inputEl().dom.value = '';
9097         
9098     },
9099     
9100     onRemoveItem : function(e, _self, o)
9101     {
9102         Roo.log('remove item');
9103         var index = this.item.indexOf(o.data) * 1;
9104         
9105         if( index < 0){
9106             Roo.log('not this item?!');
9107             return;
9108         }
9109         
9110         this.item.splice(index, 1);
9111         o.item.remove();
9112         
9113         this.syncValue();
9114         
9115         this.fireEvent('remove', this);
9116         
9117     },
9118     
9119     syncValue : function()
9120     {
9121         if(!this.item.length){
9122             this.clearValue();
9123             return;
9124         }
9125             
9126         var value = [];
9127         var _this = this;
9128         Roo.each(this.item, function(i){
9129             if(_this.valueField){
9130                 value.push(i[_this.valueField]);
9131                 return;
9132             }
9133
9134             value.push(i);
9135         });
9136
9137         this.value = value.join(',');
9138
9139         if(this.hiddenField){
9140             this.hiddenField.dom.value = this.value;
9141         }
9142     },
9143     
9144     clearItem : function()
9145     {
9146         if(!this.multiple){
9147             return;
9148         }
9149         
9150         this.item = [];
9151         
9152         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9153            c.remove();
9154         });
9155         
9156         this.syncValue();
9157     }
9158     
9159     
9160
9161     /** 
9162     * @cfg {Boolean} grow 
9163     * @hide 
9164     */
9165     /** 
9166     * @cfg {Number} growMin 
9167     * @hide 
9168     */
9169     /** 
9170     * @cfg {Number} growMax 
9171     * @hide 
9172     */
9173     /**
9174      * @hide
9175      * @method autoSize
9176      */
9177 });
9178 /*
9179  * Based on:
9180  * Ext JS Library 1.1.1
9181  * Copyright(c) 2006-2007, Ext JS, LLC.
9182  *
9183  * Originally Released Under LGPL - original licence link has changed is not relivant.
9184  *
9185  * Fork - LGPL
9186  * <script type="text/javascript">
9187  */
9188
9189 /**
9190  * @class Roo.View
9191  * @extends Roo.util.Observable
9192  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9193  * This class also supports single and multi selection modes. <br>
9194  * Create a data model bound view:
9195  <pre><code>
9196  var store = new Roo.data.Store(...);
9197
9198  var view = new Roo.View({
9199     el : "my-element",
9200     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9201  
9202     singleSelect: true,
9203     selectedClass: "ydataview-selected",
9204     store: store
9205  });
9206
9207  // listen for node click?
9208  view.on("click", function(vw, index, node, e){
9209  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9210  });
9211
9212  // load XML data
9213  dataModel.load("foobar.xml");
9214  </code></pre>
9215  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9216  * <br><br>
9217  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9218  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9219  * 
9220  * Note: old style constructor is still suported (container, template, config)
9221  * 
9222  * @constructor
9223  * Create a new View
9224  * @param {Object} config The config object
9225  * 
9226  */
9227 Roo.View = function(config, depreciated_tpl, depreciated_config){
9228     
9229     if (typeof(depreciated_tpl) == 'undefined') {
9230         // new way.. - universal constructor.
9231         Roo.apply(this, config);
9232         this.el  = Roo.get(this.el);
9233     } else {
9234         // old format..
9235         this.el  = Roo.get(config);
9236         this.tpl = depreciated_tpl;
9237         Roo.apply(this, depreciated_config);
9238     }
9239     this.wrapEl  = this.el.wrap().wrap();
9240     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9241     
9242     
9243     if(typeof(this.tpl) == "string"){
9244         this.tpl = new Roo.Template(this.tpl);
9245     } else {
9246         // support xtype ctors..
9247         this.tpl = new Roo.factory(this.tpl, Roo);
9248     }
9249     
9250     
9251     this.tpl.compile();
9252    
9253   
9254     
9255      
9256     /** @private */
9257     this.addEvents({
9258         /**
9259          * @event beforeclick
9260          * Fires before a click is processed. Returns false to cancel the default action.
9261          * @param {Roo.View} this
9262          * @param {Number} index The index of the target node
9263          * @param {HTMLElement} node The target node
9264          * @param {Roo.EventObject} e The raw event object
9265          */
9266             "beforeclick" : true,
9267         /**
9268          * @event click
9269          * Fires when a template node is clicked.
9270          * @param {Roo.View} this
9271          * @param {Number} index The index of the target node
9272          * @param {HTMLElement} node The target node
9273          * @param {Roo.EventObject} e The raw event object
9274          */
9275             "click" : true,
9276         /**
9277          * @event dblclick
9278          * Fires when a template node is double clicked.
9279          * @param {Roo.View} this
9280          * @param {Number} index The index of the target node
9281          * @param {HTMLElement} node The target node
9282          * @param {Roo.EventObject} e The raw event object
9283          */
9284             "dblclick" : true,
9285         /**
9286          * @event contextmenu
9287          * Fires when a template node is right clicked.
9288          * @param {Roo.View} this
9289          * @param {Number} index The index of the target node
9290          * @param {HTMLElement} node The target node
9291          * @param {Roo.EventObject} e The raw event object
9292          */
9293             "contextmenu" : true,
9294         /**
9295          * @event selectionchange
9296          * Fires when the selected nodes change.
9297          * @param {Roo.View} this
9298          * @param {Array} selections Array of the selected nodes
9299          */
9300             "selectionchange" : true,
9301     
9302         /**
9303          * @event beforeselect
9304          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9305          * @param {Roo.View} this
9306          * @param {HTMLElement} node The node to be selected
9307          * @param {Array} selections Array of currently selected nodes
9308          */
9309             "beforeselect" : true,
9310         /**
9311          * @event preparedata
9312          * Fires on every row to render, to allow you to change the data.
9313          * @param {Roo.View} this
9314          * @param {Object} data to be rendered (change this)
9315          */
9316           "preparedata" : true
9317           
9318           
9319         });
9320
9321
9322
9323     this.el.on({
9324         "click": this.onClick,
9325         "dblclick": this.onDblClick,
9326         "contextmenu": this.onContextMenu,
9327         scope:this
9328     });
9329
9330     this.selections = [];
9331     this.nodes = [];
9332     this.cmp = new Roo.CompositeElementLite([]);
9333     if(this.store){
9334         this.store = Roo.factory(this.store, Roo.data);
9335         this.setStore(this.store, true);
9336     }
9337     
9338     if ( this.footer && this.footer.xtype) {
9339            
9340          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9341         
9342         this.footer.dataSource = this.store
9343         this.footer.container = fctr;
9344         this.footer = Roo.factory(this.footer, Roo);
9345         fctr.insertFirst(this.el);
9346         
9347         // this is a bit insane - as the paging toolbar seems to detach the el..
9348 //        dom.parentNode.parentNode.parentNode
9349          // they get detached?
9350     }
9351     
9352     
9353     Roo.View.superclass.constructor.call(this);
9354     
9355     
9356 };
9357
9358 Roo.extend(Roo.View, Roo.util.Observable, {
9359     
9360      /**
9361      * @cfg {Roo.data.Store} store Data store to load data from.
9362      */
9363     store : false,
9364     
9365     /**
9366      * @cfg {String|Roo.Element} el The container element.
9367      */
9368     el : '',
9369     
9370     /**
9371      * @cfg {String|Roo.Template} tpl The template used by this View 
9372      */
9373     tpl : false,
9374     /**
9375      * @cfg {String} dataName the named area of the template to use as the data area
9376      *                          Works with domtemplates roo-name="name"
9377      */
9378     dataName: false,
9379     /**
9380      * @cfg {String} selectedClass The css class to add to selected nodes
9381      */
9382     selectedClass : "x-view-selected",
9383      /**
9384      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9385      */
9386     emptyText : "",
9387     
9388     /**
9389      * @cfg {String} text to display on mask (default Loading)
9390      */
9391     mask : false,
9392     /**
9393      * @cfg {Boolean} multiSelect Allow multiple selection
9394      */
9395     multiSelect : false,
9396     /**
9397      * @cfg {Boolean} singleSelect Allow single selection
9398      */
9399     singleSelect:  false,
9400     
9401     /**
9402      * @cfg {Boolean} toggleSelect - selecting 
9403      */
9404     toggleSelect : false,
9405     
9406     /**
9407      * Returns the element this view is bound to.
9408      * @return {Roo.Element}
9409      */
9410     getEl : function(){
9411         return this.wrapEl;
9412     },
9413     
9414     
9415
9416     /**
9417      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9418      */
9419     refresh : function(){
9420         Roo.log('refresh');
9421         var t = this.tpl;
9422         
9423         // if we are using something like 'domtemplate', then
9424         // the what gets used is:
9425         // t.applySubtemplate(NAME, data, wrapping data..)
9426         // the outer template then get' applied with
9427         //     the store 'extra data'
9428         // and the body get's added to the
9429         //      roo-name="data" node?
9430         //      <span class='roo-tpl-{name}'></span> ?????
9431         
9432         
9433         
9434         this.clearSelections();
9435         this.el.update("");
9436         var html = [];
9437         var records = this.store.getRange();
9438         if(records.length < 1) {
9439             
9440             // is this valid??  = should it render a template??
9441             
9442             this.el.update(this.emptyText);
9443             return;
9444         }
9445         var el = this.el;
9446         if (this.dataName) {
9447             this.el.update(t.apply(this.store.meta)); //????
9448             el = this.el.child('.roo-tpl-' + this.dataName);
9449         }
9450         
9451         for(var i = 0, len = records.length; i < len; i++){
9452             var data = this.prepareData(records[i].data, i, records[i]);
9453             this.fireEvent("preparedata", this, data, i, records[i]);
9454             html[html.length] = Roo.util.Format.trim(
9455                 this.dataName ?
9456                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9457                     t.apply(data)
9458             );
9459         }
9460         
9461         
9462         
9463         el.update(html.join(""));
9464         this.nodes = el.dom.childNodes;
9465         this.updateIndexes(0);
9466     },
9467     
9468
9469     /**
9470      * Function to override to reformat the data that is sent to
9471      * the template for each node.
9472      * DEPRICATED - use the preparedata event handler.
9473      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9474      * a JSON object for an UpdateManager bound view).
9475      */
9476     prepareData : function(data, index, record)
9477     {
9478         this.fireEvent("preparedata", this, data, index, record);
9479         return data;
9480     },
9481
9482     onUpdate : function(ds, record){
9483          Roo.log('on update');   
9484         this.clearSelections();
9485         var index = this.store.indexOf(record);
9486         var n = this.nodes[index];
9487         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9488         n.parentNode.removeChild(n);
9489         this.updateIndexes(index, index);
9490     },
9491
9492     
9493     
9494 // --------- FIXME     
9495     onAdd : function(ds, records, index)
9496     {
9497         Roo.log(['on Add', ds, records, index] );        
9498         this.clearSelections();
9499         if(this.nodes.length == 0){
9500             this.refresh();
9501             return;
9502         }
9503         var n = this.nodes[index];
9504         for(var i = 0, len = records.length; i < len; i++){
9505             var d = this.prepareData(records[i].data, i, records[i]);
9506             if(n){
9507                 this.tpl.insertBefore(n, d);
9508             }else{
9509                 
9510                 this.tpl.append(this.el, d);
9511             }
9512         }
9513         this.updateIndexes(index);
9514     },
9515
9516     onRemove : function(ds, record, index){
9517         Roo.log('onRemove');
9518         this.clearSelections();
9519         var el = this.dataName  ?
9520             this.el.child('.roo-tpl-' + this.dataName) :
9521             this.el; 
9522         
9523         el.dom.removeChild(this.nodes[index]);
9524         this.updateIndexes(index);
9525     },
9526
9527     /**
9528      * Refresh an individual node.
9529      * @param {Number} index
9530      */
9531     refreshNode : function(index){
9532         this.onUpdate(this.store, this.store.getAt(index));
9533     },
9534
9535     updateIndexes : function(startIndex, endIndex){
9536         var ns = this.nodes;
9537         startIndex = startIndex || 0;
9538         endIndex = endIndex || ns.length - 1;
9539         for(var i = startIndex; i <= endIndex; i++){
9540             ns[i].nodeIndex = i;
9541         }
9542     },
9543
9544     /**
9545      * Changes the data store this view uses and refresh the view.
9546      * @param {Store} store
9547      */
9548     setStore : function(store, initial){
9549         if(!initial && this.store){
9550             this.store.un("datachanged", this.refresh);
9551             this.store.un("add", this.onAdd);
9552             this.store.un("remove", this.onRemove);
9553             this.store.un("update", this.onUpdate);
9554             this.store.un("clear", this.refresh);
9555             this.store.un("beforeload", this.onBeforeLoad);
9556             this.store.un("load", this.onLoad);
9557             this.store.un("loadexception", this.onLoad);
9558         }
9559         if(store){
9560           
9561             store.on("datachanged", this.refresh, this);
9562             store.on("add", this.onAdd, this);
9563             store.on("remove", this.onRemove, this);
9564             store.on("update", this.onUpdate, this);
9565             store.on("clear", this.refresh, this);
9566             store.on("beforeload", this.onBeforeLoad, this);
9567             store.on("load", this.onLoad, this);
9568             store.on("loadexception", this.onLoad, this);
9569         }
9570         
9571         if(store){
9572             this.refresh();
9573         }
9574     },
9575     /**
9576      * onbeforeLoad - masks the loading area.
9577      *
9578      */
9579     onBeforeLoad : function(store,opts)
9580     {
9581          Roo.log('onBeforeLoad');   
9582         if (!opts.add) {
9583             this.el.update("");
9584         }
9585         this.el.mask(this.mask ? this.mask : "Loading" ); 
9586     },
9587     onLoad : function ()
9588     {
9589         this.el.unmask();
9590     },
9591     
9592
9593     /**
9594      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9595      * @param {HTMLElement} node
9596      * @return {HTMLElement} The template node
9597      */
9598     findItemFromChild : function(node){
9599         var el = this.dataName  ?
9600             this.el.child('.roo-tpl-' + this.dataName,true) :
9601             this.el.dom; 
9602         
9603         if(!node || node.parentNode == el){
9604                     return node;
9605             }
9606             var p = node.parentNode;
9607             while(p && p != el){
9608             if(p.parentNode == el){
9609                 return p;
9610             }
9611             p = p.parentNode;
9612         }
9613             return null;
9614     },
9615
9616     /** @ignore */
9617     onClick : function(e){
9618         var item = this.findItemFromChild(e.getTarget());
9619         if(item){
9620             var index = this.indexOf(item);
9621             if(this.onItemClick(item, index, e) !== false){
9622                 this.fireEvent("click", this, index, item, e);
9623             }
9624         }else{
9625             this.clearSelections();
9626         }
9627     },
9628
9629     /** @ignore */
9630     onContextMenu : function(e){
9631         var item = this.findItemFromChild(e.getTarget());
9632         if(item){
9633             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9634         }
9635     },
9636
9637     /** @ignore */
9638     onDblClick : function(e){
9639         var item = this.findItemFromChild(e.getTarget());
9640         if(item){
9641             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9642         }
9643     },
9644
9645     onItemClick : function(item, index, e)
9646     {
9647         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9648             return false;
9649         }
9650         if (this.toggleSelect) {
9651             var m = this.isSelected(item) ? 'unselect' : 'select';
9652             Roo.log(m);
9653             var _t = this;
9654             _t[m](item, true, false);
9655             return true;
9656         }
9657         if(this.multiSelect || this.singleSelect){
9658             if(this.multiSelect && e.shiftKey && this.lastSelection){
9659                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9660             }else{
9661                 this.select(item, this.multiSelect && e.ctrlKey);
9662                 this.lastSelection = item;
9663             }
9664             e.preventDefault();
9665         }
9666         return true;
9667     },
9668
9669     /**
9670      * Get the number of selected nodes.
9671      * @return {Number}
9672      */
9673     getSelectionCount : function(){
9674         return this.selections.length;
9675     },
9676
9677     /**
9678      * Get the currently selected nodes.
9679      * @return {Array} An array of HTMLElements
9680      */
9681     getSelectedNodes : function(){
9682         return this.selections;
9683     },
9684
9685     /**
9686      * Get the indexes of the selected nodes.
9687      * @return {Array}
9688      */
9689     getSelectedIndexes : function(){
9690         var indexes = [], s = this.selections;
9691         for(var i = 0, len = s.length; i < len; i++){
9692             indexes.push(s[i].nodeIndex);
9693         }
9694         return indexes;
9695     },
9696
9697     /**
9698      * Clear all selections
9699      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9700      */
9701     clearSelections : function(suppressEvent){
9702         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9703             this.cmp.elements = this.selections;
9704             this.cmp.removeClass(this.selectedClass);
9705             this.selections = [];
9706             if(!suppressEvent){
9707                 this.fireEvent("selectionchange", this, this.selections);
9708             }
9709         }
9710     },
9711
9712     /**
9713      * Returns true if the passed node is selected
9714      * @param {HTMLElement/Number} node The node or node index
9715      * @return {Boolean}
9716      */
9717     isSelected : function(node){
9718         var s = this.selections;
9719         if(s.length < 1){
9720             return false;
9721         }
9722         node = this.getNode(node);
9723         return s.indexOf(node) !== -1;
9724     },
9725
9726     /**
9727      * Selects nodes.
9728      * @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
9729      * @param {Boolean} keepExisting (optional) true to keep existing selections
9730      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9731      */
9732     select : function(nodeInfo, keepExisting, suppressEvent){
9733         if(nodeInfo instanceof Array){
9734             if(!keepExisting){
9735                 this.clearSelections(true);
9736             }
9737             for(var i = 0, len = nodeInfo.length; i < len; i++){
9738                 this.select(nodeInfo[i], true, true);
9739             }
9740             return;
9741         } 
9742         var node = this.getNode(nodeInfo);
9743         if(!node || this.isSelected(node)){
9744             return; // already selected.
9745         }
9746         if(!keepExisting){
9747             this.clearSelections(true);
9748         }
9749         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9750             Roo.fly(node).addClass(this.selectedClass);
9751             this.selections.push(node);
9752             if(!suppressEvent){
9753                 this.fireEvent("selectionchange", this, this.selections);
9754             }
9755         }
9756         
9757         
9758     },
9759       /**
9760      * Unselects nodes.
9761      * @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
9762      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9763      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9764      */
9765     unselect : function(nodeInfo, keepExisting, suppressEvent)
9766     {
9767         if(nodeInfo instanceof Array){
9768             Roo.each(this.selections, function(s) {
9769                 this.unselect(s, nodeInfo);
9770             }, this);
9771             return;
9772         }
9773         var node = this.getNode(nodeInfo);
9774         if(!node || !this.isSelected(node)){
9775             Roo.log("not selected");
9776             return; // not selected.
9777         }
9778         // fireevent???
9779         var ns = [];
9780         Roo.each(this.selections, function(s) {
9781             if (s == node ) {
9782                 Roo.fly(node).removeClass(this.selectedClass);
9783
9784                 return;
9785             }
9786             ns.push(s);
9787         },this);
9788         
9789         this.selections= ns;
9790         this.fireEvent("selectionchange", this, this.selections);
9791     },
9792
9793     /**
9794      * Gets a template node.
9795      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9796      * @return {HTMLElement} The node or null if it wasn't found
9797      */
9798     getNode : function(nodeInfo){
9799         if(typeof nodeInfo == "string"){
9800             return document.getElementById(nodeInfo);
9801         }else if(typeof nodeInfo == "number"){
9802             return this.nodes[nodeInfo];
9803         }
9804         return nodeInfo;
9805     },
9806
9807     /**
9808      * Gets a range template nodes.
9809      * @param {Number} startIndex
9810      * @param {Number} endIndex
9811      * @return {Array} An array of nodes
9812      */
9813     getNodes : function(start, end){
9814         var ns = this.nodes;
9815         start = start || 0;
9816         end = typeof end == "undefined" ? ns.length - 1 : end;
9817         var nodes = [];
9818         if(start <= end){
9819             for(var i = start; i <= end; i++){
9820                 nodes.push(ns[i]);
9821             }
9822         } else{
9823             for(var i = start; i >= end; i--){
9824                 nodes.push(ns[i]);
9825             }
9826         }
9827         return nodes;
9828     },
9829
9830     /**
9831      * Finds the index of the passed node
9832      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9833      * @return {Number} The index of the node or -1
9834      */
9835     indexOf : function(node){
9836         node = this.getNode(node);
9837         if(typeof node.nodeIndex == "number"){
9838             return node.nodeIndex;
9839         }
9840         var ns = this.nodes;
9841         for(var i = 0, len = ns.length; i < len; i++){
9842             if(ns[i] == node){
9843                 return i;
9844             }
9845         }
9846         return -1;
9847     }
9848 });
9849 /*
9850  * - LGPL
9851  *
9852  * based on jquery fullcalendar
9853  * 
9854  */
9855
9856 Roo.bootstrap = Roo.bootstrap || {};
9857 /**
9858  * @class Roo.bootstrap.Calendar
9859  * @extends Roo.bootstrap.Component
9860  * Bootstrap Calendar class
9861  * @cfg {Boolean} loadMask (true|false) default false
9862     
9863  * @constructor
9864  * Create a new Container
9865  * @param {Object} config The config object
9866  */
9867
9868
9869
9870 Roo.bootstrap.Calendar = function(config){
9871     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9872      this.addEvents({
9873         /**
9874              * @event select
9875              * Fires when a date is selected
9876              * @param {DatePicker} this
9877              * @param {Date} date The selected date
9878              */
9879         'select': true,
9880         /**
9881              * @event monthchange
9882              * Fires when the displayed month changes 
9883              * @param {DatePicker} this
9884              * @param {Date} date The selected month
9885              */
9886         'monthchange': true,
9887         /**
9888              * @event evententer
9889              * Fires when mouse over an event
9890              * @param {Calendar} this
9891              * @param {event} Event
9892              */
9893         'evententer': true,
9894         /**
9895              * @event eventleave
9896              * Fires when the mouse leaves an
9897              * @param {Calendar} this
9898              * @param {event}
9899              */
9900         'eventleave': true,
9901         /**
9902              * @event eventclick
9903              * Fires when the mouse click an
9904              * @param {Calendar} this
9905              * @param {event}
9906              */
9907         'eventclick': true
9908         
9909     });
9910
9911 };
9912
9913 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9914     
9915      /**
9916      * @cfg {Number} startDay
9917      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9918      */
9919     startDay : 0,
9920     
9921     loadMask : false,
9922       
9923     getAutoCreate : function(){
9924         
9925         
9926         var fc_button = function(name, corner, style, content ) {
9927             return Roo.apply({},{
9928                 tag : 'span',
9929                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9930                          (corner.length ?
9931                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9932                             ''
9933                         ),
9934                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9935                 unselectable: 'on'
9936             });
9937         };
9938         
9939         var header = {
9940             tag : 'table',
9941             cls : 'fc-header',
9942             style : 'width:100%',
9943             cn : [
9944                 {
9945                     tag: 'tr',
9946                     cn : [
9947                         {
9948                             tag : 'td',
9949                             cls : 'fc-header-left',
9950                             cn : [
9951                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9952                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9953                                 { tag: 'span', cls: 'fc-header-space' },
9954                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9955                                 
9956                                 
9957                             ]
9958                         },
9959                         
9960                         {
9961                             tag : 'td',
9962                             cls : 'fc-header-center',
9963                             cn : [
9964                                 {
9965                                     tag: 'span',
9966                                     cls: 'fc-header-title',
9967                                     cn : {
9968                                         tag: 'H2',
9969                                         html : 'month / year'
9970                                     }
9971                                 }
9972                                 
9973                             ]
9974                         },
9975                         {
9976                             tag : 'td',
9977                             cls : 'fc-header-right',
9978                             cn : [
9979                           /*      fc_button('month', 'left', '', 'month' ),
9980                                 fc_button('week', '', '', 'week' ),
9981                                 fc_button('day', 'right', '', 'day' )
9982                             */    
9983                                 
9984                             ]
9985                         }
9986                         
9987                     ]
9988                 }
9989             ]
9990         };
9991         
9992        
9993         var cal_heads = function() {
9994             var ret = [];
9995             // fixme - handle this.
9996             
9997             for (var i =0; i < Date.dayNames.length; i++) {
9998                 var d = Date.dayNames[i];
9999                 ret.push({
10000                     tag: 'th',
10001                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10002                     html : d.substring(0,3)
10003                 });
10004                 
10005             }
10006             ret[0].cls += ' fc-first';
10007             ret[6].cls += ' fc-last';
10008             return ret;
10009         };
10010         var cal_cell = function(n) {
10011             return  {
10012                 tag: 'td',
10013                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10014                 cn : [
10015                     {
10016                         cn : [
10017                             {
10018                                 cls: 'fc-day-number',
10019                                 html: 'D'
10020                             },
10021                             {
10022                                 cls: 'fc-day-content',
10023                              
10024                                 cn : [
10025                                      {
10026                                         style: 'position: relative;' // height: 17px;
10027                                     }
10028                                 ]
10029                             }
10030                             
10031                             
10032                         ]
10033                     }
10034                 ]
10035                 
10036             }
10037         };
10038         var cal_rows = function() {
10039             
10040             var ret = []
10041             for (var r = 0; r < 6; r++) {
10042                 var row= {
10043                     tag : 'tr',
10044                     cls : 'fc-week',
10045                     cn : []
10046                 };
10047                 
10048                 for (var i =0; i < Date.dayNames.length; i++) {
10049                     var d = Date.dayNames[i];
10050                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10051
10052                 }
10053                 row.cn[0].cls+=' fc-first';
10054                 row.cn[0].cn[0].style = 'min-height:90px';
10055                 row.cn[6].cls+=' fc-last';
10056                 ret.push(row);
10057                 
10058             }
10059             ret[0].cls += ' fc-first';
10060             ret[4].cls += ' fc-prev-last';
10061             ret[5].cls += ' fc-last';
10062             return ret;
10063             
10064         };
10065         
10066         var cal_table = {
10067             tag: 'table',
10068             cls: 'fc-border-separate',
10069             style : 'width:100%',
10070             cellspacing  : 0,
10071             cn : [
10072                 { 
10073                     tag: 'thead',
10074                     cn : [
10075                         { 
10076                             tag: 'tr',
10077                             cls : 'fc-first fc-last',
10078                             cn : cal_heads()
10079                         }
10080                     ]
10081                 },
10082                 { 
10083                     tag: 'tbody',
10084                     cn : cal_rows()
10085                 }
10086                   
10087             ]
10088         };
10089          
10090          var cfg = {
10091             cls : 'fc fc-ltr',
10092             cn : [
10093                 header,
10094                 {
10095                     cls : 'fc-content',
10096                     style : "position: relative;",
10097                     cn : [
10098                         {
10099                             cls : 'fc-view fc-view-month fc-grid',
10100                             style : 'position: relative',
10101                             unselectable : 'on',
10102                             cn : [
10103                                 {
10104                                     cls : 'fc-event-container',
10105                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10106                                 },
10107                                 cal_table
10108                             ]
10109                         }
10110                     ]
10111     
10112                 }
10113            ] 
10114             
10115         };
10116         
10117          
10118         
10119         return cfg;
10120     },
10121     
10122     
10123     initEvents : function()
10124     {
10125         if(!this.store){
10126             throw "can not find store for calendar";
10127         }
10128         
10129         var mark = {
10130             tag: "div",
10131             cls:"x-dlg-mask",
10132             style: "text-align:center",
10133             cn: [
10134                 {
10135                     tag: "div",
10136                     style: "background-color:white;width:50%;margin:250 auto",
10137                     cn: [
10138                         {
10139                             tag: "img",
10140                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10141                         },
10142                         {
10143                             tag: "span",
10144                             html: "Loading"
10145                         }
10146                         
10147                     ]
10148                 }
10149             ]
10150         }
10151         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10152         
10153         var size = this.el.select('.fc-content', true).first().getSize();
10154         this.maskEl.setSize(size.width, size.height);
10155         this.maskEl.enableDisplayMode("block");
10156         if(!this.loadMask){
10157             this.maskEl.hide();
10158         }
10159         
10160         this.store = Roo.factory(this.store, Roo.data);
10161         this.store.on('load', this.onLoad, this);
10162         this.store.on('beforeload', this.onBeforeLoad, this);
10163         
10164         this.resize();
10165         
10166         this.cells = this.el.select('.fc-day',true);
10167         //Roo.log(this.cells);
10168         this.textNodes = this.el.query('.fc-day-number');
10169         this.cells.addClassOnOver('fc-state-hover');
10170         
10171         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10172         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10173         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10174         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10175         
10176         this.on('monthchange', this.onMonthChange, this);
10177         
10178         this.update(new Date().clearTime());
10179     },
10180     
10181     resize : function() {
10182         var sz  = this.el.getSize();
10183         
10184         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10185         this.el.select('.fc-day-content div',true).setHeight(34);
10186     },
10187     
10188     
10189     // private
10190     showPrevMonth : function(e){
10191         this.update(this.activeDate.add("mo", -1));
10192     },
10193     showToday : function(e){
10194         this.update(new Date().clearTime());
10195     },
10196     // private
10197     showNextMonth : function(e){
10198         this.update(this.activeDate.add("mo", 1));
10199     },
10200
10201     // private
10202     showPrevYear : function(){
10203         this.update(this.activeDate.add("y", -1));
10204     },
10205
10206     // private
10207     showNextYear : function(){
10208         this.update(this.activeDate.add("y", 1));
10209     },
10210
10211     
10212    // private
10213     update : function(date)
10214     {
10215         var vd = this.activeDate;
10216         this.activeDate = date;
10217 //        if(vd && this.el){
10218 //            var t = date.getTime();
10219 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10220 //                Roo.log('using add remove');
10221 //                
10222 //                this.fireEvent('monthchange', this, date);
10223 //                
10224 //                this.cells.removeClass("fc-state-highlight");
10225 //                this.cells.each(function(c){
10226 //                   if(c.dateValue == t){
10227 //                       c.addClass("fc-state-highlight");
10228 //                       setTimeout(function(){
10229 //                            try{c.dom.firstChild.focus();}catch(e){}
10230 //                       }, 50);
10231 //                       return false;
10232 //                   }
10233 //                   return true;
10234 //                });
10235 //                return;
10236 //            }
10237 //        }
10238         
10239         var days = date.getDaysInMonth();
10240         
10241         var firstOfMonth = date.getFirstDateOfMonth();
10242         var startingPos = firstOfMonth.getDay()-this.startDay;
10243         
10244         if(startingPos < this.startDay){
10245             startingPos += 7;
10246         }
10247         
10248         var pm = date.add(Date.MONTH, -1);
10249         var prevStart = pm.getDaysInMonth()-startingPos;
10250 //        
10251         this.cells = this.el.select('.fc-day',true);
10252         this.textNodes = this.el.query('.fc-day-number');
10253         this.cells.addClassOnOver('fc-state-hover');
10254         
10255         var cells = this.cells.elements;
10256         var textEls = this.textNodes;
10257         
10258         Roo.each(cells, function(cell){
10259             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10260         });
10261         
10262         days += startingPos;
10263
10264         // convert everything to numbers so it's fast
10265         var day = 86400000;
10266         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10267         //Roo.log(d);
10268         //Roo.log(pm);
10269         //Roo.log(prevStart);
10270         
10271         var today = new Date().clearTime().getTime();
10272         var sel = date.clearTime().getTime();
10273         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10274         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10275         var ddMatch = this.disabledDatesRE;
10276         var ddText = this.disabledDatesText;
10277         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10278         var ddaysText = this.disabledDaysText;
10279         var format = this.format;
10280         
10281         var setCellClass = function(cal, cell){
10282             
10283             //Roo.log('set Cell Class');
10284             cell.title = "";
10285             var t = d.getTime();
10286             
10287             //Roo.log(d);
10288             
10289             cell.dateValue = t;
10290             if(t == today){
10291                 cell.className += " fc-today";
10292                 cell.className += " fc-state-highlight";
10293                 cell.title = cal.todayText;
10294             }
10295             if(t == sel){
10296                 // disable highlight in other month..
10297                 //cell.className += " fc-state-highlight";
10298                 
10299             }
10300             // disabling
10301             if(t < min) {
10302                 cell.className = " fc-state-disabled";
10303                 cell.title = cal.minText;
10304                 return;
10305             }
10306             if(t > max) {
10307                 cell.className = " fc-state-disabled";
10308                 cell.title = cal.maxText;
10309                 return;
10310             }
10311             if(ddays){
10312                 if(ddays.indexOf(d.getDay()) != -1){
10313                     cell.title = ddaysText;
10314                     cell.className = " fc-state-disabled";
10315                 }
10316             }
10317             if(ddMatch && format){
10318                 var fvalue = d.dateFormat(format);
10319                 if(ddMatch.test(fvalue)){
10320                     cell.title = ddText.replace("%0", fvalue);
10321                     cell.className = " fc-state-disabled";
10322                 }
10323             }
10324             
10325             if (!cell.initialClassName) {
10326                 cell.initialClassName = cell.dom.className;
10327             }
10328             
10329             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10330         };
10331
10332         var i = 0;
10333         
10334         for(; i < startingPos; i++) {
10335             textEls[i].innerHTML = (++prevStart);
10336             d.setDate(d.getDate()+1);
10337             
10338             cells[i].className = "fc-past fc-other-month";
10339             setCellClass(this, cells[i]);
10340         }
10341         
10342         var intDay = 0;
10343         
10344         for(; i < days; i++){
10345             intDay = i - startingPos + 1;
10346             textEls[i].innerHTML = (intDay);
10347             d.setDate(d.getDate()+1);
10348             
10349             cells[i].className = ''; // "x-date-active";
10350             setCellClass(this, cells[i]);
10351         }
10352         var extraDays = 0;
10353         
10354         for(; i < 42; i++) {
10355             textEls[i].innerHTML = (++extraDays);
10356             d.setDate(d.getDate()+1);
10357             
10358             cells[i].className = "fc-future fc-other-month";
10359             setCellClass(this, cells[i]);
10360         }
10361         
10362         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10363         
10364         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10365         
10366         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10367         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10368         
10369         if(totalRows != 6){
10370             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10371             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10372         }
10373         
10374         this.fireEvent('monthchange', this, date);
10375         
10376         
10377         /*
10378         if(!this.internalRender){
10379             var main = this.el.dom.firstChild;
10380             var w = main.offsetWidth;
10381             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10382             Roo.fly(main).setWidth(w);
10383             this.internalRender = true;
10384             // opera does not respect the auto grow header center column
10385             // then, after it gets a width opera refuses to recalculate
10386             // without a second pass
10387             if(Roo.isOpera && !this.secondPass){
10388                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10389                 this.secondPass = true;
10390                 this.update.defer(10, this, [date]);
10391             }
10392         }
10393         */
10394         
10395     },
10396     
10397     findCell : function(dt) {
10398         dt = dt.clearTime().getTime();
10399         var ret = false;
10400         this.cells.each(function(c){
10401             //Roo.log("check " +c.dateValue + '?=' + dt);
10402             if(c.dateValue == dt){
10403                 ret = c;
10404                 return false;
10405             }
10406             return true;
10407         });
10408         
10409         return ret;
10410     },
10411     
10412     findCells : function(ev) {
10413         var s = ev.start.clone().clearTime().getTime();
10414        // Roo.log(s);
10415         var e= ev.end.clone().clearTime().getTime();
10416        // Roo.log(e);
10417         var ret = [];
10418         this.cells.each(function(c){
10419              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10420             
10421             if(c.dateValue > e){
10422                 return ;
10423             }
10424             if(c.dateValue < s){
10425                 return ;
10426             }
10427             ret.push(c);
10428         });
10429         
10430         return ret;    
10431     },
10432     
10433     findBestRow: function(cells)
10434     {
10435         var ret = 0;
10436         
10437         for (var i =0 ; i < cells.length;i++) {
10438             ret  = Math.max(cells[i].rows || 0,ret);
10439         }
10440         return ret;
10441         
10442     },
10443     
10444     
10445     addItem : function(ev)
10446     {
10447         // look for vertical location slot in
10448         var cells = this.findCells(ev);
10449         
10450         ev.row = this.findBestRow(cells);
10451         
10452         // work out the location.
10453         
10454         var crow = false;
10455         var rows = [];
10456         for(var i =0; i < cells.length; i++) {
10457             if (!crow) {
10458                 crow = {
10459                     start : cells[i],
10460                     end :  cells[i]
10461                 };
10462                 continue;
10463             }
10464             if (crow.start.getY() == cells[i].getY()) {
10465                 // on same row.
10466                 crow.end = cells[i];
10467                 continue;
10468             }
10469             // different row.
10470             rows.push(crow);
10471             crow = {
10472                 start: cells[i],
10473                 end : cells[i]
10474             };
10475             
10476         }
10477         
10478         rows.push(crow);
10479         ev.els = [];
10480         ev.rows = rows;
10481         ev.cells = cells;
10482         for (var i = 0; i < cells.length;i++) {
10483             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10484             
10485         }
10486         
10487         this.calevents.push(ev);
10488     },
10489     
10490     clearEvents: function() {
10491         
10492         if(!this.calevents){
10493             return;
10494         }
10495         
10496         Roo.each(this.cells.elements, function(c){
10497             c.rows = 0;
10498         });
10499         
10500         Roo.each(this.calevents, function(e) {
10501             Roo.each(e.els, function(el) {
10502                 el.un('mouseenter' ,this.onEventEnter, this);
10503                 el.un('mouseleave' ,this.onEventLeave, this);
10504                 el.remove();
10505             },this);
10506         },this);
10507         
10508     },
10509     
10510     renderEvents: function()
10511     {   
10512         // first make sure there is enough space..
10513         
10514         this.cells.each(function(c) {
10515         
10516             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10517         });
10518         
10519         for (var e = 0; e < this.calevents.length; e++) {
10520             var ev = this.calevents[e];
10521             var cells = ev.cells;
10522             var rows = ev.rows;
10523             
10524             for(var i =0; i < rows.length; i++) {
10525                 
10526                  
10527                 // how many rows should it span..
10528                 
10529                 var  cfg = {
10530                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10531                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10532                     
10533                     unselectable : "on",
10534                     cn : [
10535                         {
10536                             cls: 'fc-event-inner',
10537                             cn : [
10538 //                                {
10539 //                                  tag:'span',
10540 //                                  cls: 'fc-event-time',
10541 //                                  html : cells.length > 1 ? '' : ev.time
10542 //                                },
10543                                 {
10544                                   tag:'span',
10545                                   cls: 'fc-event-title',
10546                                   html : String.format('{0}', ev.title)
10547                                 }
10548                                 
10549                                 
10550                             ]
10551                         },
10552                         {
10553                             cls: 'ui-resizable-handle ui-resizable-e',
10554                             html : '&nbsp;&nbsp;&nbsp'
10555                         }
10556                         
10557                     ]
10558                 };
10559                 if (i == 0) {
10560                     cfg.cls += ' fc-event-start';
10561                 }
10562                 if ((i+1) == rows.length) {
10563                     cfg.cls += ' fc-event-end';
10564                 }
10565                 
10566                 var ctr = this.el.select('.fc-event-container',true).first();
10567                 var cg = ctr.createChild(cfg);
10568                 
10569                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10570                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10571                 cg.on('click', this.onEventClick, this, ev);
10572                 
10573                 ev.els.push(cg);
10574                 
10575                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10576                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10577                 //Roo.log(cg);
10578                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10579                 cg.setWidth(ebox.right - sbox.x -2);
10580             }
10581             
10582             
10583         }
10584         
10585     },
10586     
10587     onEventEnter: function (e, el,event,d) {
10588         this.fireEvent('evententer', this, el, event);
10589     },
10590     
10591     onEventLeave: function (e, el,event,d) {
10592         this.fireEvent('eventleave', this, el, event);
10593     },
10594     
10595     onEventClick: function (e, el,event,d) {
10596         this.fireEvent('eventclick', this, el, event);
10597     },
10598     
10599     onMonthChange: function () {
10600         this.store.load();
10601     },
10602     
10603     onLoad: function () 
10604     {   
10605         this.calevents = [];
10606         var cal = this;
10607         
10608         if(this.store.getCount() > 0){
10609             this.store.data.each(function(d){
10610                cal.addItem({
10611                     id : d.data.id,
10612                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10613                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10614                     time : d.data.start_time,
10615                     title : d.data.title,
10616                     description : d.data.description,
10617                     venue : d.data.venue
10618                 });
10619             });
10620         }
10621         
10622         this.renderEvents();
10623         
10624         if(this.loadMask){
10625             this.maskEl.hide();
10626         }
10627     },
10628     
10629     onBeforeLoad: function()
10630     {
10631         this.clearEvents();
10632         
10633         if(this.loadMask){
10634             this.maskEl.show();
10635         }
10636     }
10637 });
10638
10639  
10640  /*
10641  * - LGPL
10642  *
10643  * element
10644  * 
10645  */
10646
10647 /**
10648  * @class Roo.bootstrap.Popover
10649  * @extends Roo.bootstrap.Component
10650  * Bootstrap Popover class
10651  * @cfg {String} html contents of the popover   (or false to use children..)
10652  * @cfg {String} title of popover (or false to hide)
10653  * @cfg {String} placement how it is placed
10654  * @cfg {String} trigger click || hover (or false to trigger manually)
10655  * @cfg {String} over what (parent or false to trigger manually.)
10656  * 
10657  * @constructor
10658  * Create a new Popover
10659  * @param {Object} config The config object
10660  */
10661
10662 Roo.bootstrap.Popover = function(config){
10663     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10664 };
10665
10666 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10667     
10668     title: 'Fill in a title',
10669     html: false,
10670     
10671     placement : 'right',
10672     trigger : 'hover', // hover
10673     
10674     over: 'parent',
10675     
10676     can_build_overlaid : false,
10677     
10678     getChildContainer : function()
10679     {
10680         return this.el.select('.popover-content',true).first();
10681     },
10682     
10683     getAutoCreate : function(){
10684          Roo.log('make popover?');
10685         var cfg = {
10686            cls : 'popover roo-dynamic',
10687            style: 'display:block',
10688            cn : [
10689                 {
10690                     cls : 'arrow'
10691                 },
10692                 {
10693                     cls : 'popover-inner',
10694                     cn : [
10695                         {
10696                             tag: 'h3',
10697                             cls: 'popover-title',
10698                             html : this.title
10699                         },
10700                         {
10701                             cls : 'popover-content',
10702                             html : this.html
10703                         }
10704                     ]
10705                     
10706                 }
10707            ]
10708         };
10709         
10710         return cfg;
10711     },
10712     setTitle: function(str)
10713     {
10714         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10715     },
10716     setContent: function(str)
10717     {
10718         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10719     },
10720     // as it get's added to the bottom of the page.
10721     onRender : function(ct, position)
10722     {
10723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10724         if(!this.el){
10725             var cfg = Roo.apply({},  this.getAutoCreate());
10726             cfg.id = Roo.id();
10727             
10728             if (this.cls) {
10729                 cfg.cls += ' ' + this.cls;
10730             }
10731             if (this.style) {
10732                 cfg.style = this.style;
10733             }
10734             Roo.log("adding to ")
10735             this.el = Roo.get(document.body).createChild(cfg, position);
10736             Roo.log(this.el);
10737         }
10738         this.initEvents();
10739     },
10740     
10741     initEvents : function()
10742     {
10743         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10744         this.el.enableDisplayMode('block');
10745         this.el.hide();
10746         if (this.over === false) {
10747             return; 
10748         }
10749         if (this.triggers === false) {
10750             return;
10751         }
10752         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10753         var triggers = this.trigger ? this.trigger.split(' ') : [];
10754         Roo.each(triggers, function(trigger) {
10755         
10756             if (trigger == 'click') {
10757                 on_el.on('click', this.toggle, this);
10758             } else if (trigger != 'manual') {
10759                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10760                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10761       
10762                 on_el.on(eventIn  ,this.enter, this);
10763                 on_el.on(eventOut, this.leave, this);
10764             }
10765         }, this);
10766         
10767     },
10768     
10769     
10770     // private
10771     timeout : null,
10772     hoverState : null,
10773     
10774     toggle : function () {
10775         this.hoverState == 'in' ? this.leave() : this.enter();
10776     },
10777     
10778     enter : function () {
10779        
10780     
10781         clearTimeout(this.timeout);
10782     
10783         this.hoverState = 'in'
10784     
10785         if (!this.delay || !this.delay.show) {
10786             this.show();
10787             return 
10788         }
10789         var _t = this;
10790         this.timeout = setTimeout(function () {
10791             if (_t.hoverState == 'in') {
10792                 _t.show();
10793             }
10794         }, this.delay.show)
10795     },
10796     leave : function() {
10797         clearTimeout(this.timeout);
10798     
10799         this.hoverState = 'out'
10800     
10801         if (!this.delay || !this.delay.hide) {
10802             this.hide();
10803             return 
10804         }
10805         var _t = this;
10806         this.timeout = setTimeout(function () {
10807             if (_t.hoverState == 'out') {
10808                 _t.hide();
10809             }
10810         }, this.delay.hide)
10811     },
10812     
10813     show : function (on_el)
10814     {
10815         if (!on_el) {
10816             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10817         }
10818         // set content.
10819         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10820         if (this.html !== false) {
10821             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10822         }
10823         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10824         if (!this.title.length) {
10825             this.el.select('.popover-title',true).hide();
10826         }
10827         
10828         var placement = typeof this.placement == 'function' ?
10829             this.placement.call(this, this.el, on_el) :
10830             this.placement;
10831             
10832         var autoToken = /\s?auto?\s?/i;
10833         var autoPlace = autoToken.test(placement);
10834         if (autoPlace) {
10835             placement = placement.replace(autoToken, '') || 'top';
10836         }
10837         
10838         //this.el.detach()
10839         //this.el.setXY([0,0]);
10840         this.el.show();
10841         this.el.dom.style.display='block';
10842         this.el.addClass(placement);
10843         
10844         //this.el.appendTo(on_el);
10845         
10846         var p = this.getPosition();
10847         var box = this.el.getBox();
10848         
10849         if (autoPlace) {
10850             // fixme..
10851         }
10852         var align = Roo.bootstrap.Popover.alignment[placement]
10853         this.el.alignTo(on_el, align[0],align[1]);
10854         //var arrow = this.el.select('.arrow',true).first();
10855         //arrow.set(align[2], 
10856         
10857         this.el.addClass('in');
10858         this.hoverState = null;
10859         
10860         if (this.el.hasClass('fade')) {
10861             // fade it?
10862         }
10863         
10864     },
10865     hide : function()
10866     {
10867         this.el.setXY([0,0]);
10868         this.el.removeClass('in');
10869         this.el.hide();
10870         
10871     }
10872     
10873 });
10874
10875 Roo.bootstrap.Popover.alignment = {
10876     'left' : ['r-l', [-10,0], 'right'],
10877     'right' : ['l-r', [10,0], 'left'],
10878     'bottom' : ['t-b', [0,10], 'top'],
10879     'top' : [ 'b-t', [0,-10], 'bottom']
10880 };
10881
10882  /*
10883  * - LGPL
10884  *
10885  * Progress
10886  * 
10887  */
10888
10889 /**
10890  * @class Roo.bootstrap.Progress
10891  * @extends Roo.bootstrap.Component
10892  * Bootstrap Progress class
10893  * @cfg {Boolean} striped striped of the progress bar
10894  * @cfg {Boolean} active animated of the progress bar
10895  * 
10896  * 
10897  * @constructor
10898  * Create a new Progress
10899  * @param {Object} config The config object
10900  */
10901
10902 Roo.bootstrap.Progress = function(config){
10903     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10904 };
10905
10906 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10907     
10908     striped : false,
10909     active: false,
10910     
10911     getAutoCreate : function(){
10912         var cfg = {
10913             tag: 'div',
10914             cls: 'progress'
10915         };
10916         
10917         
10918         if(this.striped){
10919             cfg.cls += ' progress-striped';
10920         }
10921       
10922         if(this.active){
10923             cfg.cls += ' active';
10924         }
10925         
10926         
10927         return cfg;
10928     }
10929    
10930 });
10931
10932  
10933
10934  /*
10935  * - LGPL
10936  *
10937  * ProgressBar
10938  * 
10939  */
10940
10941 /**
10942  * @class Roo.bootstrap.ProgressBar
10943  * @extends Roo.bootstrap.Component
10944  * Bootstrap ProgressBar class
10945  * @cfg {Number} aria_valuenow aria-value now
10946  * @cfg {Number} aria_valuemin aria-value min
10947  * @cfg {Number} aria_valuemax aria-value max
10948  * @cfg {String} label label for the progress bar
10949  * @cfg {String} panel (success | info | warning | danger )
10950  * @cfg {String} role role of the progress bar
10951  * @cfg {String} sr_only text
10952  * 
10953  * 
10954  * @constructor
10955  * Create a new ProgressBar
10956  * @param {Object} config The config object
10957  */
10958
10959 Roo.bootstrap.ProgressBar = function(config){
10960     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10961 };
10962
10963 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10964     
10965     aria_valuenow : 0,
10966     aria_valuemin : 0,
10967     aria_valuemax : 100,
10968     label : false,
10969     panel : false,
10970     role : false,
10971     sr_only: false,
10972     
10973     getAutoCreate : function()
10974     {
10975         
10976         var cfg = {
10977             tag: 'div',
10978             cls: 'progress-bar',
10979             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10980         };
10981         
10982         if(this.sr_only){
10983             cfg.cn = {
10984                 tag: 'span',
10985                 cls: 'sr-only',
10986                 html: this.sr_only
10987             }
10988         }
10989         
10990         if(this.role){
10991             cfg.role = this.role;
10992         }
10993         
10994         if(this.aria_valuenow){
10995             cfg['aria-valuenow'] = this.aria_valuenow;
10996         }
10997         
10998         if(this.aria_valuemin){
10999             cfg['aria-valuemin'] = this.aria_valuemin;
11000         }
11001         
11002         if(this.aria_valuemax){
11003             cfg['aria-valuemax'] = this.aria_valuemax;
11004         }
11005         
11006         if(this.label && !this.sr_only){
11007             cfg.html = this.label;
11008         }
11009         
11010         if(this.panel){
11011             cfg.cls += ' progress-bar-' + this.panel;
11012         }
11013         
11014         return cfg;
11015     },
11016     
11017     update : function(aria_valuenow)
11018     {
11019         this.aria_valuenow = aria_valuenow;
11020         
11021         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11022     }
11023    
11024 });
11025
11026  
11027
11028  /*
11029  * - LGPL
11030  *
11031  * TabPanel
11032  * 
11033  */
11034
11035 /**
11036  * @class Roo.bootstrap.TabPanel
11037  * @extends Roo.bootstrap.Component
11038  * Bootstrap TabPanel class
11039  * @cfg {Boolean} active panel active
11040  * @cfg {String} html panel content
11041  * @cfg {String} tabId tab relate id
11042  * @cfg {String} navId The navbar which triggers show hide
11043  * 
11044  * 
11045  * @constructor
11046  * Create a new TabPanel
11047  * @param {Object} config The config object
11048  */
11049
11050 Roo.bootstrap.TabPanel = function(config){
11051     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11052 };
11053
11054 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11055     
11056     active: false,
11057     html: false,
11058     tabId: false,
11059     navId : false,
11060     
11061     getAutoCreate : function(){
11062         var cfg = {
11063             tag: 'div',
11064             cls: 'tab-pane',
11065             html: this.html || ''
11066         };
11067         
11068         if(this.active){
11069             cfg.cls += ' active';
11070         }
11071         
11072         if(this.tabId){
11073             cfg.tabId = this.tabId;
11074         }
11075         
11076         return cfg;
11077     },
11078     onRender : function(ct, position)
11079     {
11080        // Roo.log("Call onRender: " + this.xtype);
11081         
11082         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11083         
11084         if (this.navId && this.tabId) {
11085             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11086             if (!item) {
11087                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11088             } else {
11089                 item.on('changed', function(item, state) {
11090                     this.setActive(state);
11091                 }, this);
11092             }
11093         }
11094         
11095     },
11096     setActive: function(state)
11097     {
11098         Roo.log("panel - set active " + this.tabId + "=" + state);
11099         
11100         this.active = state;
11101         if (!state) {
11102             this.el.removeClass('active');
11103             
11104         } else  if (!this.el.hasClass('active')) {
11105             this.el.addClass('active');
11106         }
11107         this.fireEvent('changed', this, state);
11108     }
11109     
11110     
11111 });
11112  
11113
11114  
11115
11116  /*
11117  * - LGPL
11118  *
11119  * DateField
11120  * 
11121  */
11122
11123 /**
11124  * @class Roo.bootstrap.DateField
11125  * @extends Roo.bootstrap.Input
11126  * Bootstrap DateField class
11127  * @cfg {Number} weekStart default 0
11128  * @cfg {Number} weekStart default 0
11129  * @cfg {Number} viewMode default empty, (months|years)
11130  * @cfg {Number} minViewMode default empty, (months|years)
11131  * @cfg {Number} startDate default -Infinity
11132  * @cfg {Number} endDate default Infinity
11133  * @cfg {Boolean} todayHighlight default false
11134  * @cfg {Boolean} todayBtn default false
11135  * @cfg {Boolean} calendarWeeks default false
11136  * @cfg {Object} daysOfWeekDisabled default empty
11137  * 
11138  * @cfg {Boolean} keyboardNavigation default true
11139  * @cfg {String} language default en
11140  * 
11141  * @constructor
11142  * Create a new DateField
11143  * @param {Object} config The config object
11144  */
11145
11146 Roo.bootstrap.DateField = function(config){
11147     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11148      this.addEvents({
11149             /**
11150              * @event show
11151              * Fires when this field show.
11152              * @param {Roo.bootstrap.DateField} this
11153              * @param {Mixed} date The date value
11154              */
11155             show : true,
11156             /**
11157              * @event show
11158              * Fires when this field hide.
11159              * @param {Roo.bootstrap.DateField} this
11160              * @param {Mixed} date The date value
11161              */
11162             hide : true,
11163             /**
11164              * @event select
11165              * Fires when select a date.
11166              * @param {Roo.bootstrap.DateField} this
11167              * @param {Mixed} date The date value
11168              */
11169             select : true
11170         });
11171 };
11172
11173 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11174     
11175     /**
11176      * @cfg {String} format
11177      * The default date format string which can be overriden for localization support.  The format must be
11178      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11179      */
11180     format : "m/d/y",
11181     /**
11182      * @cfg {String} altFormats
11183      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11184      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11185      */
11186     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11187     
11188     weekStart : 0,
11189     
11190     viewMode : '',
11191     
11192     minViewMode : '',
11193     
11194     todayHighlight : false,
11195     
11196     todayBtn: false,
11197     
11198     language: 'en',
11199     
11200     keyboardNavigation: true,
11201     
11202     calendarWeeks: false,
11203     
11204     startDate: -Infinity,
11205     
11206     endDate: Infinity,
11207     
11208     daysOfWeekDisabled: [],
11209     
11210     _events: [],
11211     
11212     UTCDate: function()
11213     {
11214         return new Date(Date.UTC.apply(Date, arguments));
11215     },
11216     
11217     UTCToday: function()
11218     {
11219         var today = new Date();
11220         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11221     },
11222     
11223     getDate: function() {
11224             var d = this.getUTCDate();
11225             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11226     },
11227     
11228     getUTCDate: function() {
11229             return this.date;
11230     },
11231     
11232     setDate: function(d) {
11233             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11234     },
11235     
11236     setUTCDate: function(d) {
11237             this.date = d;
11238             this.setValue(this.formatDate(this.date));
11239     },
11240         
11241     onRender: function(ct, position)
11242     {
11243         
11244         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11245         
11246         this.language = this.language || 'en';
11247         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11248         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11249         
11250         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11251         this.format = this.format || 'm/d/y';
11252         this.isInline = false;
11253         this.isInput = true;
11254         this.component = this.el.select('.add-on', true).first() || false;
11255         this.component = (this.component && this.component.length === 0) ? false : this.component;
11256         this.hasInput = this.component && this.inputEL().length;
11257         
11258         if (typeof(this.minViewMode === 'string')) {
11259             switch (this.minViewMode) {
11260                 case 'months':
11261                     this.minViewMode = 1;
11262                     break;
11263                 case 'years':
11264                     this.minViewMode = 2;
11265                     break;
11266                 default:
11267                     this.minViewMode = 0;
11268                     break;
11269             }
11270         }
11271         
11272         if (typeof(this.viewMode === 'string')) {
11273             switch (this.viewMode) {
11274                 case 'months':
11275                     this.viewMode = 1;
11276                     break;
11277                 case 'years':
11278                     this.viewMode = 2;
11279                     break;
11280                 default:
11281                     this.viewMode = 0;
11282                     break;
11283             }
11284         }
11285                 
11286         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11287         
11288         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11289         
11290         this.picker().on('mousedown', this.onMousedown, this);
11291         this.picker().on('click', this.onClick, this);
11292         
11293         this.picker().addClass('datepicker-dropdown');
11294         
11295         this.startViewMode = this.viewMode;
11296         
11297         
11298         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11299             if(!this.calendarWeeks){
11300                 v.remove();
11301                 return;
11302             };
11303             
11304             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11305             v.attr('colspan', function(i, val){
11306                 return parseInt(val) + 1;
11307             });
11308         })
11309                         
11310         
11311         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11312         
11313         this.setStartDate(this.startDate);
11314         this.setEndDate(this.endDate);
11315         
11316         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11317         
11318         this.fillDow();
11319         this.fillMonths();
11320         this.update();
11321         this.showMode();
11322         
11323         if(this.isInline) {
11324             this.show();
11325         }
11326     },
11327     
11328     picker : function()
11329     {
11330         return this.el.select('.datepicker', true).first();
11331     },
11332     
11333     fillDow: function()
11334     {
11335         var dowCnt = this.weekStart;
11336         
11337         var dow = {
11338             tag: 'tr',
11339             cn: [
11340                 
11341             ]
11342         };
11343         
11344         if(this.calendarWeeks){
11345             dow.cn.push({
11346                 tag: 'th',
11347                 cls: 'cw',
11348                 html: '&nbsp;'
11349             })
11350         }
11351         
11352         while (dowCnt < this.weekStart + 7) {
11353             dow.cn.push({
11354                 tag: 'th',
11355                 cls: 'dow',
11356                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11357             });
11358         }
11359         
11360         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11361     },
11362     
11363     fillMonths: function()
11364     {    
11365         var i = 0
11366         var months = this.picker().select('>.datepicker-months td', true).first();
11367         
11368         months.dom.innerHTML = '';
11369         
11370         while (i < 12) {
11371             var month = {
11372                 tag: 'span',
11373                 cls: 'month',
11374                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11375             }
11376             
11377             months.createChild(month);
11378         }
11379         
11380     },
11381     
11382     update: function(){
11383         
11384         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11385         
11386         if (this.date < this.startDate) {
11387             this.viewDate = new Date(this.startDate);
11388         } else if (this.date > this.endDate) {
11389             this.viewDate = new Date(this.endDate);
11390         } else {
11391             this.viewDate = new Date(this.date);
11392         }
11393         
11394         this.fill();
11395     },
11396     
11397     fill: function() {
11398         var d = new Date(this.viewDate),
11399                 year = d.getUTCFullYear(),
11400                 month = d.getUTCMonth(),
11401                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11402                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11403                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11404                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11405                 currentDate = this.date && this.date.valueOf(),
11406                 today = this.UTCToday();
11407         
11408         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11409         
11410 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11411         
11412 //        this.picker.select('>tfoot th.today').
11413 //                                              .text(dates[this.language].today)
11414 //                                              .toggle(this.todayBtn !== false);
11415     
11416         this.updateNavArrows();
11417         this.fillMonths();
11418                                                 
11419         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11420         
11421         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11422          
11423         prevMonth.setUTCDate(day);
11424         
11425         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11426         
11427         var nextMonth = new Date(prevMonth);
11428         
11429         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11430         
11431         nextMonth = nextMonth.valueOf();
11432         
11433         var fillMonths = false;
11434         
11435         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11436         
11437         while(prevMonth.valueOf() < nextMonth) {
11438             var clsName = '';
11439             
11440             if (prevMonth.getUTCDay() === this.weekStart) {
11441                 if(fillMonths){
11442                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11443                 }
11444                     
11445                 fillMonths = {
11446                     tag: 'tr',
11447                     cn: []
11448                 };
11449                 
11450                 if(this.calendarWeeks){
11451                     // ISO 8601: First week contains first thursday.
11452                     // ISO also states week starts on Monday, but we can be more abstract here.
11453                     var
11454                     // Start of current week: based on weekstart/current date
11455                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11456                     // Thursday of this week
11457                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11458                     // First Thursday of year, year from thursday
11459                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11460                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11461                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11462                     
11463                     fillMonths.cn.push({
11464                         tag: 'td',
11465                         cls: 'cw',
11466                         html: calWeek
11467                     });
11468                 }
11469             }
11470             
11471             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11472                 clsName += ' old';
11473             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11474                 clsName += ' new';
11475             }
11476             if (this.todayHighlight &&
11477                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11478                 prevMonth.getUTCMonth() == today.getMonth() &&
11479                 prevMonth.getUTCDate() == today.getDate()) {
11480                 clsName += ' today';
11481             }
11482             
11483             if (currentDate && prevMonth.valueOf() === currentDate) {
11484                 clsName += ' active';
11485             }
11486             
11487             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11488                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11489                     clsName += ' disabled';
11490             }
11491             
11492             fillMonths.cn.push({
11493                 tag: 'td',
11494                 cls: 'day ' + clsName,
11495                 html: prevMonth.getDate()
11496             })
11497             
11498             prevMonth.setDate(prevMonth.getDate()+1);
11499         }
11500           
11501         var currentYear = this.date && this.date.getUTCFullYear();
11502         var currentMonth = this.date && this.date.getUTCMonth();
11503         
11504         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11505         
11506         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11507             v.removeClass('active');
11508             
11509             if(currentYear === year && k === currentMonth){
11510                 v.addClass('active');
11511             }
11512             
11513             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11514                 v.addClass('disabled');
11515             }
11516             
11517         });
11518         
11519         
11520         year = parseInt(year/10, 10) * 10;
11521         
11522         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11523         
11524         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11525         
11526         year -= 1;
11527         for (var i = -1; i < 11; i++) {
11528             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11529                 tag: 'span',
11530                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11531                 html: year
11532             })
11533             
11534             year += 1;
11535         }
11536     },
11537     
11538     showMode: function(dir) {
11539         if (dir) {
11540             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11541         }
11542         Roo.each(this.picker().select('>div',true).elements, function(v){
11543             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11544             v.hide();
11545         });
11546         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11547     },
11548     
11549     place: function()
11550     {
11551         if(this.isInline) return;
11552         
11553         this.picker().removeClass(['bottom', 'top']);
11554         
11555         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11556             /*
11557              * place to the top of element!
11558              *
11559              */
11560             
11561             this.picker().addClass('top');
11562             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11563             
11564             return;
11565         }
11566         
11567         this.picker().addClass('bottom');
11568         
11569         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11570     },
11571     
11572     parseDate : function(value){
11573         if(!value || value instanceof Date){
11574             return value;
11575         }
11576         var v = Date.parseDate(value, this.format);
11577         if (!v && this.useIso) {
11578             v = Date.parseDate(value, 'Y-m-d');
11579         }
11580         if(!v && this.altFormats){
11581             if(!this.altFormatsArray){
11582                 this.altFormatsArray = this.altFormats.split("|");
11583             }
11584             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11585                 v = Date.parseDate(value, this.altFormatsArray[i]);
11586             }
11587         }
11588         return v;
11589     },
11590     
11591     formatDate : function(date, fmt){
11592         return (!date || !(date instanceof Date)) ?
11593         date : date.dateFormat(fmt || this.format);
11594     },
11595     
11596     onFocus : function()
11597     {
11598         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11599         this.show();
11600     },
11601     
11602     onBlur : function()
11603     {
11604         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11605         this.hide();
11606     },
11607     
11608     show : function()
11609     {
11610         this.picker().show();
11611         this.update();
11612         this.place();
11613         
11614         this.fireEvent('show', this, this.date);
11615     },
11616     
11617     hide : function()
11618     {
11619         if(this.isInline) return;
11620         this.picker().hide();
11621         this.viewMode = this.startViewMode;
11622         this.showMode();
11623         
11624         this.fireEvent('hide', this, this.date);
11625         
11626     },
11627     
11628     onMousedown: function(e){
11629         e.stopPropagation();
11630         e.preventDefault();
11631     },
11632     
11633     keyup: function(e){
11634         Roo.bootstrap.DateField.superclass.keyup.call(this);
11635         this.update();
11636         
11637     },
11638
11639     setValue: function(v){
11640         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11641         
11642         this.fireEvent('select', this, this.date);
11643         
11644     },
11645     
11646     fireKey: function(e){
11647         if (!this.picker().isVisible()){
11648             if (e.keyCode == 27) // allow escape to hide and re-show picker
11649                 this.show();
11650             return;
11651         }
11652         var dateChanged = false,
11653         dir, day, month,
11654         newDate, newViewDate;
11655         switch(e.keyCode){
11656             case 27: // escape
11657                 this.hide();
11658                 e.preventDefault();
11659                 break;
11660             case 37: // left
11661             case 39: // right
11662                 if (!this.keyboardNavigation) break;
11663                 dir = e.keyCode == 37 ? -1 : 1;
11664                 
11665                 if (e.ctrlKey){
11666                     newDate = this.moveYear(this.date, dir);
11667                     newViewDate = this.moveYear(this.viewDate, dir);
11668                 } else if (e.shiftKey){
11669                     newDate = this.moveMonth(this.date, dir);
11670                     newViewDate = this.moveMonth(this.viewDate, dir);
11671                 } else {
11672                     newDate = new Date(this.date);
11673                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11674                     newViewDate = new Date(this.viewDate);
11675                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11676                 }
11677                 if (this.dateWithinRange(newDate)){
11678                     this.date = newDate;
11679                     this.viewDate = newViewDate;
11680                     this.setValue(this.formatDate(this.date));
11681                     this.update();
11682                     e.preventDefault();
11683                     dateChanged = true;
11684                 }
11685                 break;
11686             case 38: // up
11687             case 40: // down
11688                 if (!this.keyboardNavigation) break;
11689                 dir = e.keyCode == 38 ? -1 : 1;
11690                 if (e.ctrlKey){
11691                     newDate = this.moveYear(this.date, dir);
11692                     newViewDate = this.moveYear(this.viewDate, dir);
11693                 } else if (e.shiftKey){
11694                     newDate = this.moveMonth(this.date, dir);
11695                     newViewDate = this.moveMonth(this.viewDate, dir);
11696                 } else {
11697                     newDate = new Date(this.date);
11698                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11699                     newViewDate = new Date(this.viewDate);
11700                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11701                 }
11702                 if (this.dateWithinRange(newDate)){
11703                     this.date = newDate;
11704                     this.viewDate = newViewDate;
11705                     this.setValue(this.formatDate(this.date));
11706                     this.update();
11707                     e.preventDefault();
11708                     dateChanged = true;
11709                 }
11710                 break;
11711             case 13: // enter
11712                 this.setValue(this.formatDate(this.date));
11713                 this.hide();
11714                 e.preventDefault();
11715                 break;
11716             case 9: // tab
11717                 this.setValue(this.formatDate(this.date));
11718                 this.hide();
11719                 break;
11720         }
11721     },
11722     
11723     
11724     onClick: function(e) {
11725         e.stopPropagation();
11726         e.preventDefault();
11727         
11728         var target = e.getTarget();
11729         
11730         if(target.nodeName.toLowerCase() === 'i'){
11731             target = Roo.get(target).dom.parentNode;
11732         }
11733         
11734         var nodeName = target.nodeName;
11735         var className = target.className;
11736         var html = target.innerHTML;
11737         
11738         switch(nodeName.toLowerCase()) {
11739             case 'th':
11740                 switch(className) {
11741                     case 'switch':
11742                         this.showMode(1);
11743                         break;
11744                     case 'prev':
11745                     case 'next':
11746                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11747                         switch(this.viewMode){
11748                                 case 0:
11749                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11750                                         break;
11751                                 case 1:
11752                                 case 2:
11753                                         this.viewDate = this.moveYear(this.viewDate, dir);
11754                                         break;
11755                         }
11756                         this.fill();
11757                         break;
11758                     case 'today':
11759                         var date = new Date();
11760                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11761                         this.fill()
11762                         this.setValue(this.formatDate(this.date));
11763                         this.hide();
11764                         break;
11765                 }
11766                 break;
11767             case 'span':
11768                 if (className.indexOf('disabled') === -1) {
11769                     this.viewDate.setUTCDate(1);
11770                     if (className.indexOf('month') !== -1) {
11771                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11772                     } else {
11773                         var year = parseInt(html, 10) || 0;
11774                         this.viewDate.setUTCFullYear(year);
11775                         
11776                     }
11777                     this.showMode(-1);
11778                     this.fill();
11779                 }
11780                 break;
11781                 
11782             case 'td':
11783                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11784                     var day = parseInt(html, 10) || 1;
11785                     var year = this.viewDate.getUTCFullYear(),
11786                         month = this.viewDate.getUTCMonth();
11787
11788                     if (className.indexOf('old') !== -1) {
11789                         if(month === 0 ){
11790                             month = 11;
11791                             year -= 1;
11792                         }else{
11793                             month -= 1;
11794                         }
11795                     } else if (className.indexOf('new') !== -1) {
11796                         if (month == 11) {
11797                             month = 0;
11798                             year += 1;
11799                         } else {
11800                             month += 1;
11801                         }
11802                     }
11803                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11804                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11805                     this.fill();
11806                     this.setValue(this.formatDate(this.date));
11807                     this.hide();
11808                 }
11809                 break;
11810         }
11811     },
11812     
11813     setStartDate: function(startDate){
11814         this.startDate = startDate || -Infinity;
11815         if (this.startDate !== -Infinity) {
11816             this.startDate = this.parseDate(this.startDate);
11817         }
11818         this.update();
11819         this.updateNavArrows();
11820     },
11821
11822     setEndDate: function(endDate){
11823         this.endDate = endDate || Infinity;
11824         if (this.endDate !== Infinity) {
11825             this.endDate = this.parseDate(this.endDate);
11826         }
11827         this.update();
11828         this.updateNavArrows();
11829     },
11830     
11831     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11832         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11833         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11834             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11835         }
11836         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11837             return parseInt(d, 10);
11838         });
11839         this.update();
11840         this.updateNavArrows();
11841     },
11842     
11843     updateNavArrows: function() {
11844         var d = new Date(this.viewDate),
11845         year = d.getUTCFullYear(),
11846         month = d.getUTCMonth();
11847         
11848         Roo.each(this.picker().select('.prev', true).elements, function(v){
11849             v.show();
11850             switch (this.viewMode) {
11851                 case 0:
11852
11853                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11854                         v.hide();
11855                     }
11856                     break;
11857                 case 1:
11858                 case 2:
11859                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11860                         v.hide();
11861                     }
11862                     break;
11863             }
11864         });
11865         
11866         Roo.each(this.picker().select('.next', true).elements, function(v){
11867             v.show();
11868             switch (this.viewMode) {
11869                 case 0:
11870
11871                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11872                         v.hide();
11873                     }
11874                     break;
11875                 case 1:
11876                 case 2:
11877                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11878                         v.hide();
11879                     }
11880                     break;
11881             }
11882         })
11883     },
11884     
11885     moveMonth: function(date, dir){
11886         if (!dir) return date;
11887         var new_date = new Date(date.valueOf()),
11888         day = new_date.getUTCDate(),
11889         month = new_date.getUTCMonth(),
11890         mag = Math.abs(dir),
11891         new_month, test;
11892         dir = dir > 0 ? 1 : -1;
11893         if (mag == 1){
11894             test = dir == -1
11895             // If going back one month, make sure month is not current month
11896             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11897             ? function(){
11898                 return new_date.getUTCMonth() == month;
11899             }
11900             // If going forward one month, make sure month is as expected
11901             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11902             : function(){
11903                 return new_date.getUTCMonth() != new_month;
11904             };
11905             new_month = month + dir;
11906             new_date.setUTCMonth(new_month);
11907             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11908             if (new_month < 0 || new_month > 11)
11909                 new_month = (new_month + 12) % 12;
11910         } else {
11911             // For magnitudes >1, move one month at a time...
11912             for (var i=0; i<mag; i++)
11913                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11914                 new_date = this.moveMonth(new_date, dir);
11915             // ...then reset the day, keeping it in the new month
11916             new_month = new_date.getUTCMonth();
11917             new_date.setUTCDate(day);
11918             test = function(){
11919                 return new_month != new_date.getUTCMonth();
11920             };
11921         }
11922         // Common date-resetting loop -- if date is beyond end of month, make it
11923         // end of month
11924         while (test()){
11925             new_date.setUTCDate(--day);
11926             new_date.setUTCMonth(new_month);
11927         }
11928         return new_date;
11929     },
11930
11931     moveYear: function(date, dir){
11932         return this.moveMonth(date, dir*12);
11933     },
11934
11935     dateWithinRange: function(date){
11936         return date >= this.startDate && date <= this.endDate;
11937     },
11938
11939     
11940     remove: function() {
11941         this.picker().remove();
11942     }
11943    
11944 });
11945
11946 Roo.apply(Roo.bootstrap.DateField,  {
11947     
11948     head : {
11949         tag: 'thead',
11950         cn: [
11951         {
11952             tag: 'tr',
11953             cn: [
11954             {
11955                 tag: 'th',
11956                 cls: 'prev',
11957                 html: '<i class="icon-arrow-left"/>'
11958             },
11959             {
11960                 tag: 'th',
11961                 cls: 'switch',
11962                 colspan: '5'
11963             },
11964             {
11965                 tag: 'th',
11966                 cls: 'next',
11967                 html: '<i class="icon-arrow-right"/>'
11968             }
11969
11970             ]
11971         }
11972         ]
11973     },
11974     
11975     content : {
11976         tag: 'tbody',
11977         cn: [
11978         {
11979             tag: 'tr',
11980             cn: [
11981             {
11982                 tag: 'td',
11983                 colspan: '7'
11984             }
11985             ]
11986         }
11987         ]
11988     },
11989     
11990     footer : {
11991         tag: 'tfoot',
11992         cn: [
11993         {
11994             tag: 'tr',
11995             cn: [
11996             {
11997                 tag: 'th',
11998                 colspan: '7',
11999                 cls: 'today'
12000             }
12001                     
12002             ]
12003         }
12004         ]
12005     },
12006     
12007     dates:{
12008         en: {
12009             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12010             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12011             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12012             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12013             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12014             today: "Today"
12015         }
12016     },
12017     
12018     modes: [
12019     {
12020         clsName: 'days',
12021         navFnc: 'Month',
12022         navStep: 1
12023     },
12024     {
12025         clsName: 'months',
12026         navFnc: 'FullYear',
12027         navStep: 1
12028     },
12029     {
12030         clsName: 'years',
12031         navFnc: 'FullYear',
12032         navStep: 10
12033     }]
12034 });
12035
12036 Roo.apply(Roo.bootstrap.DateField,  {
12037   
12038     template : {
12039         tag: 'div',
12040         cls: 'datepicker dropdown-menu',
12041         cn: [
12042         {
12043             tag: 'div',
12044             cls: 'datepicker-days',
12045             cn: [
12046             {
12047                 tag: 'table',
12048                 cls: 'table-condensed',
12049                 cn:[
12050                 Roo.bootstrap.DateField.head,
12051                 {
12052                     tag: 'tbody'
12053                 },
12054                 Roo.bootstrap.DateField.footer
12055                 ]
12056             }
12057             ]
12058         },
12059         {
12060             tag: 'div',
12061             cls: 'datepicker-months',
12062             cn: [
12063             {
12064                 tag: 'table',
12065                 cls: 'table-condensed',
12066                 cn:[
12067                 Roo.bootstrap.DateField.head,
12068                 Roo.bootstrap.DateField.content,
12069                 Roo.bootstrap.DateField.footer
12070                 ]
12071             }
12072             ]
12073         },
12074         {
12075             tag: 'div',
12076             cls: 'datepicker-years',
12077             cn: [
12078             {
12079                 tag: 'table',
12080                 cls: 'table-condensed',
12081                 cn:[
12082                 Roo.bootstrap.DateField.head,
12083                 Roo.bootstrap.DateField.content,
12084                 Roo.bootstrap.DateField.footer
12085                 ]
12086             }
12087             ]
12088         }
12089         ]
12090     }
12091 });
12092
12093  
12094
12095  /*
12096  * - LGPL
12097  *
12098  * TimeField
12099  * 
12100  */
12101
12102 /**
12103  * @class Roo.bootstrap.TimeField
12104  * @extends Roo.bootstrap.Input
12105  * Bootstrap DateField class
12106  * 
12107  * 
12108  * @constructor
12109  * Create a new TimeField
12110  * @param {Object} config The config object
12111  */
12112
12113 Roo.bootstrap.TimeField = function(config){
12114     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12115     this.addEvents({
12116             /**
12117              * @event show
12118              * Fires when this field show.
12119              * @param {Roo.bootstrap.DateField} this
12120              * @param {Mixed} date The date value
12121              */
12122             show : true,
12123             /**
12124              * @event show
12125              * Fires when this field hide.
12126              * @param {Roo.bootstrap.DateField} this
12127              * @param {Mixed} date The date value
12128              */
12129             hide : true,
12130             /**
12131              * @event select
12132              * Fires when select a date.
12133              * @param {Roo.bootstrap.DateField} this
12134              * @param {Mixed} date The date value
12135              */
12136             select : true
12137         });
12138 };
12139
12140 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12141     
12142     /**
12143      * @cfg {String} format
12144      * The default time format string which can be overriden for localization support.  The format must be
12145      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12146      */
12147     format : "H:i",
12148        
12149     onRender: function(ct, position)
12150     {
12151         
12152         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12153                 
12154         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12155         
12156         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12157         
12158         this.pop = this.picker().select('>.datepicker-time',true).first();
12159         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12160         
12161         this.picker().on('mousedown', this.onMousedown, this);
12162         this.picker().on('click', this.onClick, this);
12163         
12164         this.picker().addClass('datepicker-dropdown');
12165     
12166         this.fillTime();
12167         this.update();
12168             
12169         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12170         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12171         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12172         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12173         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12174         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12175
12176     },
12177     
12178     fireKey: function(e){
12179         if (!this.picker().isVisible()){
12180             if (e.keyCode == 27) // allow escape to hide and re-show picker
12181                 this.show();
12182             return;
12183         }
12184
12185         e.preventDefault();
12186         
12187         switch(e.keyCode){
12188             case 27: // escape
12189                 this.hide();
12190                 break;
12191             case 37: // left
12192             case 39: // right
12193                 this.onTogglePeriod();
12194                 break;
12195             case 38: // up
12196                 this.onIncrementMinutes();
12197                 break;
12198             case 40: // down
12199                 this.onDecrementMinutes();
12200                 break;
12201             case 13: // enter
12202             case 9: // tab
12203                 this.setTime();
12204                 break;
12205         }
12206     },
12207     
12208     onClick: function(e) {
12209         e.stopPropagation();
12210         e.preventDefault();
12211     },
12212     
12213     picker : function()
12214     {
12215         return this.el.select('.datepicker', true).first();
12216     },
12217     
12218     fillTime: function()
12219     {    
12220         var time = this.pop.select('tbody', true).first();
12221         
12222         time.dom.innerHTML = '';
12223         
12224         time.createChild({
12225             tag: 'tr',
12226             cn: [
12227                 {
12228                     tag: 'td',
12229                     cn: [
12230                         {
12231                             tag: 'a',
12232                             href: '#',
12233                             cls: 'btn',
12234                             cn: [
12235                                 {
12236                                     tag: 'span',
12237                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12238                                 }
12239                             ]
12240                         } 
12241                     ]
12242                 },
12243                 {
12244                     tag: 'td',
12245                     cls: 'separator'
12246                 },
12247                 {
12248                     tag: 'td',
12249                     cn: [
12250                         {
12251                             tag: 'a',
12252                             href: '#',
12253                             cls: 'btn',
12254                             cn: [
12255                                 {
12256                                     tag: 'span',
12257                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12258                                 }
12259                             ]
12260                         }
12261                     ]
12262                 },
12263                 {
12264                     tag: 'td',
12265                     cls: 'separator'
12266                 }
12267             ]
12268         });
12269         
12270         time.createChild({
12271             tag: 'tr',
12272             cn: [
12273                 {
12274                     tag: 'td',
12275                     cn: [
12276                         {
12277                             tag: 'span',
12278                             cls: 'timepicker-hour',
12279                             html: '00'
12280                         }  
12281                     ]
12282                 },
12283                 {
12284                     tag: 'td',
12285                     cls: 'separator',
12286                     html: ':'
12287                 },
12288                 {
12289                     tag: 'td',
12290                     cn: [
12291                         {
12292                             tag: 'span',
12293                             cls: 'timepicker-minute',
12294                             html: '00'
12295                         }  
12296                     ]
12297                 },
12298                 {
12299                     tag: 'td',
12300                     cls: 'separator'
12301                 },
12302                 {
12303                     tag: 'td',
12304                     cn: [
12305                         {
12306                             tag: 'button',
12307                             type: 'button',
12308                             cls: 'btn btn-primary period',
12309                             html: 'AM'
12310                             
12311                         }
12312                     ]
12313                 }
12314             ]
12315         });
12316         
12317         time.createChild({
12318             tag: 'tr',
12319             cn: [
12320                 {
12321                     tag: 'td',
12322                     cn: [
12323                         {
12324                             tag: 'a',
12325                             href: '#',
12326                             cls: 'btn',
12327                             cn: [
12328                                 {
12329                                     tag: 'span',
12330                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12331                                 }
12332                             ]
12333                         }
12334                     ]
12335                 },
12336                 {
12337                     tag: 'td',
12338                     cls: 'separator'
12339                 },
12340                 {
12341                     tag: 'td',
12342                     cn: [
12343                         {
12344                             tag: 'a',
12345                             href: '#',
12346                             cls: 'btn',
12347                             cn: [
12348                                 {
12349                                     tag: 'span',
12350                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12351                                 }
12352                             ]
12353                         }
12354                     ]
12355                 },
12356                 {
12357                     tag: 'td',
12358                     cls: 'separator'
12359                 }
12360             ]
12361         });
12362         
12363     },
12364     
12365     update: function()
12366     {
12367         
12368         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12369         
12370         this.fill();
12371     },
12372     
12373     fill: function() 
12374     {
12375         var hours = this.time.getHours();
12376         var minutes = this.time.getMinutes();
12377         var period = 'AM';
12378         
12379         if(hours > 11){
12380             period = 'PM';
12381         }
12382         
12383         if(hours == 0){
12384             hours = 12;
12385         }
12386         
12387         
12388         if(hours > 12){
12389             hours = hours - 12;
12390         }
12391         
12392         if(hours < 10){
12393             hours = '0' + hours;
12394         }
12395         
12396         if(minutes < 10){
12397             minutes = '0' + minutes;
12398         }
12399         
12400         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12401         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12402         this.pop.select('button', true).first().dom.innerHTML = period;
12403         
12404     },
12405     
12406     place: function()
12407     {   
12408         this.picker().removeClass(['bottom', 'top']);
12409         
12410         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12411             /*
12412              * place to the top of element!
12413              *
12414              */
12415             
12416             this.picker().addClass('top');
12417             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12418             
12419             return;
12420         }
12421         
12422         this.picker().addClass('bottom');
12423         
12424         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12425     },
12426   
12427     onFocus : function()
12428     {
12429         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12430         this.show();
12431     },
12432     
12433     onBlur : function()
12434     {
12435         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12436         this.hide();
12437     },
12438     
12439     show : function()
12440     {
12441         this.picker().show();
12442         this.pop.show();
12443         this.update();
12444         this.place();
12445         
12446         this.fireEvent('show', this, this.date);
12447     },
12448     
12449     hide : function()
12450     {
12451         this.picker().hide();
12452         this.pop.hide();
12453         
12454         this.fireEvent('hide', this, this.date);
12455     },
12456     
12457     setTime : function()
12458     {
12459         this.hide();
12460         this.setValue(this.time.format(this.format));
12461         
12462         this.fireEvent('select', this, this.date);
12463         
12464         
12465     },
12466     
12467     onMousedown: function(e){
12468         e.stopPropagation();
12469         e.preventDefault();
12470     },
12471     
12472     onIncrementHours: function()
12473     {
12474         Roo.log('onIncrementHours');
12475         this.time = this.time.add(Date.HOUR, 1);
12476         this.update();
12477         
12478     },
12479     
12480     onDecrementHours: function()
12481     {
12482         Roo.log('onDecrementHours');
12483         this.time = this.time.add(Date.HOUR, -1);
12484         this.update();
12485     },
12486     
12487     onIncrementMinutes: function()
12488     {
12489         Roo.log('onIncrementMinutes');
12490         this.time = this.time.add(Date.MINUTE, 1);
12491         this.update();
12492     },
12493     
12494     onDecrementMinutes: function()
12495     {
12496         Roo.log('onDecrementMinutes');
12497         this.time = this.time.add(Date.MINUTE, -1);
12498         this.update();
12499     },
12500     
12501     onTogglePeriod: function()
12502     {
12503         Roo.log('onTogglePeriod');
12504         this.time = this.time.add(Date.HOUR, 12);
12505         this.update();
12506     }
12507     
12508    
12509 });
12510
12511 Roo.apply(Roo.bootstrap.TimeField,  {
12512     
12513     content : {
12514         tag: 'tbody',
12515         cn: [
12516             {
12517                 tag: 'tr',
12518                 cn: [
12519                 {
12520                     tag: 'td',
12521                     colspan: '7'
12522                 }
12523                 ]
12524             }
12525         ]
12526     },
12527     
12528     footer : {
12529         tag: 'tfoot',
12530         cn: [
12531             {
12532                 tag: 'tr',
12533                 cn: [
12534                 {
12535                     tag: 'th',
12536                     colspan: '7',
12537                     cls: '',
12538                     cn: [
12539                         {
12540                             tag: 'button',
12541                             cls: 'btn btn-info ok',
12542                             html: 'OK'
12543                         }
12544                     ]
12545                 }
12546
12547                 ]
12548             }
12549         ]
12550     }
12551 });
12552
12553 Roo.apply(Roo.bootstrap.TimeField,  {
12554   
12555     template : {
12556         tag: 'div',
12557         cls: 'datepicker dropdown-menu',
12558         cn: [
12559             {
12560                 tag: 'div',
12561                 cls: 'datepicker-time',
12562                 cn: [
12563                 {
12564                     tag: 'table',
12565                     cls: 'table-condensed',
12566                     cn:[
12567                     Roo.bootstrap.TimeField.content,
12568                     Roo.bootstrap.TimeField.footer
12569                     ]
12570                 }
12571                 ]
12572             }
12573         ]
12574     }
12575 });
12576
12577  
12578
12579  /*
12580  * - LGPL
12581  *
12582  * CheckBox
12583  * 
12584  */
12585
12586 /**
12587  * @class Roo.bootstrap.CheckBox
12588  * @extends Roo.bootstrap.Input
12589  * Bootstrap CheckBox class
12590  * 
12591  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12592  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12593  * @cfg {String} boxLabel The text that appears beside the checkbox
12594  * @cfg {Boolean} checked initnal the element
12595  * 
12596  * @constructor
12597  * Create a new CheckBox
12598  * @param {Object} config The config object
12599  */
12600
12601 Roo.bootstrap.CheckBox = function(config){
12602     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12603    
12604         this.addEvents({
12605             /**
12606             * @event check
12607             * Fires when the element is checked or unchecked.
12608             * @param {Roo.bootstrap.CheckBox} this This input
12609             * @param {Boolean} checked The new checked value
12610             */
12611            check : true
12612         });
12613 };
12614
12615 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12616     
12617     inputType: 'checkbox',
12618     inputValue: 1,
12619     valueOff: 0,
12620     boxLabel: false,
12621     checked: false,
12622     
12623     getAutoCreate : function()
12624     {
12625         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12626         
12627         var id = Roo.id();
12628         
12629         var cfg = {};
12630         
12631         cfg.cls = 'form-group' //input-group
12632         
12633         var input =  {
12634             tag: 'input',
12635             id : id,
12636             type : this.inputType,
12637             value : (!this.checked) ? this.valueOff : this.inputValue,
12638             cls : 'form-box',
12639             placeholder : this.placeholder || ''
12640             
12641         };
12642         
12643         if (this.disabled) {
12644             input.disabled=true;
12645         }
12646         
12647         if(this.checked){
12648             input.checked = this.checked;
12649         }
12650         
12651         if (this.name) {
12652             input.name = this.name;
12653         }
12654         
12655         if (this.size) {
12656             input.cls += ' input-' + this.size;
12657         }
12658         
12659         var settings=this;
12660         ['xs','sm','md','lg'].map(function(size){
12661             if (settings[size]) {
12662                 cfg.cls += ' col-' + size + '-' + settings[size];
12663             }
12664         });
12665         
12666         var inputblock = input;
12667         
12668         if (this.before || this.after) {
12669             
12670             inputblock = {
12671                 cls : 'input-group',
12672                 cn :  [] 
12673             };
12674             if (this.before) {
12675                 inputblock.cn.push({
12676                     tag :'span',
12677                     cls : 'input-group-addon',
12678                     html : this.before
12679                 });
12680             }
12681             inputblock.cn.push(input);
12682             if (this.after) {
12683                 inputblock.cn.push({
12684                     tag :'span',
12685                     cls : 'input-group-addon',
12686                     html : this.after
12687                 });
12688             }
12689             
12690         };
12691         
12692         if (align ==='left' && this.fieldLabel.length) {
12693                 Roo.log("left and has label");
12694                 cfg.cn = [
12695                     
12696                     {
12697                         tag: 'label',
12698                         'for' :  id,
12699                         cls : 'control-label col-md-' + this.labelWidth,
12700                         html : this.fieldLabel
12701                         
12702                     },
12703                     {
12704                         cls : "col-md-" + (12 - this.labelWidth), 
12705                         cn: [
12706                             inputblock
12707                         ]
12708                     }
12709                     
12710                 ];
12711         } else if ( this.fieldLabel.length) {
12712                 Roo.log(" label");
12713                 cfg.cn = [
12714                    
12715                     {
12716                         tag: this.boxLabel ? 'span' : 'label',
12717                         'for': id,
12718                         cls: 'control-label box-input-label',
12719                         //cls : 'input-group-addon',
12720                         html : this.fieldLabel
12721                         
12722                     },
12723                     
12724                     inputblock
12725                     
12726                 ];
12727
12728         } else {
12729             
12730                    Roo.log(" no label && no align");
12731                 cfg.cn = [
12732                     
12733                         inputblock
12734                     
12735                 ];
12736                 
12737                 
12738         };
12739         
12740         if(this.boxLabel){
12741             cfg.cn.push({
12742                 tag: 'label',
12743                 'for': id,
12744                 cls: 'box-label',
12745                 html: this.boxLabel
12746             })
12747         }
12748         
12749         return cfg;
12750         
12751     },
12752     
12753     /**
12754      * return the real input element.
12755      */
12756     inputEl: function ()
12757     {
12758         return this.el.select('input.form-box',true).first();
12759     },
12760     
12761     initEvents : function()
12762     {
12763 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12764         
12765         this.inputEl().on('click', this.onClick,  this);
12766         
12767     },
12768     
12769     onClick : function()
12770     {   
12771         this.setChecked(!this.checked);
12772     },
12773     
12774     setChecked : function(state,suppressEvent)
12775     {
12776         this.checked = state;
12777         
12778         this.inputEl().dom.checked = state;
12779         
12780         if(suppressEvent !== true){
12781             this.fireEvent('check', this, state);
12782         }
12783         
12784         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12785         
12786     },
12787     
12788     setValue : function(v,suppressEvent)
12789     {
12790         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12791     }
12792     
12793 });
12794
12795  
12796 /*
12797  * - LGPL
12798  *
12799  * Radio
12800  * 
12801  */
12802
12803 /**
12804  * @class Roo.bootstrap.Radio
12805  * @extends Roo.bootstrap.CheckBox
12806  * Bootstrap Radio class
12807
12808  * @constructor
12809  * Create a new Radio
12810  * @param {Object} config The config object
12811  */
12812
12813 Roo.bootstrap.Radio = function(config){
12814     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12815    
12816 };
12817
12818 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12819     
12820     inputType: 'radio',
12821     inputValue: '',
12822     valueOff: '',
12823     
12824     getAutoCreate : function()
12825     {
12826         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12827         
12828         var id = Roo.id();
12829         
12830         var cfg = {};
12831         
12832         cfg.cls = 'form-group' //input-group
12833         
12834         var input =  {
12835             tag: 'input',
12836             id : id,
12837             type : this.inputType,
12838             value : (!this.checked) ? this.valueOff : this.inputValue,
12839             cls : 'form-box',
12840             placeholder : this.placeholder || ''
12841             
12842         };
12843         
12844         if (this.disabled) {
12845             input.disabled=true;
12846         }
12847         
12848         if(this.checked){
12849             input.checked = this.checked;
12850         }
12851         
12852         if (this.name) {
12853             input.name = this.name;
12854         }
12855         
12856         if (this.size) {
12857             input.cls += ' input-' + this.size;
12858         }
12859         
12860         var settings=this;
12861         ['xs','sm','md','lg'].map(function(size){
12862             if (settings[size]) {
12863                 cfg.cls += ' col-' + size + '-' + settings[size];
12864             }
12865         });
12866         
12867         var inputblock = input;
12868         
12869         if (this.before || this.after) {
12870             
12871             inputblock = {
12872                 cls : 'input-group',
12873                 cn :  [] 
12874             };
12875             if (this.before) {
12876                 inputblock.cn.push({
12877                     tag :'span',
12878                     cls : 'input-group-addon',
12879                     html : this.before
12880                 });
12881             }
12882             inputblock.cn.push(input);
12883             if (this.after) {
12884                 inputblock.cn.push({
12885                     tag :'span',
12886                     cls : 'input-group-addon',
12887                     html : this.after
12888                 });
12889             }
12890             
12891         };
12892         
12893         if (align ==='left' && this.fieldLabel.length) {
12894                 Roo.log("left and has label");
12895                 cfg.cn = [
12896                     
12897                     {
12898                         tag: 'label',
12899                         'for' :  id,
12900                         cls : 'control-label col-md-' + this.labelWidth,
12901                         html : this.fieldLabel
12902                         
12903                     },
12904                     {
12905                         cls : "col-md-" + (12 - this.labelWidth), 
12906                         cn: [
12907                             inputblock
12908                         ]
12909                     }
12910                     
12911                 ];
12912         } else if ( this.fieldLabel.length) {
12913                 Roo.log(" label");
12914                  cfg.cn = [
12915                    
12916                     {
12917                         tag: 'label',
12918                         'for': id,
12919                         cls: 'control-label box-input-label',
12920                         //cls : 'input-group-addon',
12921                         html : this.fieldLabel
12922                         
12923                     },
12924                     
12925                     inputblock
12926                     
12927                 ];
12928
12929         } else {
12930             
12931                    Roo.log(" no label && no align");
12932                 cfg.cn = [
12933                     
12934                         inputblock
12935                     
12936                 ];
12937                 
12938                 
12939         };
12940         
12941         if(this.boxLabel){
12942             cfg.cn.push({
12943                 tag: 'label',
12944                 'for': id,
12945                 cls: 'box-label',
12946                 html: this.boxLabel
12947             })
12948         }
12949         
12950         return cfg;
12951         
12952     },
12953    
12954     onClick : function()
12955     {   
12956         this.setChecked(true);
12957     },
12958     
12959     setChecked : function(state,suppressEvent)
12960     {
12961         if(state){
12962             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12963                 v.dom.checked = false;
12964             });
12965         }
12966         
12967         this.checked = state;
12968         this.inputEl().dom.checked = state;
12969         
12970         if(suppressEvent !== true){
12971             this.fireEvent('check', this, state);
12972         }
12973         
12974         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12975         
12976     },
12977     
12978     getGroupValue : function()
12979     {
12980         var value = ''
12981         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12982             if(v.dom.checked == true){
12983                 value = v.dom.value;
12984             }
12985         });
12986         
12987         return value;
12988     },
12989     
12990     /**
12991      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12992      * @return {Mixed} value The field value
12993      */
12994     getValue : function(){
12995         return this.getGroupValue();
12996     }
12997     
12998 });
12999
13000  
13001 //<script type="text/javascript">
13002
13003 /*
13004  * Based  Ext JS Library 1.1.1
13005  * Copyright(c) 2006-2007, Ext JS, LLC.
13006  * LGPL
13007  *
13008  */
13009  
13010 /**
13011  * @class Roo.HtmlEditorCore
13012  * @extends Roo.Component
13013  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13014  *
13015  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13016  */
13017
13018 Roo.HtmlEditorCore = function(config){
13019     
13020     
13021     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13022     this.addEvents({
13023         /**
13024          * @event initialize
13025          * Fires when the editor is fully initialized (including the iframe)
13026          * @param {Roo.HtmlEditorCore} this
13027          */
13028         initialize: true,
13029         /**
13030          * @event activate
13031          * Fires when the editor is first receives the focus. Any insertion must wait
13032          * until after this event.
13033          * @param {Roo.HtmlEditorCore} this
13034          */
13035         activate: true,
13036          /**
13037          * @event beforesync
13038          * Fires before the textarea is updated with content from the editor iframe. Return false
13039          * to cancel the sync.
13040          * @param {Roo.HtmlEditorCore} this
13041          * @param {String} html
13042          */
13043         beforesync: true,
13044          /**
13045          * @event beforepush
13046          * Fires before the iframe editor is updated with content from the textarea. Return false
13047          * to cancel the push.
13048          * @param {Roo.HtmlEditorCore} this
13049          * @param {String} html
13050          */
13051         beforepush: true,
13052          /**
13053          * @event sync
13054          * Fires when the textarea is updated with content from the editor iframe.
13055          * @param {Roo.HtmlEditorCore} this
13056          * @param {String} html
13057          */
13058         sync: true,
13059          /**
13060          * @event push
13061          * Fires when the iframe editor is updated with content from the textarea.
13062          * @param {Roo.HtmlEditorCore} this
13063          * @param {String} html
13064          */
13065         push: true,
13066         
13067         /**
13068          * @event editorevent
13069          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13070          * @param {Roo.HtmlEditorCore} this
13071          */
13072         editorevent: true
13073     });
13074      
13075 };
13076
13077
13078 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13079
13080
13081      /**
13082      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13083      */
13084     
13085     owner : false,
13086     
13087      /**
13088      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13089      *                        Roo.resizable.
13090      */
13091     resizable : false,
13092      /**
13093      * @cfg {Number} height (in pixels)
13094      */   
13095     height: 300,
13096    /**
13097      * @cfg {Number} width (in pixels)
13098      */   
13099     width: 500,
13100     
13101     /**
13102      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13103      * 
13104      */
13105     stylesheets: false,
13106     
13107     // id of frame..
13108     frameId: false,
13109     
13110     // private properties
13111     validationEvent : false,
13112     deferHeight: true,
13113     initialized : false,
13114     activated : false,
13115     sourceEditMode : false,
13116     onFocus : Roo.emptyFn,
13117     iframePad:3,
13118     hideMode:'offsets',
13119     
13120     clearUp: true,
13121     
13122      
13123     
13124
13125     /**
13126      * Protected method that will not generally be called directly. It
13127      * is called when the editor initializes the iframe with HTML contents. Override this method if you
13128      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13129      */
13130     getDocMarkup : function(){
13131         // body styles..
13132         var st = '';
13133         Roo.log(this.stylesheets);
13134         
13135         // inherit styels from page...?? 
13136         if (this.stylesheets === false) {
13137             
13138             Roo.get(document.head).select('style').each(function(node) {
13139                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13140             });
13141             
13142             Roo.get(document.head).select('link').each(function(node) { 
13143                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13144             });
13145             
13146         } else if (!this.stylesheets.length) {
13147                 // simple..
13148                 st = '<style type="text/css">' +
13149                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13150                    '</style>';
13151         } else {
13152             Roo.each(this.stylesheets, function(s) {
13153                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13154             });
13155             
13156         }
13157         
13158         st +=  '<style type="text/css">' +
13159             'IMG { cursor: pointer } ' +
13160         '</style>';
13161
13162         
13163         return '<html><head>' + st  +
13164             //<style type="text/css">' +
13165             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13166             //'</style>' +
13167             ' </head><body class="roo-htmleditor-body"></body></html>';
13168     },
13169
13170     // private
13171     onRender : function(ct, position)
13172     {
13173         var _t = this;
13174         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13175         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13176         
13177         
13178         this.el.dom.style.border = '0 none';
13179         this.el.dom.setAttribute('tabIndex', -1);
13180         this.el.addClass('x-hidden hide');
13181         
13182         
13183         
13184         if(Roo.isIE){ // fix IE 1px bogus margin
13185             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13186         }
13187        
13188         
13189         this.frameId = Roo.id();
13190         
13191          
13192         
13193         var iframe = this.owner.wrap.createChild({
13194             tag: 'iframe',
13195             cls: 'form-control', // bootstrap..
13196             id: this.frameId,
13197             name: this.frameId,
13198             frameBorder : 'no',
13199             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13200         }, this.el
13201         );
13202         
13203         
13204         this.iframe = iframe.dom;
13205
13206          this.assignDocWin();
13207         
13208         this.doc.designMode = 'on';
13209        
13210         this.doc.open();
13211         this.doc.write(this.getDocMarkup());
13212         this.doc.close();
13213
13214         
13215         var task = { // must defer to wait for browser to be ready
13216             run : function(){
13217                 //console.log("run task?" + this.doc.readyState);
13218                 this.assignDocWin();
13219                 if(this.doc.body || this.doc.readyState == 'complete'){
13220                     try {
13221                         this.doc.designMode="on";
13222                     } catch (e) {
13223                         return;
13224                     }
13225                     Roo.TaskMgr.stop(task);
13226                     this.initEditor.defer(10, this);
13227                 }
13228             },
13229             interval : 10,
13230             duration: 10000,
13231             scope: this
13232         };
13233         Roo.TaskMgr.start(task);
13234
13235         
13236          
13237     },
13238
13239     // private
13240     onResize : function(w, h)
13241     {
13242          Roo.log('resize: ' +w + ',' + h );
13243         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13244         if(!this.iframe){
13245             return;
13246         }
13247         if(typeof w == 'number'){
13248             
13249             this.iframe.style.width = w + 'px';
13250         }
13251         if(typeof h == 'number'){
13252             
13253             this.iframe.style.height = h + 'px';
13254             if(this.doc){
13255                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13256             }
13257         }
13258         
13259     },
13260
13261     /**
13262      * Toggles the editor between standard and source edit mode.
13263      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13264      */
13265     toggleSourceEdit : function(sourceEditMode){
13266         
13267         this.sourceEditMode = sourceEditMode === true;
13268         
13269         if(this.sourceEditMode){
13270  
13271             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13272             
13273         }else{
13274             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13275             //this.iframe.className = '';
13276             this.deferFocus();
13277         }
13278         //this.setSize(this.owner.wrap.getSize());
13279         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13280     },
13281
13282     
13283   
13284
13285     /**
13286      * Protected method that will not generally be called directly. If you need/want
13287      * custom HTML cleanup, this is the method you should override.
13288      * @param {String} html The HTML to be cleaned
13289      * return {String} The cleaned HTML
13290      */
13291     cleanHtml : function(html){
13292         html = String(html);
13293         if(html.length > 5){
13294             if(Roo.isSafari){ // strip safari nonsense
13295                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13296             }
13297         }
13298         if(html == '&nbsp;'){
13299             html = '';
13300         }
13301         return html;
13302     },
13303
13304     /**
13305      * HTML Editor -> Textarea
13306      * Protected method that will not generally be called directly. Syncs the contents
13307      * of the editor iframe with the textarea.
13308      */
13309     syncValue : function(){
13310         if(this.initialized){
13311             var bd = (this.doc.body || this.doc.documentElement);
13312             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13313             var html = bd.innerHTML;
13314             if(Roo.isSafari){
13315                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13316                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13317                 if(m && m[1]){
13318                     html = '<div style="'+m[0]+'">' + html + '</div>';
13319                 }
13320             }
13321             html = this.cleanHtml(html);
13322             // fix up the special chars.. normaly like back quotes in word...
13323             // however we do not want to do this with chinese..
13324             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13325                 var cc = b.charCodeAt();
13326                 if (
13327                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13328                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13329                     (cc >= 0xf900 && cc < 0xfb00 )
13330                 ) {
13331                         return b;
13332                 }
13333                 return "&#"+cc+";" 
13334             });
13335             if(this.owner.fireEvent('beforesync', this, html) !== false){
13336                 this.el.dom.value = html;
13337                 this.owner.fireEvent('sync', this, html);
13338             }
13339         }
13340     },
13341
13342     /**
13343      * Protected method that will not generally be called directly. Pushes the value of the textarea
13344      * into the iframe editor.
13345      */
13346     pushValue : function(){
13347         if(this.initialized){
13348             var v = this.el.dom.value.trim();
13349             
13350 //            if(v.length < 1){
13351 //                v = '&#160;';
13352 //            }
13353             
13354             if(this.owner.fireEvent('beforepush', this, v) !== false){
13355                 var d = (this.doc.body || this.doc.documentElement);
13356                 d.innerHTML = v;
13357                 this.cleanUpPaste();
13358                 this.el.dom.value = d.innerHTML;
13359                 this.owner.fireEvent('push', this, v);
13360             }
13361         }
13362     },
13363
13364     // private
13365     deferFocus : function(){
13366         this.focus.defer(10, this);
13367     },
13368
13369     // doc'ed in Field
13370     focus : function(){
13371         if(this.win && !this.sourceEditMode){
13372             this.win.focus();
13373         }else{
13374             this.el.focus();
13375         }
13376     },
13377     
13378     assignDocWin: function()
13379     {
13380         var iframe = this.iframe;
13381         
13382          if(Roo.isIE){
13383             this.doc = iframe.contentWindow.document;
13384             this.win = iframe.contentWindow;
13385         } else {
13386             if (!Roo.get(this.frameId)) {
13387                 return;
13388             }
13389             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13390             this.win = Roo.get(this.frameId).dom.contentWindow;
13391         }
13392     },
13393     
13394     // private
13395     initEditor : function(){
13396         //console.log("INIT EDITOR");
13397         this.assignDocWin();
13398         
13399         
13400         
13401         this.doc.designMode="on";
13402         this.doc.open();
13403         this.doc.write(this.getDocMarkup());
13404         this.doc.close();
13405         
13406         var dbody = (this.doc.body || this.doc.documentElement);
13407         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13408         // this copies styles from the containing element into thsi one..
13409         // not sure why we need all of this..
13410         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13411         ss['background-attachment'] = 'fixed'; // w3c
13412         dbody.bgProperties = 'fixed'; // ie
13413         Roo.DomHelper.applyStyles(dbody, ss);
13414         Roo.EventManager.on(this.doc, {
13415             //'mousedown': this.onEditorEvent,
13416             'mouseup': this.onEditorEvent,
13417             'dblclick': this.onEditorEvent,
13418             'click': this.onEditorEvent,
13419             'keyup': this.onEditorEvent,
13420             buffer:100,
13421             scope: this
13422         });
13423         if(Roo.isGecko){
13424             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13425         }
13426         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13427             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13428         }
13429         this.initialized = true;
13430
13431         this.owner.fireEvent('initialize', this);
13432         this.pushValue();
13433     },
13434
13435     // private
13436     onDestroy : function(){
13437         
13438         
13439         
13440         if(this.rendered){
13441             
13442             //for (var i =0; i < this.toolbars.length;i++) {
13443             //    // fixme - ask toolbars for heights?
13444             //    this.toolbars[i].onDestroy();
13445            // }
13446             
13447             //this.wrap.dom.innerHTML = '';
13448             //this.wrap.remove();
13449         }
13450     },
13451
13452     // private
13453     onFirstFocus : function(){
13454         
13455         this.assignDocWin();
13456         
13457         
13458         this.activated = true;
13459          
13460     
13461         if(Roo.isGecko){ // prevent silly gecko errors
13462             this.win.focus();
13463             var s = this.win.getSelection();
13464             if(!s.focusNode || s.focusNode.nodeType != 3){
13465                 var r = s.getRangeAt(0);
13466                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13467                 r.collapse(true);
13468                 this.deferFocus();
13469             }
13470             try{
13471                 this.execCmd('useCSS', true);
13472                 this.execCmd('styleWithCSS', false);
13473             }catch(e){}
13474         }
13475         this.owner.fireEvent('activate', this);
13476     },
13477
13478     // private
13479     adjustFont: function(btn){
13480         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13481         //if(Roo.isSafari){ // safari
13482         //    adjust *= 2;
13483        // }
13484         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13485         if(Roo.isSafari){ // safari
13486             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13487             v =  (v < 10) ? 10 : v;
13488             v =  (v > 48) ? 48 : v;
13489             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13490             
13491         }
13492         
13493         
13494         v = Math.max(1, v+adjust);
13495         
13496         this.execCmd('FontSize', v  );
13497     },
13498
13499     onEditorEvent : function(e){
13500         this.owner.fireEvent('editorevent', this, e);
13501       //  this.updateToolbar();
13502         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13503     },
13504
13505     insertTag : function(tg)
13506     {
13507         // could be a bit smarter... -> wrap the current selected tRoo..
13508         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13509             
13510             range = this.createRange(this.getSelection());
13511             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13512             wrappingNode.appendChild(range.extractContents());
13513             range.insertNode(wrappingNode);
13514
13515             return;
13516             
13517             
13518             
13519         }
13520         this.execCmd("formatblock",   tg);
13521         
13522     },
13523     
13524     insertText : function(txt)
13525     {
13526         
13527         
13528         var range = this.createRange();
13529         range.deleteContents();
13530                //alert(Sender.getAttribute('label'));
13531                
13532         range.insertNode(this.doc.createTextNode(txt));
13533     } ,
13534     
13535      
13536
13537     /**
13538      * Executes a Midas editor command on the editor document and performs necessary focus and
13539      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13540      * @param {String} cmd The Midas command
13541      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13542      */
13543     relayCmd : function(cmd, value){
13544         this.win.focus();
13545         this.execCmd(cmd, value);
13546         this.owner.fireEvent('editorevent', this);
13547         //this.updateToolbar();
13548         this.owner.deferFocus();
13549     },
13550
13551     /**
13552      * Executes a Midas editor command directly on the editor document.
13553      * For visual commands, you should use {@link #relayCmd} instead.
13554      * <b>This should only be called after the editor is initialized.</b>
13555      * @param {String} cmd The Midas command
13556      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13557      */
13558     execCmd : function(cmd, value){
13559         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13560         this.syncValue();
13561     },
13562  
13563  
13564    
13565     /**
13566      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13567      * to insert tRoo.
13568      * @param {String} text | dom node.. 
13569      */
13570     insertAtCursor : function(text)
13571     {
13572         
13573         
13574         
13575         if(!this.activated){
13576             return;
13577         }
13578         /*
13579         if(Roo.isIE){
13580             this.win.focus();
13581             var r = this.doc.selection.createRange();
13582             if(r){
13583                 r.collapse(true);
13584                 r.pasteHTML(text);
13585                 this.syncValue();
13586                 this.deferFocus();
13587             
13588             }
13589             return;
13590         }
13591         */
13592         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13593             this.win.focus();
13594             
13595             
13596             // from jquery ui (MIT licenced)
13597             var range, node;
13598             var win = this.win;
13599             
13600             if (win.getSelection && win.getSelection().getRangeAt) {
13601                 range = win.getSelection().getRangeAt(0);
13602                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13603                 range.insertNode(node);
13604             } else if (win.document.selection && win.document.selection.createRange) {
13605                 // no firefox support
13606                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13607                 win.document.selection.createRange().pasteHTML(txt);
13608             } else {
13609                 // no firefox support
13610                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13611                 this.execCmd('InsertHTML', txt);
13612             } 
13613             
13614             this.syncValue();
13615             
13616             this.deferFocus();
13617         }
13618     },
13619  // private
13620     mozKeyPress : function(e){
13621         if(e.ctrlKey){
13622             var c = e.getCharCode(), cmd;
13623           
13624             if(c > 0){
13625                 c = String.fromCharCode(c).toLowerCase();
13626                 switch(c){
13627                     case 'b':
13628                         cmd = 'bold';
13629                         break;
13630                     case 'i':
13631                         cmd = 'italic';
13632                         break;
13633                     
13634                     case 'u':
13635                         cmd = 'underline';
13636                         break;
13637                     
13638                     case 'v':
13639                         this.cleanUpPaste.defer(100, this);
13640                         return;
13641                         
13642                 }
13643                 if(cmd){
13644                     this.win.focus();
13645                     this.execCmd(cmd);
13646                     this.deferFocus();
13647                     e.preventDefault();
13648                 }
13649                 
13650             }
13651         }
13652     },
13653
13654     // private
13655     fixKeys : function(){ // load time branching for fastest keydown performance
13656         if(Roo.isIE){
13657             return function(e){
13658                 var k = e.getKey(), r;
13659                 if(k == e.TAB){
13660                     e.stopEvent();
13661                     r = this.doc.selection.createRange();
13662                     if(r){
13663                         r.collapse(true);
13664                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13665                         this.deferFocus();
13666                     }
13667                     return;
13668                 }
13669                 
13670                 if(k == e.ENTER){
13671                     r = this.doc.selection.createRange();
13672                     if(r){
13673                         var target = r.parentElement();
13674                         if(!target || target.tagName.toLowerCase() != 'li'){
13675                             e.stopEvent();
13676                             r.pasteHTML('<br />');
13677                             r.collapse(false);
13678                             r.select();
13679                         }
13680                     }
13681                 }
13682                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13683                     this.cleanUpPaste.defer(100, this);
13684                     return;
13685                 }
13686                 
13687                 
13688             };
13689         }else if(Roo.isOpera){
13690             return function(e){
13691                 var k = e.getKey();
13692                 if(k == e.TAB){
13693                     e.stopEvent();
13694                     this.win.focus();
13695                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13696                     this.deferFocus();
13697                 }
13698                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13699                     this.cleanUpPaste.defer(100, this);
13700                     return;
13701                 }
13702                 
13703             };
13704         }else if(Roo.isSafari){
13705             return function(e){
13706                 var k = e.getKey();
13707                 
13708                 if(k == e.TAB){
13709                     e.stopEvent();
13710                     this.execCmd('InsertText','\t');
13711                     this.deferFocus();
13712                     return;
13713                 }
13714                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13715                     this.cleanUpPaste.defer(100, this);
13716                     return;
13717                 }
13718                 
13719              };
13720         }
13721     }(),
13722     
13723     getAllAncestors: function()
13724     {
13725         var p = this.getSelectedNode();
13726         var a = [];
13727         if (!p) {
13728             a.push(p); // push blank onto stack..
13729             p = this.getParentElement();
13730         }
13731         
13732         
13733         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13734             a.push(p);
13735             p = p.parentNode;
13736         }
13737         a.push(this.doc.body);
13738         return a;
13739     },
13740     lastSel : false,
13741     lastSelNode : false,
13742     
13743     
13744     getSelection : function() 
13745     {
13746         this.assignDocWin();
13747         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13748     },
13749     
13750     getSelectedNode: function() 
13751     {
13752         // this may only work on Gecko!!!
13753         
13754         // should we cache this!!!!
13755         
13756         
13757         
13758          
13759         var range = this.createRange(this.getSelection()).cloneRange();
13760         
13761         if (Roo.isIE) {
13762             var parent = range.parentElement();
13763             while (true) {
13764                 var testRange = range.duplicate();
13765                 testRange.moveToElementText(parent);
13766                 if (testRange.inRange(range)) {
13767                     break;
13768                 }
13769                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13770                     break;
13771                 }
13772                 parent = parent.parentElement;
13773             }
13774             return parent;
13775         }
13776         
13777         // is ancestor a text element.
13778         var ac =  range.commonAncestorContainer;
13779         if (ac.nodeType == 3) {
13780             ac = ac.parentNode;
13781         }
13782         
13783         var ar = ac.childNodes;
13784          
13785         var nodes = [];
13786         var other_nodes = [];
13787         var has_other_nodes = false;
13788         for (var i=0;i<ar.length;i++) {
13789             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13790                 continue;
13791             }
13792             // fullly contained node.
13793             
13794             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13795                 nodes.push(ar[i]);
13796                 continue;
13797             }
13798             
13799             // probably selected..
13800             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13801                 other_nodes.push(ar[i]);
13802                 continue;
13803             }
13804             // outer..
13805             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13806                 continue;
13807             }
13808             
13809             
13810             has_other_nodes = true;
13811         }
13812         if (!nodes.length && other_nodes.length) {
13813             nodes= other_nodes;
13814         }
13815         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13816             return false;
13817         }
13818         
13819         return nodes[0];
13820     },
13821     createRange: function(sel)
13822     {
13823         // this has strange effects when using with 
13824         // top toolbar - not sure if it's a great idea.
13825         //this.editor.contentWindow.focus();
13826         if (typeof sel != "undefined") {
13827             try {
13828                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13829             } catch(e) {
13830                 return this.doc.createRange();
13831             }
13832         } else {
13833             return this.doc.createRange();
13834         }
13835     },
13836     getParentElement: function()
13837     {
13838         
13839         this.assignDocWin();
13840         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13841         
13842         var range = this.createRange(sel);
13843          
13844         try {
13845             var p = range.commonAncestorContainer;
13846             while (p.nodeType == 3) { // text node
13847                 p = p.parentNode;
13848             }
13849             return p;
13850         } catch (e) {
13851             return null;
13852         }
13853     
13854     },
13855     /***
13856      *
13857      * Range intersection.. the hard stuff...
13858      *  '-1' = before
13859      *  '0' = hits..
13860      *  '1' = after.
13861      *         [ -- selected range --- ]
13862      *   [fail]                        [fail]
13863      *
13864      *    basically..
13865      *      if end is before start or  hits it. fail.
13866      *      if start is after end or hits it fail.
13867      *
13868      *   if either hits (but other is outside. - then it's not 
13869      *   
13870      *    
13871      **/
13872     
13873     
13874     // @see http://www.thismuchiknow.co.uk/?p=64.
13875     rangeIntersectsNode : function(range, node)
13876     {
13877         var nodeRange = node.ownerDocument.createRange();
13878         try {
13879             nodeRange.selectNode(node);
13880         } catch (e) {
13881             nodeRange.selectNodeContents(node);
13882         }
13883     
13884         var rangeStartRange = range.cloneRange();
13885         rangeStartRange.collapse(true);
13886     
13887         var rangeEndRange = range.cloneRange();
13888         rangeEndRange.collapse(false);
13889     
13890         var nodeStartRange = nodeRange.cloneRange();
13891         nodeStartRange.collapse(true);
13892     
13893         var nodeEndRange = nodeRange.cloneRange();
13894         nodeEndRange.collapse(false);
13895     
13896         return rangeStartRange.compareBoundaryPoints(
13897                  Range.START_TO_START, nodeEndRange) == -1 &&
13898                rangeEndRange.compareBoundaryPoints(
13899                  Range.START_TO_START, nodeStartRange) == 1;
13900         
13901          
13902     },
13903     rangeCompareNode : function(range, node)
13904     {
13905         var nodeRange = node.ownerDocument.createRange();
13906         try {
13907             nodeRange.selectNode(node);
13908         } catch (e) {
13909             nodeRange.selectNodeContents(node);
13910         }
13911         
13912         
13913         range.collapse(true);
13914     
13915         nodeRange.collapse(true);
13916      
13917         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13918         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13919          
13920         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13921         
13922         var nodeIsBefore   =  ss == 1;
13923         var nodeIsAfter    = ee == -1;
13924         
13925         if (nodeIsBefore && nodeIsAfter)
13926             return 0; // outer
13927         if (!nodeIsBefore && nodeIsAfter)
13928             return 1; //right trailed.
13929         
13930         if (nodeIsBefore && !nodeIsAfter)
13931             return 2;  // left trailed.
13932         // fully contined.
13933         return 3;
13934     },
13935
13936     // private? - in a new class?
13937     cleanUpPaste :  function()
13938     {
13939         // cleans up the whole document..
13940         Roo.log('cleanuppaste');
13941         
13942         this.cleanUpChildren(this.doc.body);
13943         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13944         if (clean != this.doc.body.innerHTML) {
13945             this.doc.body.innerHTML = clean;
13946         }
13947         
13948     },
13949     
13950     cleanWordChars : function(input) {// change the chars to hex code
13951         var he = Roo.HtmlEditorCore;
13952         
13953         var output = input;
13954         Roo.each(he.swapCodes, function(sw) { 
13955             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13956             
13957             output = output.replace(swapper, sw[1]);
13958         });
13959         
13960         return output;
13961     },
13962     
13963     
13964     cleanUpChildren : function (n)
13965     {
13966         if (!n.childNodes.length) {
13967             return;
13968         }
13969         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13970            this.cleanUpChild(n.childNodes[i]);
13971         }
13972     },
13973     
13974     
13975         
13976     
13977     cleanUpChild : function (node)
13978     {
13979         var ed = this;
13980         //console.log(node);
13981         if (node.nodeName == "#text") {
13982             // clean up silly Windows -- stuff?
13983             return; 
13984         }
13985         if (node.nodeName == "#comment") {
13986             node.parentNode.removeChild(node);
13987             // clean up silly Windows -- stuff?
13988             return; 
13989         }
13990         
13991         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13992             // remove node.
13993             node.parentNode.removeChild(node);
13994             return;
13995             
13996         }
13997         
13998         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13999         
14000         // remove <a name=....> as rendering on yahoo mailer is borked with this.
14001         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14002         
14003         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14004         //    remove_keep_children = true;
14005         //}
14006         
14007         if (remove_keep_children) {
14008             this.cleanUpChildren(node);
14009             // inserts everything just before this node...
14010             while (node.childNodes.length) {
14011                 var cn = node.childNodes[0];
14012                 node.removeChild(cn);
14013                 node.parentNode.insertBefore(cn, node);
14014             }
14015             node.parentNode.removeChild(node);
14016             return;
14017         }
14018         
14019         if (!node.attributes || !node.attributes.length) {
14020             this.cleanUpChildren(node);
14021             return;
14022         }
14023         
14024         function cleanAttr(n,v)
14025         {
14026             
14027             if (v.match(/^\./) || v.match(/^\//)) {
14028                 return;
14029             }
14030             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14031                 return;
14032             }
14033             if (v.match(/^#/)) {
14034                 return;
14035             }
14036 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14037             node.removeAttribute(n);
14038             
14039         }
14040         
14041         function cleanStyle(n,v)
14042         {
14043             if (v.match(/expression/)) { //XSS?? should we even bother..
14044                 node.removeAttribute(n);
14045                 return;
14046             }
14047             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14048             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14049             
14050             
14051             var parts = v.split(/;/);
14052             var clean = [];
14053             
14054             Roo.each(parts, function(p) {
14055                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14056                 if (!p.length) {
14057                     return true;
14058                 }
14059                 var l = p.split(':').shift().replace(/\s+/g,'');
14060                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14061                 
14062                 if ( cblack.indexOf(l) > -1) {
14063 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14064                     //node.removeAttribute(n);
14065                     return true;
14066                 }
14067                 //Roo.log()
14068                 // only allow 'c whitelisted system attributes'
14069                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14070 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14071                     //node.removeAttribute(n);
14072                     return true;
14073                 }
14074                 
14075                 
14076                  
14077                 
14078                 clean.push(p);
14079                 return true;
14080             });
14081             if (clean.length) { 
14082                 node.setAttribute(n, clean.join(';'));
14083             } else {
14084                 node.removeAttribute(n);
14085             }
14086             
14087         }
14088         
14089         
14090         for (var i = node.attributes.length-1; i > -1 ; i--) {
14091             var a = node.attributes[i];
14092             //console.log(a);
14093             
14094             if (a.name.toLowerCase().substr(0,2)=='on')  {
14095                 node.removeAttribute(a.name);
14096                 continue;
14097             }
14098             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14099                 node.removeAttribute(a.name);
14100                 continue;
14101             }
14102             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14103                 cleanAttr(a.name,a.value); // fixme..
14104                 continue;
14105             }
14106             if (a.name == 'style') {
14107                 cleanStyle(a.name,a.value);
14108                 continue;
14109             }
14110             /// clean up MS crap..
14111             // tecnically this should be a list of valid class'es..
14112             
14113             
14114             if (a.name == 'class') {
14115                 if (a.value.match(/^Mso/)) {
14116                     node.className = '';
14117                 }
14118                 
14119                 if (a.value.match(/body/)) {
14120                     node.className = '';
14121                 }
14122                 continue;
14123             }
14124             
14125             // style cleanup!?
14126             // class cleanup?
14127             
14128         }
14129         
14130         
14131         this.cleanUpChildren(node);
14132         
14133         
14134     }
14135     
14136     
14137     // hide stuff that is not compatible
14138     /**
14139      * @event blur
14140      * @hide
14141      */
14142     /**
14143      * @event change
14144      * @hide
14145      */
14146     /**
14147      * @event focus
14148      * @hide
14149      */
14150     /**
14151      * @event specialkey
14152      * @hide
14153      */
14154     /**
14155      * @cfg {String} fieldClass @hide
14156      */
14157     /**
14158      * @cfg {String} focusClass @hide
14159      */
14160     /**
14161      * @cfg {String} autoCreate @hide
14162      */
14163     /**
14164      * @cfg {String} inputType @hide
14165      */
14166     /**
14167      * @cfg {String} invalidClass @hide
14168      */
14169     /**
14170      * @cfg {String} invalidText @hide
14171      */
14172     /**
14173      * @cfg {String} msgFx @hide
14174      */
14175     /**
14176      * @cfg {String} validateOnBlur @hide
14177      */
14178 });
14179
14180 Roo.HtmlEditorCore.white = [
14181         'area', 'br', 'img', 'input', 'hr', 'wbr',
14182         
14183        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14184        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14185        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14186        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14187        'table',   'ul',         'xmp', 
14188        
14189        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14190       'thead',   'tr', 
14191      
14192       'dir', 'menu', 'ol', 'ul', 'dl',
14193        
14194       'embed',  'object'
14195 ];
14196
14197
14198 Roo.HtmlEditorCore.black = [
14199     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14200         'applet', // 
14201         'base',   'basefont', 'bgsound', 'blink',  'body', 
14202         'frame',  'frameset', 'head',    'html',   'ilayer', 
14203         'iframe', 'layer',  'link',     'meta',    'object',   
14204         'script', 'style' ,'title',  'xml' // clean later..
14205 ];
14206 Roo.HtmlEditorCore.clean = [
14207     'script', 'style', 'title', 'xml'
14208 ];
14209 Roo.HtmlEditorCore.remove = [
14210     'font'
14211 ];
14212 // attributes..
14213
14214 Roo.HtmlEditorCore.ablack = [
14215     'on'
14216 ];
14217     
14218 Roo.HtmlEditorCore.aclean = [ 
14219     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14220 ];
14221
14222 // protocols..
14223 Roo.HtmlEditorCore.pwhite= [
14224         'http',  'https',  'mailto'
14225 ];
14226
14227 // white listed style attributes.
14228 Roo.HtmlEditorCore.cwhite= [
14229       //  'text-align', /// default is to allow most things..
14230       
14231          
14232 //        'font-size'//??
14233 ];
14234
14235 // black listed style attributes.
14236 Roo.HtmlEditorCore.cblack= [
14237       //  'font-size' -- this can be set by the project 
14238 ];
14239
14240
14241 Roo.HtmlEditorCore.swapCodes   =[ 
14242     [    8211, "--" ], 
14243     [    8212, "--" ], 
14244     [    8216,  "'" ],  
14245     [    8217, "'" ],  
14246     [    8220, '"' ],  
14247     [    8221, '"' ],  
14248     [    8226, "*" ],  
14249     [    8230, "..." ]
14250 ]; 
14251
14252     /*
14253  * - LGPL
14254  *
14255  * HtmlEditor
14256  * 
14257  */
14258
14259 /**
14260  * @class Roo.bootstrap.HtmlEditor
14261  * @extends Roo.bootstrap.TextArea
14262  * Bootstrap HtmlEditor class
14263
14264  * @constructor
14265  * Create a new HtmlEditor
14266  * @param {Object} config The config object
14267  */
14268
14269 Roo.bootstrap.HtmlEditor = function(config){
14270     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14271     if (!this.toolbars) {
14272         this.toolbars = [];
14273     }
14274     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14275     this.addEvents({
14276             /**
14277              * @event initialize
14278              * Fires when the editor is fully initialized (including the iframe)
14279              * @param {HtmlEditor} this
14280              */
14281             initialize: true,
14282             /**
14283              * @event activate
14284              * Fires when the editor is first receives the focus. Any insertion must wait
14285              * until after this event.
14286              * @param {HtmlEditor} this
14287              */
14288             activate: true,
14289              /**
14290              * @event beforesync
14291              * Fires before the textarea is updated with content from the editor iframe. Return false
14292              * to cancel the sync.
14293              * @param {HtmlEditor} this
14294              * @param {String} html
14295              */
14296             beforesync: true,
14297              /**
14298              * @event beforepush
14299              * Fires before the iframe editor is updated with content from the textarea. Return false
14300              * to cancel the push.
14301              * @param {HtmlEditor} this
14302              * @param {String} html
14303              */
14304             beforepush: true,
14305              /**
14306              * @event sync
14307              * Fires when the textarea is updated with content from the editor iframe.
14308              * @param {HtmlEditor} this
14309              * @param {String} html
14310              */
14311             sync: true,
14312              /**
14313              * @event push
14314              * Fires when the iframe editor is updated with content from the textarea.
14315              * @param {HtmlEditor} this
14316              * @param {String} html
14317              */
14318             push: true,
14319              /**
14320              * @event editmodechange
14321              * Fires when the editor switches edit modes
14322              * @param {HtmlEditor} this
14323              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14324              */
14325             editmodechange: true,
14326             /**
14327              * @event editorevent
14328              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14329              * @param {HtmlEditor} this
14330              */
14331             editorevent: true,
14332             /**
14333              * @event firstfocus
14334              * Fires when on first focus - needed by toolbars..
14335              * @param {HtmlEditor} this
14336              */
14337             firstfocus: true,
14338             /**
14339              * @event autosave
14340              * Auto save the htmlEditor value as a file into Events
14341              * @param {HtmlEditor} this
14342              */
14343             autosave: true,
14344             /**
14345              * @event savedpreview
14346              * preview the saved version of htmlEditor
14347              * @param {HtmlEditor} this
14348              */
14349             savedpreview: true
14350         });
14351 };
14352
14353
14354 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14355     
14356     
14357       /**
14358      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14359      */
14360     toolbars : false,
14361    
14362      /**
14363      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14364      *                        Roo.resizable.
14365      */
14366     resizable : false,
14367      /**
14368      * @cfg {Number} height (in pixels)
14369      */   
14370     height: 300,
14371    /**
14372      * @cfg {Number} width (in pixels)
14373      */   
14374     width: false,
14375     
14376     /**
14377      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14378      * 
14379      */
14380     stylesheets: false,
14381     
14382     // id of frame..
14383     frameId: false,
14384     
14385     // private properties
14386     validationEvent : false,
14387     deferHeight: true,
14388     initialized : false,
14389     activated : false,
14390     
14391     onFocus : Roo.emptyFn,
14392     iframePad:3,
14393     hideMode:'offsets',
14394     
14395     
14396     tbContainer : false,
14397     
14398     toolbarContainer :function() {
14399         return this.wrap.select('.x-html-editor-tb',true).first();
14400     },
14401
14402     /**
14403      * Protected method that will not generally be called directly. It
14404      * is called when the editor creates its toolbar. Override this method if you need to
14405      * add custom toolbar buttons.
14406      * @param {HtmlEditor} editor
14407      */
14408     createToolbar : function(){
14409         
14410         Roo.log("create toolbars");
14411         
14412         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14413         this.toolbars[0].render(this.toolbarContainer());
14414         
14415         return;
14416         
14417 //        if (!editor.toolbars || !editor.toolbars.length) {
14418 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14419 //        }
14420 //        
14421 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14422 //            editor.toolbars[i] = Roo.factory(
14423 //                    typeof(editor.toolbars[i]) == 'string' ?
14424 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14425 //                Roo.bootstrap.HtmlEditor);
14426 //            editor.toolbars[i].init(editor);
14427 //        }
14428     },
14429
14430      
14431     // private
14432     onRender : function(ct, position)
14433     {
14434        // Roo.log("Call onRender: " + this.xtype);
14435         var _t = this;
14436         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14437       
14438         this.wrap = this.inputEl().wrap({
14439             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14440         });
14441         
14442         this.editorcore.onRender(ct, position);
14443          
14444         if (this.resizable) {
14445             this.resizeEl = new Roo.Resizable(this.wrap, {
14446                 pinned : true,
14447                 wrap: true,
14448                 dynamic : true,
14449                 minHeight : this.height,
14450                 height: this.height,
14451                 handles : this.resizable,
14452                 width: this.width,
14453                 listeners : {
14454                     resize : function(r, w, h) {
14455                         _t.onResize(w,h); // -something
14456                     }
14457                 }
14458             });
14459             
14460         }
14461         this.createToolbar(this);
14462        
14463         
14464         if(!this.width && this.resizable){
14465             this.setSize(this.wrap.getSize());
14466         }
14467         if (this.resizeEl) {
14468             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14469             // should trigger onReize..
14470         }
14471         
14472     },
14473
14474     // private
14475     onResize : function(w, h)
14476     {
14477         Roo.log('resize: ' +w + ',' + h );
14478         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14479         var ew = false;
14480         var eh = false;
14481         
14482         if(this.inputEl() ){
14483             if(typeof w == 'number'){
14484                 var aw = w - this.wrap.getFrameWidth('lr');
14485                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14486                 ew = aw;
14487             }
14488             if(typeof h == 'number'){
14489                  var tbh = -11;  // fixme it needs to tool bar size!
14490                 for (var i =0; i < this.toolbars.length;i++) {
14491                     // fixme - ask toolbars for heights?
14492                     tbh += this.toolbars[i].el.getHeight();
14493                     //if (this.toolbars[i].footer) {
14494                     //    tbh += this.toolbars[i].footer.el.getHeight();
14495                     //}
14496                 }
14497               
14498                 
14499                 
14500                 
14501                 
14502                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14503                 ah -= 5; // knock a few pixes off for look..
14504                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14505                 var eh = ah;
14506             }
14507         }
14508         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14509         this.editorcore.onResize(ew,eh);
14510         
14511     },
14512
14513     /**
14514      * Toggles the editor between standard and source edit mode.
14515      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14516      */
14517     toggleSourceEdit : function(sourceEditMode)
14518     {
14519         this.editorcore.toggleSourceEdit(sourceEditMode);
14520         
14521         if(this.editorcore.sourceEditMode){
14522             Roo.log('editor - showing textarea');
14523             
14524 //            Roo.log('in');
14525 //            Roo.log(this.syncValue());
14526             this.syncValue();
14527             this.inputEl().removeClass('hide');
14528             this.inputEl().dom.removeAttribute('tabIndex');
14529             this.inputEl().focus();
14530         }else{
14531             Roo.log('editor - hiding textarea');
14532 //            Roo.log('out')
14533 //            Roo.log(this.pushValue()); 
14534             this.pushValue();
14535             
14536             this.inputEl().addClass('hide');
14537             this.inputEl().dom.setAttribute('tabIndex', -1);
14538             //this.deferFocus();
14539         }
14540          
14541         if(this.resizable){
14542             this.setSize(this.wrap.getSize());
14543         }
14544         
14545         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14546     },
14547  
14548     // private (for BoxComponent)
14549     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14550
14551     // private (for BoxComponent)
14552     getResizeEl : function(){
14553         return this.wrap;
14554     },
14555
14556     // private (for BoxComponent)
14557     getPositionEl : function(){
14558         return this.wrap;
14559     },
14560
14561     // private
14562     initEvents : function(){
14563         this.originalValue = this.getValue();
14564     },
14565
14566 //    /**
14567 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14568 //     * @method
14569 //     */
14570 //    markInvalid : Roo.emptyFn,
14571 //    /**
14572 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14573 //     * @method
14574 //     */
14575 //    clearInvalid : Roo.emptyFn,
14576
14577     setValue : function(v){
14578         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14579         this.editorcore.pushValue();
14580     },
14581
14582      
14583     // private
14584     deferFocus : function(){
14585         this.focus.defer(10, this);
14586     },
14587
14588     // doc'ed in Field
14589     focus : function(){
14590         this.editorcore.focus();
14591         
14592     },
14593       
14594
14595     // private
14596     onDestroy : function(){
14597         
14598         
14599         
14600         if(this.rendered){
14601             
14602             for (var i =0; i < this.toolbars.length;i++) {
14603                 // fixme - ask toolbars for heights?
14604                 this.toolbars[i].onDestroy();
14605             }
14606             
14607             this.wrap.dom.innerHTML = '';
14608             this.wrap.remove();
14609         }
14610     },
14611
14612     // private
14613     onFirstFocus : function(){
14614         //Roo.log("onFirstFocus");
14615         this.editorcore.onFirstFocus();
14616          for (var i =0; i < this.toolbars.length;i++) {
14617             this.toolbars[i].onFirstFocus();
14618         }
14619         
14620     },
14621     
14622     // private
14623     syncValue : function()
14624     {   
14625         this.editorcore.syncValue();
14626     },
14627     
14628     pushValue : function()
14629     {   
14630         this.editorcore.pushValue();
14631     }
14632      
14633     
14634     // hide stuff that is not compatible
14635     /**
14636      * @event blur
14637      * @hide
14638      */
14639     /**
14640      * @event change
14641      * @hide
14642      */
14643     /**
14644      * @event focus
14645      * @hide
14646      */
14647     /**
14648      * @event specialkey
14649      * @hide
14650      */
14651     /**
14652      * @cfg {String} fieldClass @hide
14653      */
14654     /**
14655      * @cfg {String} focusClass @hide
14656      */
14657     /**
14658      * @cfg {String} autoCreate @hide
14659      */
14660     /**
14661      * @cfg {String} inputType @hide
14662      */
14663     /**
14664      * @cfg {String} invalidClass @hide
14665      */
14666     /**
14667      * @cfg {String} invalidText @hide
14668      */
14669     /**
14670      * @cfg {String} msgFx @hide
14671      */
14672     /**
14673      * @cfg {String} validateOnBlur @hide
14674      */
14675 });
14676  
14677     
14678    
14679    
14680    
14681       
14682
14683 /**
14684  * @class Roo.bootstrap.HtmlEditorToolbar1
14685  * Basic Toolbar
14686  * 
14687  * Usage:
14688  *
14689  new Roo.bootstrap.HtmlEditor({
14690     ....
14691     toolbars : [
14692         new Roo.bootstrap.HtmlEditorToolbar1({
14693             disable : { fonts: 1 , format: 1, ..., ... , ...],
14694             btns : [ .... ]
14695         })
14696     }
14697      
14698  * 
14699  * @cfg {Object} disable List of elements to disable..
14700  * @cfg {Array} btns List of additional buttons.
14701  * 
14702  * 
14703  * NEEDS Extra CSS? 
14704  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14705  */
14706  
14707 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14708 {
14709     
14710     Roo.apply(this, config);
14711     
14712     // default disabled, based on 'good practice'..
14713     this.disable = this.disable || {};
14714     Roo.applyIf(this.disable, {
14715         fontSize : true,
14716         colors : true,
14717         specialElements : true
14718     });
14719     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14720     
14721     this.editor = config.editor;
14722     this.editorcore = config.editor.editorcore;
14723     
14724     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14725     
14726     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14727     // dont call parent... till later.
14728 }
14729 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14730     
14731     
14732     bar : true,
14733     
14734     editor : false,
14735     editorcore : false,
14736     
14737     
14738     formats : [
14739         "p" ,  
14740         "h1","h2","h3","h4","h5","h6", 
14741         "pre", "code", 
14742         "abbr", "acronym", "address", "cite", "samp", "var",
14743         'div','span'
14744     ],
14745     
14746     onRender : function(ct, position)
14747     {
14748        // Roo.log("Call onRender: " + this.xtype);
14749         
14750        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14751        Roo.log(this.el);
14752        this.el.dom.style.marginBottom = '0';
14753        var _this = this;
14754        var editorcore = this.editorcore;
14755        var editor= this.editor;
14756        
14757        var children = [];
14758        var btn = function(id,cmd , toggle, handler){
14759        
14760             var  event = toggle ? 'toggle' : 'click';
14761        
14762             var a = {
14763                 size : 'sm',
14764                 xtype: 'Button',
14765                 xns: Roo.bootstrap,
14766                 glyphicon : id,
14767                 cmd : id || cmd,
14768                 enableToggle:toggle !== false,
14769                 //html : 'submit'
14770                 pressed : toggle ? false : null,
14771                 listeners : {}
14772             }
14773             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14774                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14775             }
14776             children.push(a);
14777             return a;
14778        }
14779         
14780         var style = {
14781                 xtype: 'Button',
14782                 size : 'sm',
14783                 xns: Roo.bootstrap,
14784                 glyphicon : 'font',
14785                 //html : 'submit'
14786                 menu : {
14787                     xtype: 'Menu',
14788                     xns: Roo.bootstrap,
14789                     items:  []
14790                 }
14791         };
14792         Roo.each(this.formats, function(f) {
14793             style.menu.items.push({
14794                 xtype :'MenuItem',
14795                 xns: Roo.bootstrap,
14796                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14797                 tagname : f,
14798                 listeners : {
14799                     click : function()
14800                     {
14801                         editorcore.insertTag(this.tagname);
14802                         editor.focus();
14803                     }
14804                 }
14805                 
14806             });
14807         });
14808          children.push(style);   
14809             
14810             
14811         btn('bold',false,true);
14812         btn('italic',false,true);
14813         btn('align-left', 'justifyleft',true);
14814         btn('align-center', 'justifycenter',true);
14815         btn('align-right' , 'justifyright',true);
14816         btn('link', false, false, function(btn) {
14817             //Roo.log("create link?");
14818             var url = prompt(this.createLinkText, this.defaultLinkValue);
14819             if(url && url != 'http:/'+'/'){
14820                 this.editorcore.relayCmd('createlink', url);
14821             }
14822         }),
14823         btn('list','insertunorderedlist',true);
14824         btn('pencil', false,true, function(btn){
14825                 Roo.log(this);
14826                 
14827                 this.toggleSourceEdit(btn.pressed);
14828         });
14829         /*
14830         var cog = {
14831                 xtype: 'Button',
14832                 size : 'sm',
14833                 xns: Roo.bootstrap,
14834                 glyphicon : 'cog',
14835                 //html : 'submit'
14836                 menu : {
14837                     xtype: 'Menu',
14838                     xns: Roo.bootstrap,
14839                     items:  []
14840                 }
14841         };
14842         
14843         cog.menu.items.push({
14844             xtype :'MenuItem',
14845             xns: Roo.bootstrap,
14846             html : Clean styles,
14847             tagname : f,
14848             listeners : {
14849                 click : function()
14850                 {
14851                     editorcore.insertTag(this.tagname);
14852                     editor.focus();
14853                 }
14854             }
14855             
14856         });
14857        */
14858         
14859          
14860        this.xtype = 'Navbar';
14861         
14862         for(var i=0;i< children.length;i++) {
14863             
14864             this.buttons.add(this.addxtypeChild(children[i]));
14865             
14866         }
14867         
14868         editor.on('editorevent', this.updateToolbar, this);
14869     },
14870     onBtnClick : function(id)
14871     {
14872        this.editorcore.relayCmd(id);
14873        this.editorcore.focus();
14874     },
14875     
14876     /**
14877      * Protected method that will not generally be called directly. It triggers
14878      * a toolbar update by reading the markup state of the current selection in the editor.
14879      */
14880     updateToolbar: function(){
14881
14882         if(!this.editorcore.activated){
14883             this.editor.onFirstFocus(); // is this neeed?
14884             return;
14885         }
14886
14887         var btns = this.buttons; 
14888         var doc = this.editorcore.doc;
14889         btns.get('bold').setActive(doc.queryCommandState('bold'));
14890         btns.get('italic').setActive(doc.queryCommandState('italic'));
14891         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14892         
14893         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14894         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14895         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14896         
14897         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14898         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14899          /*
14900         
14901         var ans = this.editorcore.getAllAncestors();
14902         if (this.formatCombo) {
14903             
14904             
14905             var store = this.formatCombo.store;
14906             this.formatCombo.setValue("");
14907             for (var i =0; i < ans.length;i++) {
14908                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14909                     // select it..
14910                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14911                     break;
14912                 }
14913             }
14914         }
14915         
14916         
14917         
14918         // hides menus... - so this cant be on a menu...
14919         Roo.bootstrap.MenuMgr.hideAll();
14920         */
14921         Roo.bootstrap.MenuMgr.hideAll();
14922         //this.editorsyncValue();
14923     },
14924     onFirstFocus: function() {
14925         this.buttons.each(function(item){
14926            item.enable();
14927         });
14928     },
14929     toggleSourceEdit : function(sourceEditMode){
14930         
14931           
14932         if(sourceEditMode){
14933             Roo.log("disabling buttons");
14934            this.buttons.each( function(item){
14935                 if(item.cmd != 'pencil'){
14936                     item.disable();
14937                 }
14938             });
14939           
14940         }else{
14941             Roo.log("enabling buttons");
14942             if(this.editorcore.initialized){
14943                 this.buttons.each( function(item){
14944                     item.enable();
14945                 });
14946             }
14947             
14948         }
14949         Roo.log("calling toggole on editor");
14950         // tell the editor that it's been pressed..
14951         this.editor.toggleSourceEdit(sourceEditMode);
14952        
14953     }
14954 });
14955
14956
14957
14958
14959
14960 /**
14961  * @class Roo.bootstrap.Table.AbstractSelectionModel
14962  * @extends Roo.util.Observable
14963  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14964  * implemented by descendant classes.  This class should not be directly instantiated.
14965  * @constructor
14966  */
14967 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14968     this.locked = false;
14969     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14970 };
14971
14972
14973 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14974     /** @ignore Called by the grid automatically. Do not call directly. */
14975     init : function(grid){
14976         this.grid = grid;
14977         this.initEvents();
14978     },
14979
14980     /**
14981      * Locks the selections.
14982      */
14983     lock : function(){
14984         this.locked = true;
14985     },
14986
14987     /**
14988      * Unlocks the selections.
14989      */
14990     unlock : function(){
14991         this.locked = false;
14992     },
14993
14994     /**
14995      * Returns true if the selections are locked.
14996      * @return {Boolean}
14997      */
14998     isLocked : function(){
14999         return this.locked;
15000     }
15001 });
15002 /**
15003  * @class Roo.bootstrap.Table.ColumnModel
15004  * @extends Roo.util.Observable
15005  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15006  * the columns in the table.
15007  
15008  * @constructor
15009  * @param {Object} config An Array of column config objects. See this class's
15010  * config objects for details.
15011 */
15012 Roo.bootstrap.Table.ColumnModel = function(config){
15013         /**
15014      * The config passed into the constructor
15015      */
15016     this.config = config;
15017     this.lookup = {};
15018
15019     // if no id, create one
15020     // if the column does not have a dataIndex mapping,
15021     // map it to the order it is in the config
15022     for(var i = 0, len = config.length; i < len; i++){
15023         var c = config[i];
15024         if(typeof c.dataIndex == "undefined"){
15025             c.dataIndex = i;
15026         }
15027         if(typeof c.renderer == "string"){
15028             c.renderer = Roo.util.Format[c.renderer];
15029         }
15030         if(typeof c.id == "undefined"){
15031             c.id = Roo.id();
15032         }
15033 //        if(c.editor && c.editor.xtype){
15034 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15035 //        }
15036 //        if(c.editor && c.editor.isFormField){
15037 //            c.editor = new Roo.grid.GridEditor(c.editor);
15038 //        }
15039
15040         this.lookup[c.id] = c;
15041     }
15042
15043     /**
15044      * The width of columns which have no width specified (defaults to 100)
15045      * @type Number
15046      */
15047     this.defaultWidth = 100;
15048
15049     /**
15050      * Default sortable of columns which have no sortable specified (defaults to false)
15051      * @type Boolean
15052      */
15053     this.defaultSortable = false;
15054
15055     this.addEvents({
15056         /**
15057              * @event widthchange
15058              * Fires when the width of a column changes.
15059              * @param {ColumnModel} this
15060              * @param {Number} columnIndex The column index
15061              * @param {Number} newWidth The new width
15062              */
15063             "widthchange": true,
15064         /**
15065              * @event headerchange
15066              * Fires when the text of a header changes.
15067              * @param {ColumnModel} this
15068              * @param {Number} columnIndex The column index
15069              * @param {Number} newText The new header text
15070              */
15071             "headerchange": true,
15072         /**
15073              * @event hiddenchange
15074              * Fires when a column is hidden or "unhidden".
15075              * @param {ColumnModel} this
15076              * @param {Number} columnIndex The column index
15077              * @param {Boolean} hidden true if hidden, false otherwise
15078              */
15079             "hiddenchange": true,
15080             /**
15081          * @event columnmoved
15082          * Fires when a column is moved.
15083          * @param {ColumnModel} this
15084          * @param {Number} oldIndex
15085          * @param {Number} newIndex
15086          */
15087         "columnmoved" : true,
15088         /**
15089          * @event columlockchange
15090          * Fires when a column's locked state is changed
15091          * @param {ColumnModel} this
15092          * @param {Number} colIndex
15093          * @param {Boolean} locked true if locked
15094          */
15095         "columnlockchange" : true
15096     });
15097     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15098 };
15099 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15100     /**
15101      * @cfg {String} header The header text to display in the Grid view.
15102      */
15103     /**
15104      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15105      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15106      * specified, the column's index is used as an index into the Record's data Array.
15107      */
15108     /**
15109      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15110      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15111      */
15112     /**
15113      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15114      * Defaults to the value of the {@link #defaultSortable} property.
15115      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15116      */
15117     /**
15118      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
15119      */
15120     /**
15121      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
15122      */
15123     /**
15124      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15125      */
15126     /**
15127      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15128      */
15129     /**
15130      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15131      * given the cell's data value. See {@link #setRenderer}. If not specified, the
15132      * default renderer uses the raw data value.
15133      */
15134     /**
15135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
15136      */
15137
15138     /**
15139      * Returns the id of the column at the specified index.
15140      * @param {Number} index The column index
15141      * @return {String} the id
15142      */
15143     getColumnId : function(index){
15144         return this.config[index].id;
15145     },
15146
15147     /**
15148      * Returns the column for a specified id.
15149      * @param {String} id The column id
15150      * @return {Object} the column
15151      */
15152     getColumnById : function(id){
15153         return this.lookup[id];
15154     },
15155
15156     
15157     /**
15158      * Returns the column for a specified dataIndex.
15159      * @param {String} dataIndex The column dataIndex
15160      * @return {Object|Boolean} the column or false if not found
15161      */
15162     getColumnByDataIndex: function(dataIndex){
15163         var index = this.findColumnIndex(dataIndex);
15164         return index > -1 ? this.config[index] : false;
15165     },
15166     
15167     /**
15168      * Returns the index for a specified column id.
15169      * @param {String} id The column id
15170      * @return {Number} the index, or -1 if not found
15171      */
15172     getIndexById : function(id){
15173         for(var i = 0, len = this.config.length; i < len; i++){
15174             if(this.config[i].id == id){
15175                 return i;
15176             }
15177         }
15178         return -1;
15179     },
15180     
15181     /**
15182      * Returns the index for a specified column dataIndex.
15183      * @param {String} dataIndex The column dataIndex
15184      * @return {Number} the index, or -1 if not found
15185      */
15186     
15187     findColumnIndex : function(dataIndex){
15188         for(var i = 0, len = this.config.length; i < len; i++){
15189             if(this.config[i].dataIndex == dataIndex){
15190                 return i;
15191             }
15192         }
15193         return -1;
15194     },
15195     
15196     
15197     moveColumn : function(oldIndex, newIndex){
15198         var c = this.config[oldIndex];
15199         this.config.splice(oldIndex, 1);
15200         this.config.splice(newIndex, 0, c);
15201         this.dataMap = null;
15202         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15203     },
15204
15205     isLocked : function(colIndex){
15206         return this.config[colIndex].locked === true;
15207     },
15208
15209     setLocked : function(colIndex, value, suppressEvent){
15210         if(this.isLocked(colIndex) == value){
15211             return;
15212         }
15213         this.config[colIndex].locked = value;
15214         if(!suppressEvent){
15215             this.fireEvent("columnlockchange", this, colIndex, value);
15216         }
15217     },
15218
15219     getTotalLockedWidth : function(){
15220         var totalWidth = 0;
15221         for(var i = 0; i < this.config.length; i++){
15222             if(this.isLocked(i) && !this.isHidden(i)){
15223                 this.totalWidth += this.getColumnWidth(i);
15224             }
15225         }
15226         return totalWidth;
15227     },
15228
15229     getLockedCount : function(){
15230         for(var i = 0, len = this.config.length; i < len; i++){
15231             if(!this.isLocked(i)){
15232                 return i;
15233             }
15234         }
15235     },
15236
15237     /**
15238      * Returns the number of columns.
15239      * @return {Number}
15240      */
15241     getColumnCount : function(visibleOnly){
15242         if(visibleOnly === true){
15243             var c = 0;
15244             for(var i = 0, len = this.config.length; i < len; i++){
15245                 if(!this.isHidden(i)){
15246                     c++;
15247                 }
15248             }
15249             return c;
15250         }
15251         return this.config.length;
15252     },
15253
15254     /**
15255      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15256      * @param {Function} fn
15257      * @param {Object} scope (optional)
15258      * @return {Array} result
15259      */
15260     getColumnsBy : function(fn, scope){
15261         var r = [];
15262         for(var i = 0, len = this.config.length; i < len; i++){
15263             var c = this.config[i];
15264             if(fn.call(scope||this, c, i) === true){
15265                 r[r.length] = c;
15266             }
15267         }
15268         return r;
15269     },
15270
15271     /**
15272      * Returns true if the specified column is sortable.
15273      * @param {Number} col The column index
15274      * @return {Boolean}
15275      */
15276     isSortable : function(col){
15277         if(typeof this.config[col].sortable == "undefined"){
15278             return this.defaultSortable;
15279         }
15280         return this.config[col].sortable;
15281     },
15282
15283     /**
15284      * Returns the rendering (formatting) function defined for the column.
15285      * @param {Number} col The column index.
15286      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15287      */
15288     getRenderer : function(col){
15289         if(!this.config[col].renderer){
15290             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15291         }
15292         return this.config[col].renderer;
15293     },
15294
15295     /**
15296      * Sets the rendering (formatting) function for a column.
15297      * @param {Number} col The column index
15298      * @param {Function} fn The function to use to process the cell's raw data
15299      * to return HTML markup for the grid view. The render function is called with
15300      * the following parameters:<ul>
15301      * <li>Data value.</li>
15302      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15303      * <li>css A CSS style string to apply to the table cell.</li>
15304      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15305      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15306      * <li>Row index</li>
15307      * <li>Column index</li>
15308      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15309      */
15310     setRenderer : function(col, fn){
15311         this.config[col].renderer = fn;
15312     },
15313
15314     /**
15315      * Returns the width for the specified column.
15316      * @param {Number} col The column index
15317      * @return {Number}
15318      */
15319     getColumnWidth : function(col){
15320         return this.config[col].width * 1 || this.defaultWidth;
15321     },
15322
15323     /**
15324      * Sets the width for a column.
15325      * @param {Number} col The column index
15326      * @param {Number} width The new width
15327      */
15328     setColumnWidth : function(col, width, suppressEvent){
15329         this.config[col].width = width;
15330         this.totalWidth = null;
15331         if(!suppressEvent){
15332              this.fireEvent("widthchange", this, col, width);
15333         }
15334     },
15335
15336     /**
15337      * Returns the total width of all columns.
15338      * @param {Boolean} includeHidden True to include hidden column widths
15339      * @return {Number}
15340      */
15341     getTotalWidth : function(includeHidden){
15342         if(!this.totalWidth){
15343             this.totalWidth = 0;
15344             for(var i = 0, len = this.config.length; i < len; i++){
15345                 if(includeHidden || !this.isHidden(i)){
15346                     this.totalWidth += this.getColumnWidth(i);
15347                 }
15348             }
15349         }
15350         return this.totalWidth;
15351     },
15352
15353     /**
15354      * Returns the header for the specified column.
15355      * @param {Number} col The column index
15356      * @return {String}
15357      */
15358     getColumnHeader : function(col){
15359         return this.config[col].header;
15360     },
15361
15362     /**
15363      * Sets the header for a column.
15364      * @param {Number} col The column index
15365      * @param {String} header The new header
15366      */
15367     setColumnHeader : function(col, header){
15368         this.config[col].header = header;
15369         this.fireEvent("headerchange", this, col, header);
15370     },
15371
15372     /**
15373      * Returns the tooltip for the specified column.
15374      * @param {Number} col The column index
15375      * @return {String}
15376      */
15377     getColumnTooltip : function(col){
15378             return this.config[col].tooltip;
15379     },
15380     /**
15381      * Sets the tooltip for a column.
15382      * @param {Number} col The column index
15383      * @param {String} tooltip The new tooltip
15384      */
15385     setColumnTooltip : function(col, tooltip){
15386             this.config[col].tooltip = tooltip;
15387     },
15388
15389     /**
15390      * Returns the dataIndex for the specified column.
15391      * @param {Number} col The column index
15392      * @return {Number}
15393      */
15394     getDataIndex : function(col){
15395         return this.config[col].dataIndex;
15396     },
15397
15398     /**
15399      * Sets the dataIndex for a column.
15400      * @param {Number} col The column index
15401      * @param {Number} dataIndex The new dataIndex
15402      */
15403     setDataIndex : function(col, dataIndex){
15404         this.config[col].dataIndex = dataIndex;
15405     },
15406
15407     
15408     
15409     /**
15410      * Returns true if the cell is editable.
15411      * @param {Number} colIndex The column index
15412      * @param {Number} rowIndex The row index
15413      * @return {Boolean}
15414      */
15415     isCellEditable : function(colIndex, rowIndex){
15416         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15417     },
15418
15419     /**
15420      * Returns the editor defined for the cell/column.
15421      * return false or null to disable editing.
15422      * @param {Number} colIndex The column index
15423      * @param {Number} rowIndex The row index
15424      * @return {Object}
15425      */
15426     getCellEditor : function(colIndex, rowIndex){
15427         return this.config[colIndex].editor;
15428     },
15429
15430     /**
15431      * Sets if a column is editable.
15432      * @param {Number} col The column index
15433      * @param {Boolean} editable True if the column is editable
15434      */
15435     setEditable : function(col, editable){
15436         this.config[col].editable = editable;
15437     },
15438
15439
15440     /**
15441      * Returns true if the column is hidden.
15442      * @param {Number} colIndex The column index
15443      * @return {Boolean}
15444      */
15445     isHidden : function(colIndex){
15446         return this.config[colIndex].hidden;
15447     },
15448
15449
15450     /**
15451      * Returns true if the column width cannot be changed
15452      */
15453     isFixed : function(colIndex){
15454         return this.config[colIndex].fixed;
15455     },
15456
15457     /**
15458      * Returns true if the column can be resized
15459      * @return {Boolean}
15460      */
15461     isResizable : function(colIndex){
15462         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15463     },
15464     /**
15465      * Sets if a column is hidden.
15466      * @param {Number} colIndex The column index
15467      * @param {Boolean} hidden True if the column is hidden
15468      */
15469     setHidden : function(colIndex, hidden){
15470         this.config[colIndex].hidden = hidden;
15471         this.totalWidth = null;
15472         this.fireEvent("hiddenchange", this, colIndex, hidden);
15473     },
15474
15475     /**
15476      * Sets the editor for a column.
15477      * @param {Number} col The column index
15478      * @param {Object} editor The editor object
15479      */
15480     setEditor : function(col, editor){
15481         this.config[col].editor = editor;
15482     }
15483 });
15484
15485 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15486         if(typeof value == "string" && value.length < 1){
15487             return "&#160;";
15488         }
15489         return value;
15490 };
15491
15492 // Alias for backwards compatibility
15493 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15494
15495 /**
15496  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15497  * @class Roo.bootstrap.Table.RowSelectionModel
15498  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15499  * It supports multiple selections and keyboard selection/navigation. 
15500  * @constructor
15501  * @param {Object} config
15502  */
15503
15504 Roo.bootstrap.Table.RowSelectionModel = function(config){
15505     Roo.apply(this, config);
15506     this.selections = new Roo.util.MixedCollection(false, function(o){
15507         return o.id;
15508     });
15509
15510     this.last = false;
15511     this.lastActive = false;
15512
15513     this.addEvents({
15514         /**
15515              * @event selectionchange
15516              * Fires when the selection changes
15517              * @param {SelectionModel} this
15518              */
15519             "selectionchange" : true,
15520         /**
15521              * @event afterselectionchange
15522              * Fires after the selection changes (eg. by key press or clicking)
15523              * @param {SelectionModel} this
15524              */
15525             "afterselectionchange" : true,
15526         /**
15527              * @event beforerowselect
15528              * Fires when a row is selected being selected, return false to cancel.
15529              * @param {SelectionModel} this
15530              * @param {Number} rowIndex The selected index
15531              * @param {Boolean} keepExisting False if other selections will be cleared
15532              */
15533             "beforerowselect" : true,
15534         /**
15535              * @event rowselect
15536              * Fires when a row is selected.
15537              * @param {SelectionModel} this
15538              * @param {Number} rowIndex The selected index
15539              * @param {Roo.data.Record} r The record
15540              */
15541             "rowselect" : true,
15542         /**
15543              * @event rowdeselect
15544              * Fires when a row is deselected.
15545              * @param {SelectionModel} this
15546              * @param {Number} rowIndex The selected index
15547              */
15548         "rowdeselect" : true
15549     });
15550     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15551     this.locked = false;
15552 };
15553
15554 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15555     /**
15556      * @cfg {Boolean} singleSelect
15557      * True to allow selection of only one row at a time (defaults to false)
15558      */
15559     singleSelect : false,
15560
15561     // private
15562     initEvents : function(){
15563
15564         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15565             this.grid.on("mousedown", this.handleMouseDown, this);
15566         }else{ // allow click to work like normal
15567             this.grid.on("rowclick", this.handleDragableRowClick, this);
15568         }
15569
15570         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15571             "up" : function(e){
15572                 if(!e.shiftKey){
15573                     this.selectPrevious(e.shiftKey);
15574                 }else if(this.last !== false && this.lastActive !== false){
15575                     var last = this.last;
15576                     this.selectRange(this.last,  this.lastActive-1);
15577                     this.grid.getView().focusRow(this.lastActive);
15578                     if(last !== false){
15579                         this.last = last;
15580                     }
15581                 }else{
15582                     this.selectFirstRow();
15583                 }
15584                 this.fireEvent("afterselectionchange", this);
15585             },
15586             "down" : function(e){
15587                 if(!e.shiftKey){
15588                     this.selectNext(e.shiftKey);
15589                 }else if(this.last !== false && this.lastActive !== false){
15590                     var last = this.last;
15591                     this.selectRange(this.last,  this.lastActive+1);
15592                     this.grid.getView().focusRow(this.lastActive);
15593                     if(last !== false){
15594                         this.last = last;
15595                     }
15596                 }else{
15597                     this.selectFirstRow();
15598                 }
15599                 this.fireEvent("afterselectionchange", this);
15600             },
15601             scope: this
15602         });
15603
15604         var view = this.grid.view;
15605         view.on("refresh", this.onRefresh, this);
15606         view.on("rowupdated", this.onRowUpdated, this);
15607         view.on("rowremoved", this.onRemove, this);
15608     },
15609
15610     // private
15611     onRefresh : function(){
15612         var ds = this.grid.dataSource, i, v = this.grid.view;
15613         var s = this.selections;
15614         s.each(function(r){
15615             if((i = ds.indexOfId(r.id)) != -1){
15616                 v.onRowSelect(i);
15617             }else{
15618                 s.remove(r);
15619             }
15620         });
15621     },
15622
15623     // private
15624     onRemove : function(v, index, r){
15625         this.selections.remove(r);
15626     },
15627
15628     // private
15629     onRowUpdated : function(v, index, r){
15630         if(this.isSelected(r)){
15631             v.onRowSelect(index);
15632         }
15633     },
15634
15635     /**
15636      * Select records.
15637      * @param {Array} records The records to select
15638      * @param {Boolean} keepExisting (optional) True to keep existing selections
15639      */
15640     selectRecords : function(records, keepExisting){
15641         if(!keepExisting){
15642             this.clearSelections();
15643         }
15644         var ds = this.grid.dataSource;
15645         for(var i = 0, len = records.length; i < len; i++){
15646             this.selectRow(ds.indexOf(records[i]), true);
15647         }
15648     },
15649
15650     /**
15651      * Gets the number of selected rows.
15652      * @return {Number}
15653      */
15654     getCount : function(){
15655         return this.selections.length;
15656     },
15657
15658     /**
15659      * Selects the first row in the grid.
15660      */
15661     selectFirstRow : function(){
15662         this.selectRow(0);
15663     },
15664
15665     /**
15666      * Select the last row.
15667      * @param {Boolean} keepExisting (optional) True to keep existing selections
15668      */
15669     selectLastRow : function(keepExisting){
15670         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15671     },
15672
15673     /**
15674      * Selects the row immediately following the last selected row.
15675      * @param {Boolean} keepExisting (optional) True to keep existing selections
15676      */
15677     selectNext : function(keepExisting){
15678         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15679             this.selectRow(this.last+1, keepExisting);
15680             this.grid.getView().focusRow(this.last);
15681         }
15682     },
15683
15684     /**
15685      * Selects the row that precedes the last selected row.
15686      * @param {Boolean} keepExisting (optional) True to keep existing selections
15687      */
15688     selectPrevious : function(keepExisting){
15689         if(this.last){
15690             this.selectRow(this.last-1, keepExisting);
15691             this.grid.getView().focusRow(this.last);
15692         }
15693     },
15694
15695     /**
15696      * Returns the selected records
15697      * @return {Array} Array of selected records
15698      */
15699     getSelections : function(){
15700         return [].concat(this.selections.items);
15701     },
15702
15703     /**
15704      * Returns the first selected record.
15705      * @return {Record}
15706      */
15707     getSelected : function(){
15708         return this.selections.itemAt(0);
15709     },
15710
15711
15712     /**
15713      * Clears all selections.
15714      */
15715     clearSelections : function(fast){
15716         if(this.locked) return;
15717         if(fast !== true){
15718             var ds = this.grid.dataSource;
15719             var s = this.selections;
15720             s.each(function(r){
15721                 this.deselectRow(ds.indexOfId(r.id));
15722             }, this);
15723             s.clear();
15724         }else{
15725             this.selections.clear();
15726         }
15727         this.last = false;
15728     },
15729
15730
15731     /**
15732      * Selects all rows.
15733      */
15734     selectAll : function(){
15735         if(this.locked) return;
15736         this.selections.clear();
15737         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15738             this.selectRow(i, true);
15739         }
15740     },
15741
15742     /**
15743      * Returns True if there is a selection.
15744      * @return {Boolean}
15745      */
15746     hasSelection : function(){
15747         return this.selections.length > 0;
15748     },
15749
15750     /**
15751      * Returns True if the specified row is selected.
15752      * @param {Number/Record} record The record or index of the record to check
15753      * @return {Boolean}
15754      */
15755     isSelected : function(index){
15756         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15757         return (r && this.selections.key(r.id) ? true : false);
15758     },
15759
15760     /**
15761      * Returns True if the specified record id is selected.
15762      * @param {String} id The id of record to check
15763      * @return {Boolean}
15764      */
15765     isIdSelected : function(id){
15766         return (this.selections.key(id) ? true : false);
15767     },
15768
15769     // private
15770     handleMouseDown : function(e, t){
15771         var view = this.grid.getView(), rowIndex;
15772         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15773             return;
15774         };
15775         if(e.shiftKey && this.last !== false){
15776             var last = this.last;
15777             this.selectRange(last, rowIndex, e.ctrlKey);
15778             this.last = last; // reset the last
15779             view.focusRow(rowIndex);
15780         }else{
15781             var isSelected = this.isSelected(rowIndex);
15782             if(e.button !== 0 && isSelected){
15783                 view.focusRow(rowIndex);
15784             }else if(e.ctrlKey && isSelected){
15785                 this.deselectRow(rowIndex);
15786             }else if(!isSelected){
15787                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15788                 view.focusRow(rowIndex);
15789             }
15790         }
15791         this.fireEvent("afterselectionchange", this);
15792     },
15793     // private
15794     handleDragableRowClick :  function(grid, rowIndex, e) 
15795     {
15796         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15797             this.selectRow(rowIndex, false);
15798             grid.view.focusRow(rowIndex);
15799              this.fireEvent("afterselectionchange", this);
15800         }
15801     },
15802     
15803     /**
15804      * Selects multiple rows.
15805      * @param {Array} rows Array of the indexes of the row to select
15806      * @param {Boolean} keepExisting (optional) True to keep existing selections
15807      */
15808     selectRows : function(rows, keepExisting){
15809         if(!keepExisting){
15810             this.clearSelections();
15811         }
15812         for(var i = 0, len = rows.length; i < len; i++){
15813             this.selectRow(rows[i], true);
15814         }
15815     },
15816
15817     /**
15818      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15819      * @param {Number} startRow The index of the first row in the range
15820      * @param {Number} endRow The index of the last row in the range
15821      * @param {Boolean} keepExisting (optional) True to retain existing selections
15822      */
15823     selectRange : function(startRow, endRow, keepExisting){
15824         if(this.locked) return;
15825         if(!keepExisting){
15826             this.clearSelections();
15827         }
15828         if(startRow <= endRow){
15829             for(var i = startRow; i <= endRow; i++){
15830                 this.selectRow(i, true);
15831             }
15832         }else{
15833             for(var i = startRow; i >= endRow; i--){
15834                 this.selectRow(i, true);
15835             }
15836         }
15837     },
15838
15839     /**
15840      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15841      * @param {Number} startRow The index of the first row in the range
15842      * @param {Number} endRow The index of the last row in the range
15843      */
15844     deselectRange : function(startRow, endRow, preventViewNotify){
15845         if(this.locked) return;
15846         for(var i = startRow; i <= endRow; i++){
15847             this.deselectRow(i, preventViewNotify);
15848         }
15849     },
15850
15851     /**
15852      * Selects a row.
15853      * @param {Number} row The index of the row to select
15854      * @param {Boolean} keepExisting (optional) True to keep existing selections
15855      */
15856     selectRow : function(index, keepExisting, preventViewNotify){
15857         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15858         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15859             if(!keepExisting || this.singleSelect){
15860                 this.clearSelections();
15861             }
15862             var r = this.grid.dataSource.getAt(index);
15863             this.selections.add(r);
15864             this.last = this.lastActive = index;
15865             if(!preventViewNotify){
15866                 this.grid.getView().onRowSelect(index);
15867             }
15868             this.fireEvent("rowselect", this, index, r);
15869             this.fireEvent("selectionchange", this);
15870         }
15871     },
15872
15873     /**
15874      * Deselects a row.
15875      * @param {Number} row The index of the row to deselect
15876      */
15877     deselectRow : function(index, preventViewNotify){
15878         if(this.locked) return;
15879         if(this.last == index){
15880             this.last = false;
15881         }
15882         if(this.lastActive == index){
15883             this.lastActive = false;
15884         }
15885         var r = this.grid.dataSource.getAt(index);
15886         this.selections.remove(r);
15887         if(!preventViewNotify){
15888             this.grid.getView().onRowDeselect(index);
15889         }
15890         this.fireEvent("rowdeselect", this, index);
15891         this.fireEvent("selectionchange", this);
15892     },
15893
15894     // private
15895     restoreLast : function(){
15896         if(this._last){
15897             this.last = this._last;
15898         }
15899     },
15900
15901     // private
15902     acceptsNav : function(row, col, cm){
15903         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15904     },
15905
15906     // private
15907     onEditorKey : function(field, e){
15908         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15909         if(k == e.TAB){
15910             e.stopEvent();
15911             ed.completeEdit();
15912             if(e.shiftKey){
15913                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15914             }else{
15915                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15916             }
15917         }else if(k == e.ENTER && !e.ctrlKey){
15918             e.stopEvent();
15919             ed.completeEdit();
15920             if(e.shiftKey){
15921                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15922             }else{
15923                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15924             }
15925         }else if(k == e.ESC){
15926             ed.cancelEdit();
15927         }
15928         if(newCell){
15929             g.startEditing(newCell[0], newCell[1]);
15930         }
15931     }
15932 });/*
15933  * - LGPL
15934  *
15935  * element
15936  * 
15937  */
15938
15939 /**
15940  * @class Roo.bootstrap.MessageBar
15941  * @extends Roo.bootstrap.Component
15942  * Bootstrap MessageBar class
15943  * @cfg {String} html contents of the MessageBar
15944  * @cfg {String} weight (info | success | warning | danger) default info
15945  * @cfg {String} beforeClass insert the bar before the given class
15946  * @cfg {Boolean} closable (true | false) default false
15947  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15948  * 
15949  * @constructor
15950  * Create a new Element
15951  * @param {Object} config The config object
15952  */
15953
15954 Roo.bootstrap.MessageBar = function(config){
15955     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15956 };
15957
15958 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15959     
15960     html: '',
15961     weight: 'info',
15962     closable: false,
15963     fixed: false,
15964     beforeClass: 'bootstrap-sticky-wrap',
15965     
15966     getAutoCreate : function(){
15967         
15968         var cfg = {
15969             tag: 'div',
15970             cls: 'alert alert-dismissable alert-' + this.weight,
15971             cn: [
15972                 {
15973                     tag: 'span',
15974                     cls: 'message',
15975                     html: this.html || ''
15976                 }
15977             ]
15978         }
15979         
15980         if(this.fixed){
15981             cfg.cls += ' alert-messages-fixed';
15982         }
15983         
15984         if(this.closable){
15985             cfg.cn.push({
15986                 tag: 'button',
15987                 cls: 'close',
15988                 html: 'x'
15989             });
15990         }
15991         
15992         return cfg;
15993     },
15994     
15995     onRender : function(ct, position)
15996     {
15997         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15998         
15999         if(!this.el){
16000             var cfg = Roo.apply({},  this.getAutoCreate());
16001             cfg.id = Roo.id();
16002             
16003             if (this.cls) {
16004                 cfg.cls += ' ' + this.cls;
16005             }
16006             if (this.style) {
16007                 cfg.style = this.style;
16008             }
16009             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16010             
16011             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16012         }
16013         
16014         this.el.select('>button.close').on('click', this.hide, this);
16015         
16016     },
16017     
16018     show : function()
16019     {
16020         if (!this.rendered) {
16021             this.render();
16022         }
16023         
16024         this.el.show();
16025         
16026         this.fireEvent('show', this);
16027         
16028     },
16029     
16030     hide : function()
16031     {
16032         if (!this.rendered) {
16033             this.render();
16034         }
16035         
16036         this.el.hide();
16037         
16038         this.fireEvent('hide', this);
16039     },
16040     
16041     update : function()
16042     {
16043 //        var e = this.el.dom.firstChild;
16044 //        
16045 //        if(this.closable){
16046 //            e = e.nextSibling;
16047 //        }
16048 //        
16049 //        e.data = this.html || '';
16050
16051         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16052     }
16053    
16054 });
16055
16056  
16057
16058