roojs-bootstrap.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.isActive()) {
2339                 v.setActive(false, true);
2340                 prev = v;
2341                 
2342             }
2343             
2344         });
2345
2346         item.setActive(true, true);
2347         this.fireEvent('changed', this, item, prev);
2348         
2349         
2350     },
2351     
2352     
2353     register : function(item)
2354     {
2355         this.navItems.push( item);
2356         item.navId = this.navId;
2357     
2358     },
2359     getNavItem: function(tabId)
2360     {
2361         var ret = false;
2362         Roo.each(this.navItems, function(e) {
2363             if (e.tabId == tabId) {
2364                ret =  e;
2365                return false;
2366             }
2367             return true;
2368             
2369         });
2370         return ret;
2371     }
2372 });
2373
2374  
2375 Roo.apply(Roo.bootstrap.NavGroup, {
2376     
2377     groups: {},
2378     
2379     register : function(navgrp)
2380     {
2381         this.groups[navgrp.navId] = navgrp;
2382         
2383     },
2384     get: function(navId) {
2385         return this.groups[navId];
2386     }
2387     
2388     
2389     
2390 });
2391
2392  /*
2393  * - LGPL
2394  *
2395  * row
2396  * 
2397  */
2398
2399 /**
2400  * @class Roo.bootstrap.Navbar.Item
2401  * @extends Roo.bootstrap.Component
2402  * Bootstrap Navbar.Button class
2403  * @cfg {String} href  link to
2404  * @cfg {String} html content of button
2405  * @cfg {String} badge text inside badge
2406  * @cfg {String} glyphicon name of glyphicon
2407  * @cfg {String} icon name of font awesome icon
2408  * @cfg {Boolean} active Is item active
2409  * @cfg {Boolean} preventDefault (true | false) default false
2410  * @cfg {String} tabId the tab that this item activates.
2411   
2412  * @constructor
2413  * Create a new Navbar Button
2414  * @param {Object} config The config object
2415  */
2416 Roo.bootstrap.Navbar.Item = function(config){
2417     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2418     this.addEvents({
2419         // raw events
2420         /**
2421          * @event click
2422          * The raw click event for the entire grid.
2423          * @param {Roo.EventObject} e
2424          */
2425         "click" : true,
2426          /**
2427             * @event changed
2428             * Fires when the active item active state changes
2429             * @param {Roo.bootstrap.Navbar.Item} this
2430             * @param {boolean} state the new state
2431              
2432          */
2433         'changed': true
2434     });
2435    
2436 };
2437
2438 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2439     
2440     href: false,
2441     html: '',
2442     badge: '',
2443     icon: false,
2444     glyphicon: false,
2445     active: false,
2446     preventDefault : false,
2447     tabId : false,
2448     
2449     getAutoCreate : function(){
2450         
2451         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2452         
2453         if (this.parent().parent().sidebar === true) {
2454             cfg = {
2455                 tag: 'li',
2456                 cls: '',
2457                 cn: [
2458                     {
2459                         tag: 'p',
2460                         cls: ''
2461                     }
2462                 ]
2463             }
2464             
2465             if (this.html) {
2466                 cfg.cn[0].html = this.html;
2467             }
2468             
2469             if (this.active) {
2470                 this.cls += ' active';
2471             }
2472             
2473             if (this.menu) {
2474                 cfg.cn[0].cls += ' dropdown-toggle';
2475                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2476             }
2477             
2478             if (this.href) {
2479                 cfg.cn[0].tag = 'a',
2480                 cfg.cn[0].href = this.href;
2481             }
2482             
2483             if (this.glyphicon) {
2484                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2485             }
2486             
2487             if (this.icon) {
2488                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2489             }
2490             
2491             return cfg;
2492         }
2493         
2494         cfg = {
2495             tag: 'li',
2496             cls: 'nav-item'
2497         }
2498         
2499         if (this.active) {
2500             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2501         }
2502             
2503         cfg.cn = [
2504             {
2505                 tag: 'p',
2506                 html: 'Text'
2507             }
2508         ];
2509         
2510         if (this.glyphicon) {
2511             if(cfg.html){cfg.html = ' ' + this.html};
2512             cfg.cn=[
2513                 {
2514                     tag: 'span',
2515                     cls: 'glyphicon glyphicon-' + this.glyphicon
2516                 }
2517             ];
2518         }
2519         
2520         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2521         
2522         if (this.menu) {
2523             cfg.cn[0].tag='a';
2524             cfg.cn[0].href='#';
2525             cfg.cn[0].html += " <span class='caret'></span>";
2526         //}else if (!this.href) {
2527         //    cfg.cn[0].tag='p';
2528         //    cfg.cn[0].cls='navbar-text';
2529         } else {
2530             cfg.cn[0].tag='a';
2531             cfg.cn[0].href=this.href||'#';
2532             cfg.cn[0].html=this.html;
2533         }
2534         
2535         if (this.badge !== '') {
2536             
2537             cfg.cn[0].cn=[
2538                 cfg.cn[0].html + ' ',
2539                 {
2540                     tag: 'span',
2541                     cls: 'badge',
2542                     html: this.badge
2543                 }
2544             ];
2545             cfg.cn[0].html=''
2546         }
2547          
2548         if (this.icon) {
2549             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2550         }
2551         
2552         return cfg;
2553     },
2554     initEvents: function() {
2555        // Roo.log('init events?');
2556        // Roo.log(this.el.dom);
2557         this.el.select('a',true).on('click', this.onClick, this);
2558         // at this point parent should be available..
2559         this.parent().register(this);
2560     },
2561     
2562     onClick : function(e)
2563     {
2564         if(this.preventDefault){
2565             e.preventDefault();
2566         }
2567         
2568         if(this.fireEvent('click', this, e) === false){
2569             return;
2570         };
2571         
2572         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2573              if (typeof(this.parent().setActiveItem) !== 'undefined') {
2574                 this.parent().setActiveItem(this);
2575             }
2576             
2577             
2578             
2579         } 
2580     },
2581     
2582     isActive: function () {
2583         return this.active
2584     },
2585     setActive : function(state, fire)
2586     {
2587         this.active = state;
2588         if (!state ) {
2589             this.el.removeClass('active');
2590         } else if (!this.el.hasClass('active')) {
2591             this.el.addClass('active');
2592         }
2593         if (fire) {
2594             this.fireEvent('changed', this, state);
2595         }
2596         
2597         
2598     }
2599      // this should not be here...
2600  
2601 });
2602  
2603
2604  /*
2605  * - LGPL
2606  *
2607  * row
2608  * 
2609  */
2610
2611 /**
2612  * @class Roo.bootstrap.Row
2613  * @extends Roo.bootstrap.Component
2614  * Bootstrap Row class (contains columns...)
2615  * 
2616  * @constructor
2617  * Create a new Row
2618  * @param {Object} config The config object
2619  */
2620
2621 Roo.bootstrap.Row = function(config){
2622     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2623 };
2624
2625 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2626     
2627     getAutoCreate : function(){
2628        return {
2629             cls: 'row clearfix'
2630        };
2631     }
2632     
2633     
2634 });
2635
2636  
2637
2638  /*
2639  * - LGPL
2640  *
2641  * element
2642  * 
2643  */
2644
2645 /**
2646  * @class Roo.bootstrap.Element
2647  * @extends Roo.bootstrap.Component
2648  * Bootstrap Element class
2649  * @cfg {String} html contents of the element
2650  * @cfg {String} tag tag of the element
2651  * @cfg {String} cls class of the element
2652  * 
2653  * @constructor
2654  * Create a new Element
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Element = function(config){
2659     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2660 };
2661
2662 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2663     
2664     tag: 'div',
2665     cls: '',
2666     html: '',
2667      
2668     
2669     getAutoCreate : function(){
2670         
2671         var cfg = {
2672             tag: this.tag,
2673             cls: this.cls,
2674             html: this.html
2675         }
2676         
2677         
2678         
2679         return cfg;
2680     }
2681    
2682 });
2683
2684  
2685
2686  /*
2687  * - LGPL
2688  *
2689  * pagination
2690  * 
2691  */
2692
2693 /**
2694  * @class Roo.bootstrap.Pagination
2695  * @extends Roo.bootstrap.Component
2696  * Bootstrap Pagination class
2697  * @cfg {String} size xs | sm | md | lg
2698  * @cfg {Boolean} inverse false | true
2699  * 
2700  * @constructor
2701  * Create a new Pagination
2702  * @param {Object} config The config object
2703  */
2704
2705 Roo.bootstrap.Pagination = function(config){
2706     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2707 };
2708
2709 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2710     
2711     cls: false,
2712     size: false,
2713     inverse: false,
2714     
2715     getAutoCreate : function(){
2716         var cfg = {
2717             tag: 'ul',
2718                 cls: 'pagination'
2719         };
2720         if (this.inverse) {
2721             cfg.cls += ' inverse';
2722         }
2723         if (this.html) {
2724             cfg.html=this.html;
2725         }
2726         if (this.cls) {
2727             cfg.cls += " " + this.cls;
2728         }
2729         return cfg;
2730     }
2731    
2732 });
2733
2734  
2735
2736  /*
2737  * - LGPL
2738  *
2739  * Pagination item
2740  * 
2741  */
2742
2743
2744 /**
2745  * @class Roo.bootstrap.PaginationItem
2746  * @extends Roo.bootstrap.Component
2747  * Bootstrap PaginationItem class
2748  * @cfg {String} html text
2749  * @cfg {String} href the link
2750  * @cfg {Boolean} preventDefault (true | false) default true
2751  * @cfg {Boolean} active (true | false) default false
2752  * 
2753  * 
2754  * @constructor
2755  * Create a new PaginationItem
2756  * @param {Object} config The config object
2757  */
2758
2759
2760 Roo.bootstrap.PaginationItem = function(config){
2761     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2762     this.addEvents({
2763         // raw events
2764         /**
2765          * @event click
2766          * The raw click event for the entire grid.
2767          * @param {Roo.EventObject} e
2768          */
2769         "click" : true
2770     });
2771 };
2772
2773 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2774     
2775     href : false,
2776     html : false,
2777     preventDefault: true,
2778     active : false,
2779     cls : false,
2780     
2781     getAutoCreate : function(){
2782         var cfg= {
2783             tag: 'li',
2784             cn: [
2785                 {
2786                     tag : 'a',
2787                     href : this.href ? this.href : '#',
2788                     html : this.html ? this.html : ''
2789                 }
2790             ]
2791         };
2792         
2793         if(this.cls){
2794             cfg.cls = this.cls;
2795         }
2796         
2797         if(this.active){
2798             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2799         }
2800         
2801         return cfg;
2802     },
2803     
2804     initEvents: function() {
2805         
2806         this.el.on('click', this.onClick, this);
2807         
2808     },
2809     onClick : function(e)
2810     {
2811         Roo.log('PaginationItem on click ');
2812         if(this.preventDefault){
2813             e.preventDefault();
2814         }
2815         
2816         this.fireEvent('click', this, e);
2817     }
2818    
2819 });
2820
2821  
2822
2823  /*
2824  * - LGPL
2825  *
2826  * slider
2827  * 
2828  */
2829
2830
2831 /**
2832  * @class Roo.bootstrap.Slider
2833  * @extends Roo.bootstrap.Component
2834  * Bootstrap Slider class
2835  *    
2836  * @constructor
2837  * Create a new Slider
2838  * @param {Object} config The config object
2839  */
2840
2841 Roo.bootstrap.Slider = function(config){
2842     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2843 };
2844
2845 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2846     
2847     getAutoCreate : function(){
2848         
2849         var cfg = {
2850             tag: 'div',
2851             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2852             cn: [
2853                 {
2854                     tag: 'a',
2855                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2856                 }
2857             ]
2858         }
2859         
2860         return cfg;
2861     }
2862    
2863 });
2864
2865  /*
2866  * - LGPL
2867  *
2868  * table
2869  * 
2870  */
2871
2872 /**
2873  * @class Roo.bootstrap.Table
2874  * @extends Roo.bootstrap.Component
2875  * Bootstrap Table class
2876  * @cfg {String} cls table class
2877  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2878  * @cfg {String} bgcolor Specifies the background color for a table
2879  * @cfg {Number} border Specifies whether the table cells should have borders or not
2880  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2881  * @cfg {Number} cellspacing Specifies the space between cells
2882  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2883  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2884  * @cfg {String} sortable Specifies that the table should be sortable
2885  * @cfg {String} summary Specifies a summary of the content of a table
2886  * @cfg {Number} width Specifies the width of a table
2887  * 
2888  * @cfg {boolean} striped Should the rows be alternative striped
2889  * @cfg {boolean} bordered Add borders to the table
2890  * @cfg {boolean} hover Add hover highlighting
2891  * @cfg {boolean} condensed Format condensed
2892  * @cfg {boolean} responsive Format condensed
2893  *
2894  
2895  
2896  * 
2897  * @constructor
2898  * Create a new Table
2899  * @param {Object} config The config object
2900  */
2901
2902 Roo.bootstrap.Table = function(config){
2903     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2904     
2905     if (this.sm) {
2906         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2907         this.sm = this.selModel;
2908         this.sm.xmodule = this.xmodule || false;
2909     }
2910     if (this.cm && typeof(this.cm.config) == 'undefined') {
2911         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2912         this.cm = this.colModel;
2913         this.cm.xmodule = this.xmodule || false;
2914     }
2915     if (this.store) {
2916         this.store= Roo.factory(this.store, Roo.data);
2917         this.ds = this.store;
2918         this.ds.xmodule = this.xmodule || false;
2919          
2920     }
2921 };
2922
2923 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2924     
2925     cls: false,
2926     align: false,
2927     bgcolor: false,
2928     border: false,
2929     cellpadding: false,
2930     cellspacing: false,
2931     frame: false,
2932     rules: false,
2933     sortable: false,
2934     summary: false,
2935     width: false,
2936     striped : false,
2937     bordered: false,
2938     hover:  false,
2939     condensed : false,
2940     responsive : false,
2941     sm : false,
2942     cm : false,
2943     store : false,
2944     
2945     getAutoCreate : function(){
2946         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2947         
2948         cfg = {
2949             tag: 'table',
2950             cls : 'table',
2951             cn : []
2952         }
2953             
2954         if (this.striped) {
2955             cfg.cls += ' table-striped';
2956         }
2957         if (this.hover) {
2958             cfg.cls += ' table-hover';
2959         }
2960         if (this.bordered) {
2961             cfg.cls += ' table-bordered';
2962         }
2963         if (this.condensed) {
2964             cfg.cls += ' table-condensed';
2965         }
2966         if (this.responsive) {
2967             cfg.cls += ' table-responsive';
2968         }
2969         
2970           
2971         
2972         
2973         if (this.cls) {
2974             cfg.cls+=  ' ' +this.cls;
2975         }
2976         
2977         // this lot should be simplifed...
2978         
2979         if (this.align) {
2980             cfg.align=this.align;
2981         }
2982         if (this.bgcolor) {
2983             cfg.bgcolor=this.bgcolor;
2984         }
2985         if (this.border) {
2986             cfg.border=this.border;
2987         }
2988         if (this.cellpadding) {
2989             cfg.cellpadding=this.cellpadding;
2990         }
2991         if (this.cellspacing) {
2992             cfg.cellspacing=this.cellspacing;
2993         }
2994         if (this.frame) {
2995             cfg.frame=this.frame;
2996         }
2997         if (this.rules) {
2998             cfg.rules=this.rules;
2999         }
3000         if (this.sortable) {
3001             cfg.sortable=this.sortable;
3002         }
3003         if (this.summary) {
3004             cfg.summary=this.summary;
3005         }
3006         if (this.width) {
3007             cfg.width=this.width;
3008         }
3009         
3010         if(this.store || this.cm){
3011             cfg.cn.push(this.renderHeader());
3012             cfg.cn.push(this.renderBody());
3013             cfg.cn.push(this.renderFooter());
3014             
3015             cfg.cls+=  ' TableGrid';
3016         }
3017         
3018         return cfg;
3019     },
3020 //    
3021 //    initTableGrid : function()
3022 //    {
3023 //        var cfg = {};
3024 //        
3025 //        var header = {
3026 //            tag: 'thead',
3027 //            cn : []
3028 //        };
3029 //        
3030 //        var cm = this.cm;
3031 //        
3032 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3033 //            header.cn.push({
3034 //                tag: 'th',
3035 //                html: cm.getColumnHeader(i)
3036 //            })
3037 //        }
3038 //        
3039 //        cfg.push(header);
3040 //        
3041 //        return cfg;
3042 //        
3043 //        
3044 //    },
3045     
3046     initEvents : function()
3047     {   
3048         if(!this.store || !this.cm){
3049             return;
3050         }
3051         
3052         Roo.log('initEvents with ds!!!!');
3053         
3054         var _this = this;
3055         
3056         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3057             e.on('click', _this.sort, _this);
3058         });
3059 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3060 //        this.maskEl.enableDisplayMode("block");
3061 //        this.maskEl.show();
3062         
3063         this.store.on('load', this.onLoad, this);
3064         this.store.on('beforeload', this.onBeforeLoad, this);
3065         
3066         this.store.load();
3067         
3068         
3069         
3070     },
3071     
3072     sort : function(e,el)
3073     {
3074         var col = Roo.get(el)
3075         
3076         if(!col.hasClass('sortable')){
3077             return;
3078         }
3079         
3080         var sort = col.attr('sort');
3081         var dir = 'ASC';
3082         
3083         if(col.hasClass('glyphicon-arrow-up')){
3084             dir = 'DESC';
3085         }
3086         
3087         this.store.sortInfo = {field : sort, direction : dir};
3088         
3089         this.store.load();
3090     },
3091     
3092     renderHeader : function()
3093     {
3094         var header = {
3095             tag: 'thead',
3096             cn : []
3097         };
3098         
3099         var cm = this.cm;
3100         
3101         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3102             
3103             var config = cm.config[i];
3104             
3105             var c = {
3106                 tag: 'th',
3107                 html: cm.getColumnHeader(i)
3108             };
3109             
3110             if(typeof(config.dataIndex) != 'undefined'){
3111                 c.sort = config.dataIndex;
3112             }
3113             
3114             if(typeof(config.sortable) != 'undefined' && config.sortable){
3115                 c.cls = 'sortable';
3116             }
3117             
3118             if(typeof(config.width) != 'undefined'){
3119                 c.style = 'width:' + config.width + 'px';
3120             }
3121             
3122             header.cn.push(c)
3123         }
3124         
3125         return header;
3126     },
3127     
3128     renderBody : function()
3129     {
3130         var body = {
3131             tag: 'tbody',
3132             cn : []
3133         };
3134         
3135         return body;
3136     },
3137     
3138     renderFooter : function()
3139     {
3140         var footer = {
3141             tag: 'tfoot',
3142             cn : []
3143         };
3144         
3145         return footer;
3146     },
3147     
3148     onLoad : function()
3149     {
3150         Roo.log('ds onload');
3151         
3152         var _this = this;
3153         var cm = this.cm;
3154         
3155         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3156             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3157             
3158             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3159                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3160             }
3161             
3162             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3163                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3164             }
3165         });
3166         
3167         var tbody = this.el.select('tbody', true).first();
3168         
3169         var renders = [];
3170         
3171         if(this.store.getCount() > 0){
3172             this.store.data.each(function(d){
3173                 var row = {
3174                     tag : 'tr',
3175                     cn : []
3176                 };
3177                 
3178                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3179                     var renderer = cm.getRenderer(i);
3180                     var config = cm.config[i];
3181                     var value = '';
3182                     var id = Roo.id();
3183                     
3184                     if(typeof(renderer) !== 'undefined'){
3185                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3186                     }
3187                     
3188                     if(typeof(value) === 'object'){
3189                         renders.push({
3190                             id : id,
3191                             cfg : value 
3192                         })
3193                     }
3194                     
3195                     var td = {
3196                         tag: 'td',
3197                         id: id,
3198                         html: (typeof(value) === 'object') ? '' : value
3199                     };
3200                     
3201                     if(typeof(config.width) != 'undefined'){
3202                         td.style = 'width:' +  config.width + 'px';
3203                     }
3204                     
3205                     row.cn.push(td);
3206                    
3207                 }
3208                 
3209                 tbody.createChild(row);
3210                 
3211             });
3212         }
3213         
3214         
3215         if(renders.length){
3216             var _this = this;
3217             Roo.each(renders, function(r){
3218                 _this.renderColumn(r);
3219             })
3220         }
3221 //        
3222 //        if(this.loadMask){
3223 //            this.maskEl.hide();
3224 //        }
3225     },
3226     
3227     onBeforeLoad : function()
3228     {
3229         Roo.log('ds onBeforeLoad');
3230         
3231         this.clear();
3232         
3233 //        if(this.loadMask){
3234 //            this.maskEl.show();
3235 //        }
3236     },
3237     
3238     clear : function()
3239     {
3240         this.el.select('tbody', true).first().dom.innerHTML = '';
3241     },
3242     
3243     getSelectionModel : function(){
3244         if(!this.selModel){
3245             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3246         }
3247         return this.selModel;
3248     },
3249     
3250     renderColumn : function(r)
3251     {
3252         var _this = this;
3253         r.cfg.render(Roo.get(r.id));
3254         
3255         if(r.cfg.cn){
3256             Roo.each(r.cfg.cn, function(c){
3257                 var child = {
3258                     id: r.id,
3259                     cfg: c
3260                 }
3261                 _this.renderColumn(child);
3262             })
3263         }
3264     }
3265    
3266 });
3267
3268  
3269
3270  /*
3271  * - LGPL
3272  *
3273  * table cell
3274  * 
3275  */
3276
3277 /**
3278  * @class Roo.bootstrap.TableCell
3279  * @extends Roo.bootstrap.Component
3280  * Bootstrap TableCell class
3281  * @cfg {String} html cell contain text
3282  * @cfg {String} cls cell class
3283  * @cfg {String} tag cell tag (td|th) default td
3284  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3285  * @cfg {String} align Aligns the content in a cell
3286  * @cfg {String} axis Categorizes cells
3287  * @cfg {String} bgcolor Specifies the background color of a cell
3288  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3289  * @cfg {Number} colspan Specifies the number of columns a cell should span
3290  * @cfg {String} headers Specifies one or more header cells a cell is related to
3291  * @cfg {Number} height Sets the height of a cell
3292  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3293  * @cfg {Number} rowspan Sets the number of rows a cell should span
3294  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3295  * @cfg {String} valign Vertical aligns the content in a cell
3296  * @cfg {Number} width Specifies the width of a cell
3297  * 
3298  * @constructor
3299  * Create a new TableCell
3300  * @param {Object} config The config object
3301  */
3302
3303 Roo.bootstrap.TableCell = function(config){
3304     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3305 };
3306
3307 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3308     
3309     html: false,
3310     cls: false,
3311     tag: false,
3312     abbr: false,
3313     align: false,
3314     axis: false,
3315     bgcolor: false,
3316     charoff: false,
3317     colspan: false,
3318     headers: false,
3319     height: false,
3320     nowrap: false,
3321     rowspan: false,
3322     scope: false,
3323     valign: false,
3324     width: false,
3325     
3326     
3327     getAutoCreate : function(){
3328         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3329         
3330         cfg = {
3331             tag: 'td'
3332         }
3333         
3334         if(this.tag){
3335             cfg.tag = this.tag;
3336         }
3337         
3338         if (this.html) {
3339             cfg.html=this.html
3340         }
3341         if (this.cls) {
3342             cfg.cls=this.cls
3343         }
3344         if (this.abbr) {
3345             cfg.abbr=this.abbr
3346         }
3347         if (this.align) {
3348             cfg.align=this.align
3349         }
3350         if (this.axis) {
3351             cfg.axis=this.axis
3352         }
3353         if (this.bgcolor) {
3354             cfg.bgcolor=this.bgcolor
3355         }
3356         if (this.charoff) {
3357             cfg.charoff=this.charoff
3358         }
3359         if (this.colspan) {
3360             cfg.colspan=this.colspan
3361         }
3362         if (this.headers) {
3363             cfg.headers=this.headers
3364         }
3365         if (this.height) {
3366             cfg.height=this.height
3367         }
3368         if (this.nowrap) {
3369             cfg.nowrap=this.nowrap
3370         }
3371         if (this.rowspan) {
3372             cfg.rowspan=this.rowspan
3373         }
3374         if (this.scope) {
3375             cfg.scope=this.scope
3376         }
3377         if (this.valign) {
3378             cfg.valign=this.valign
3379         }
3380         if (this.width) {
3381             cfg.width=this.width
3382         }
3383         
3384         
3385         return cfg;
3386     }
3387    
3388 });
3389
3390  
3391
3392  /*
3393  * - LGPL
3394  *
3395  * table row
3396  * 
3397  */
3398
3399 /**
3400  * @class Roo.bootstrap.TableRow
3401  * @extends Roo.bootstrap.Component
3402  * Bootstrap TableRow class
3403  * @cfg {String} cls row class
3404  * @cfg {String} align Aligns the content in a table row
3405  * @cfg {String} bgcolor Specifies a background color for a table row
3406  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3407  * @cfg {String} valign Vertical aligns the content in a table row
3408  * 
3409  * @constructor
3410  * Create a new TableRow
3411  * @param {Object} config The config object
3412  */
3413
3414 Roo.bootstrap.TableRow = function(config){
3415     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3416 };
3417
3418 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3419     
3420     cls: false,
3421     align: false,
3422     bgcolor: false,
3423     charoff: false,
3424     valign: false,
3425     
3426     getAutoCreate : function(){
3427         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3428         
3429         cfg = {
3430             tag: 'tr'
3431         }
3432             
3433         if(this.cls){
3434             cfg.cls = this.cls;
3435         }
3436         if(this.align){
3437             cfg.align = this.align;
3438         }
3439         if(this.bgcolor){
3440             cfg.bgcolor = this.bgcolor;
3441         }
3442         if(this.charoff){
3443             cfg.charoff = this.charoff;
3444         }
3445         if(this.valign){
3446             cfg.valign = this.valign;
3447         }
3448         
3449         return cfg;
3450     }
3451    
3452 });
3453
3454  
3455
3456  /*
3457  * - LGPL
3458  *
3459  * table body
3460  * 
3461  */
3462
3463 /**
3464  * @class Roo.bootstrap.TableBody
3465  * @extends Roo.bootstrap.Component
3466  * Bootstrap TableBody class
3467  * @cfg {String} cls element class
3468  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3469  * @cfg {String} align Aligns the content inside the element
3470  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3471  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3472  * 
3473  * @constructor
3474  * Create a new TableBody
3475  * @param {Object} config The config object
3476  */
3477
3478 Roo.bootstrap.TableBody = function(config){
3479     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3480 };
3481
3482 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3483     
3484     cls: false,
3485     tag: false,
3486     align: false,
3487     charoff: false,
3488     valign: false,
3489     
3490     getAutoCreate : function(){
3491         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3492         
3493         cfg = {
3494             tag: 'tbody'
3495         }
3496             
3497         if (this.cls) {
3498             cfg.cls=this.cls
3499         }
3500         if(this.tag){
3501             cfg.tag = this.tag;
3502         }
3503         
3504         if(this.align){
3505             cfg.align = this.align;
3506         }
3507         if(this.charoff){
3508             cfg.charoff = this.charoff;
3509         }
3510         if(this.valign){
3511             cfg.valign = this.valign;
3512         }
3513         
3514         return cfg;
3515     }
3516     
3517     
3518 //    initEvents : function()
3519 //    {
3520 //        
3521 //        if(!this.store){
3522 //            return;
3523 //        }
3524 //        
3525 //        this.store = Roo.factory(this.store, Roo.data);
3526 //        this.store.on('load', this.onLoad, this);
3527 //        
3528 //        this.store.load();
3529 //        
3530 //    },
3531 //    
3532 //    onLoad: function () 
3533 //    {   
3534 //        this.fireEvent('load', this);
3535 //    }
3536 //    
3537 //   
3538 });
3539
3540  
3541
3542  /*
3543  * Based on:
3544  * Ext JS Library 1.1.1
3545  * Copyright(c) 2006-2007, Ext JS, LLC.
3546  *
3547  * Originally Released Under LGPL - original licence link has changed is not relivant.
3548  *
3549  * Fork - LGPL
3550  * <script type="text/javascript">
3551  */
3552
3553 // as we use this in bootstrap.
3554 Roo.namespace('Roo.form');
3555  /**
3556  * @class Roo.form.Action
3557  * Internal Class used to handle form actions
3558  * @constructor
3559  * @param {Roo.form.BasicForm} el The form element or its id
3560  * @param {Object} config Configuration options
3561  */
3562
3563  
3564  
3565 // define the action interface
3566 Roo.form.Action = function(form, options){
3567     this.form = form;
3568     this.options = options || {};
3569 };
3570 /**
3571  * Client Validation Failed
3572  * @const 
3573  */
3574 Roo.form.Action.CLIENT_INVALID = 'client';
3575 /**
3576  * Server Validation Failed
3577  * @const 
3578  */
3579 Roo.form.Action.SERVER_INVALID = 'server';
3580  /**
3581  * Connect to Server Failed
3582  * @const 
3583  */
3584 Roo.form.Action.CONNECT_FAILURE = 'connect';
3585 /**
3586  * Reading Data from Server Failed
3587  * @const 
3588  */
3589 Roo.form.Action.LOAD_FAILURE = 'load';
3590
3591 Roo.form.Action.prototype = {
3592     type : 'default',
3593     failureType : undefined,
3594     response : undefined,
3595     result : undefined,
3596
3597     // interface method
3598     run : function(options){
3599
3600     },
3601
3602     // interface method
3603     success : function(response){
3604
3605     },
3606
3607     // interface method
3608     handleResponse : function(response){
3609
3610     },
3611
3612     // default connection failure
3613     failure : function(response){
3614         
3615         this.response = response;
3616         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3617         this.form.afterAction(this, false);
3618     },
3619
3620     processResponse : function(response){
3621         this.response = response;
3622         if(!response.responseText){
3623             return true;
3624         }
3625         this.result = this.handleResponse(response);
3626         return this.result;
3627     },
3628
3629     // utility functions used internally
3630     getUrl : function(appendParams){
3631         var url = this.options.url || this.form.url || this.form.el.dom.action;
3632         if(appendParams){
3633             var p = this.getParams();
3634             if(p){
3635                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3636             }
3637         }
3638         return url;
3639     },
3640
3641     getMethod : function(){
3642         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3643     },
3644
3645     getParams : function(){
3646         var bp = this.form.baseParams;
3647         var p = this.options.params;
3648         if(p){
3649             if(typeof p == "object"){
3650                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3651             }else if(typeof p == 'string' && bp){
3652                 p += '&' + Roo.urlEncode(bp);
3653             }
3654         }else if(bp){
3655             p = Roo.urlEncode(bp);
3656         }
3657         return p;
3658     },
3659
3660     createCallback : function(){
3661         return {
3662             success: this.success,
3663             failure: this.failure,
3664             scope: this,
3665             timeout: (this.form.timeout*1000),
3666             upload: this.form.fileUpload ? this.success : undefined
3667         };
3668     }
3669 };
3670
3671 Roo.form.Action.Submit = function(form, options){
3672     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3673 };
3674
3675 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3676     type : 'submit',
3677
3678     haveProgress : false,
3679     uploadComplete : false,
3680     
3681     // uploadProgress indicator.
3682     uploadProgress : function()
3683     {
3684         if (!this.form.progressUrl) {
3685             return;
3686         }
3687         
3688         if (!this.haveProgress) {
3689             Roo.MessageBox.progress("Uploading", "Uploading");
3690         }
3691         if (this.uploadComplete) {
3692            Roo.MessageBox.hide();
3693            return;
3694         }
3695         
3696         this.haveProgress = true;
3697    
3698         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3699         
3700         var c = new Roo.data.Connection();
3701         c.request({
3702             url : this.form.progressUrl,
3703             params: {
3704                 id : uid
3705             },
3706             method: 'GET',
3707             success : function(req){
3708                //console.log(data);
3709                 var rdata = false;
3710                 var edata;
3711                 try  {
3712                    rdata = Roo.decode(req.responseText)
3713                 } catch (e) {
3714                     Roo.log("Invalid data from server..");
3715                     Roo.log(edata);
3716                     return;
3717                 }
3718                 if (!rdata || !rdata.success) {
3719                     Roo.log(rdata);
3720                     Roo.MessageBox.alert(Roo.encode(rdata));
3721                     return;
3722                 }
3723                 var data = rdata.data;
3724                 
3725                 if (this.uploadComplete) {
3726                    Roo.MessageBox.hide();
3727                    return;
3728                 }
3729                    
3730                 if (data){
3731                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3732                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3733                     );
3734                 }
3735                 this.uploadProgress.defer(2000,this);
3736             },
3737        
3738             failure: function(data) {
3739                 Roo.log('progress url failed ');
3740                 Roo.log(data);
3741             },
3742             scope : this
3743         });
3744            
3745     },
3746     
3747     
3748     run : function()
3749     {
3750         // run get Values on the form, so it syncs any secondary forms.
3751         this.form.getValues();
3752         
3753         var o = this.options;
3754         var method = this.getMethod();
3755         var isPost = method == 'POST';
3756         if(o.clientValidation === false || this.form.isValid()){
3757             
3758             if (this.form.progressUrl) {
3759                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3760                     (new Date() * 1) + '' + Math.random());
3761                     
3762             } 
3763             
3764             
3765             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3766                 form:this.form.el.dom,
3767                 url:this.getUrl(!isPost),
3768                 method: method,
3769                 params:isPost ? this.getParams() : null,
3770                 isUpload: this.form.fileUpload
3771             }));
3772             
3773             this.uploadProgress();
3774
3775         }else if (o.clientValidation !== false){ // client validation failed
3776             this.failureType = Roo.form.Action.CLIENT_INVALID;
3777             this.form.afterAction(this, false);
3778         }
3779     },
3780
3781     success : function(response)
3782     {
3783         this.uploadComplete= true;
3784         if (this.haveProgress) {
3785             Roo.MessageBox.hide();
3786         }
3787         
3788         
3789         var result = this.processResponse(response);
3790         if(result === true || result.success){
3791             this.form.afterAction(this, true);
3792             return;
3793         }
3794         if(result.errors){
3795             this.form.markInvalid(result.errors);
3796             this.failureType = Roo.form.Action.SERVER_INVALID;
3797         }
3798         this.form.afterAction(this, false);
3799     },
3800     failure : function(response)
3801     {
3802         this.uploadComplete= true;
3803         if (this.haveProgress) {
3804             Roo.MessageBox.hide();
3805         }
3806         
3807         this.response = response;
3808         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3809         this.form.afterAction(this, false);
3810     },
3811     
3812     handleResponse : function(response){
3813         if(this.form.errorReader){
3814             var rs = this.form.errorReader.read(response);
3815             var errors = [];
3816             if(rs.records){
3817                 for(var i = 0, len = rs.records.length; i < len; i++) {
3818                     var r = rs.records[i];
3819                     errors[i] = r.data;
3820                 }
3821             }
3822             if(errors.length < 1){
3823                 errors = null;
3824             }
3825             return {
3826                 success : rs.success,
3827                 errors : errors
3828             };
3829         }
3830         var ret = false;
3831         try {
3832             ret = Roo.decode(response.responseText);
3833         } catch (e) {
3834             ret = {
3835                 success: false,
3836                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3837                 errors : []
3838             };
3839         }
3840         return ret;
3841         
3842     }
3843 });
3844
3845
3846 Roo.form.Action.Load = function(form, options){
3847     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3848     this.reader = this.form.reader;
3849 };
3850
3851 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3852     type : 'load',
3853
3854     run : function(){
3855         
3856         Roo.Ajax.request(Roo.apply(
3857                 this.createCallback(), {
3858                     method:this.getMethod(),
3859                     url:this.getUrl(false),
3860                     params:this.getParams()
3861         }));
3862     },
3863
3864     success : function(response){
3865         
3866         var result = this.processResponse(response);
3867         if(result === true || !result.success || !result.data){
3868             this.failureType = Roo.form.Action.LOAD_FAILURE;
3869             this.form.afterAction(this, false);
3870             return;
3871         }
3872         this.form.clearInvalid();
3873         this.form.setValues(result.data);
3874         this.form.afterAction(this, true);
3875     },
3876
3877     handleResponse : function(response){
3878         if(this.form.reader){
3879             var rs = this.form.reader.read(response);
3880             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3881             return {
3882                 success : rs.success,
3883                 data : data
3884             };
3885         }
3886         return Roo.decode(response.responseText);
3887     }
3888 });
3889
3890 Roo.form.Action.ACTION_TYPES = {
3891     'load' : Roo.form.Action.Load,
3892     'submit' : Roo.form.Action.Submit
3893 };/*
3894  * - LGPL
3895  *
3896  * form
3897  * 
3898  */
3899
3900 /**
3901  * @class Roo.bootstrap.Form
3902  * @extends Roo.bootstrap.Component
3903  * Bootstrap Form class
3904  * @cfg {String} method  GET | POST (default POST)
3905  * @cfg {String} labelAlign top | left (default top)
3906   * @cfg {String} align left  | right - for navbars
3907
3908  * 
3909  * @constructor
3910  * Create a new Form
3911  * @param {Object} config The config object
3912  */
3913
3914
3915 Roo.bootstrap.Form = function(config){
3916     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3917     this.addEvents({
3918         /**
3919          * @event clientvalidation
3920          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3921          * @param {Form} this
3922          * @param {Boolean} valid true if the form has passed client-side validation
3923          */
3924         clientvalidation: true,
3925         /**
3926          * @event beforeaction
3927          * Fires before any action is performed. Return false to cancel the action.
3928          * @param {Form} this
3929          * @param {Action} action The action to be performed
3930          */
3931         beforeaction: true,
3932         /**
3933          * @event actionfailed
3934          * Fires when an action fails.
3935          * @param {Form} this
3936          * @param {Action} action The action that failed
3937          */
3938         actionfailed : true,
3939         /**
3940          * @event actioncomplete
3941          * Fires when an action is completed.
3942          * @param {Form} this
3943          * @param {Action} action The action that completed
3944          */
3945         actioncomplete : true
3946     });
3947     
3948 };
3949
3950 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3951       
3952      /**
3953      * @cfg {String} method
3954      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3955      */
3956     method : 'POST',
3957     /**
3958      * @cfg {String} url
3959      * The URL to use for form actions if one isn't supplied in the action options.
3960      */
3961     /**
3962      * @cfg {Boolean} fileUpload
3963      * Set to true if this form is a file upload.
3964      */
3965      
3966     /**
3967      * @cfg {Object} baseParams
3968      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3969      */
3970       
3971     /**
3972      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3973      */
3974     timeout: 30,
3975     /**
3976      * @cfg {Sting} align (left|right) for navbar forms
3977      */
3978     align : 'left',
3979
3980     // private
3981     activeAction : null,
3982  
3983     /**
3984      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3985      * element by passing it or its id or mask the form itself by passing in true.
3986      * @type Mixed
3987      */
3988     waitMsgTarget : false,
3989     
3990      
3991     
3992     /**
3993      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3994      * element by passing it or its id or mask the form itself by passing in true.
3995      * @type Mixed
3996      */
3997     
3998     getAutoCreate : function(){
3999         
4000         var cfg = {
4001             tag: 'form',
4002             method : this.method || 'POST',
4003             id : this.id || Roo.id(),
4004             cls : ''
4005         }
4006         if (this.parent().xtype.match(/^Nav/)) {
4007             cfg.cls = 'navbar-form navbar-' + this.align;
4008             
4009         }
4010         
4011         if (this.labelAlign == 'left' ) {
4012             cfg.cls += ' form-horizontal';
4013         }
4014         
4015         
4016         return cfg;
4017     },
4018     initEvents : function()
4019     {
4020         this.el.on('submit', this.onSubmit, this);
4021         
4022         
4023     },
4024     // private
4025     onSubmit : function(e){
4026         e.stopEvent();
4027     },
4028     
4029      /**
4030      * Returns true if client-side validation on the form is successful.
4031      * @return Boolean
4032      */
4033     isValid : function(){
4034         var items = this.getItems();
4035         var valid = true;
4036         items.each(function(f){
4037            if(!f.validate()){
4038                valid = false;
4039                
4040            }
4041         });
4042         return valid;
4043     },
4044     /**
4045      * Returns true if any fields in this form have changed since their original load.
4046      * @return Boolean
4047      */
4048     isDirty : function(){
4049         var dirty = false;
4050         var items = this.getItems();
4051         items.each(function(f){
4052            if(f.isDirty()){
4053                dirty = true;
4054                return false;
4055            }
4056            return true;
4057         });
4058         return dirty;
4059     },
4060      /**
4061      * Performs a predefined action (submit or load) or custom actions you define on this form.
4062      * @param {String} actionName The name of the action type
4063      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
4064      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4065      * accept other config options):
4066      * <pre>
4067 Property          Type             Description
4068 ----------------  ---------------  ----------------------------------------------------------------------------------
4069 url               String           The url for the action (defaults to the form's url)
4070 method            String           The form method to use (defaults to the form's method, or POST if not defined)
4071 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
4072 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
4073                                    validate the form on the client (defaults to false)
4074      * </pre>
4075      * @return {BasicForm} this
4076      */
4077     doAction : function(action, options){
4078         if(typeof action == 'string'){
4079             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4080         }
4081         if(this.fireEvent('beforeaction', this, action) !== false){
4082             this.beforeAction(action);
4083             action.run.defer(100, action);
4084         }
4085         return this;
4086     },
4087     
4088     // private
4089     beforeAction : function(action){
4090         var o = action.options;
4091         
4092         // not really supported yet.. ??
4093         
4094         //if(this.waitMsgTarget === true){
4095             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4096         //}else if(this.waitMsgTarget){
4097         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4098         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4099         //}else {
4100         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4101        // }
4102          
4103     },
4104
4105     // private
4106     afterAction : function(action, success){
4107         this.activeAction = null;
4108         var o = action.options;
4109         
4110         //if(this.waitMsgTarget === true){
4111             this.el.unmask();
4112         //}else if(this.waitMsgTarget){
4113         //    this.waitMsgTarget.unmask();
4114         //}else{
4115         //    Roo.MessageBox.updateProgress(1);
4116         //    Roo.MessageBox.hide();
4117        // }
4118         // 
4119         if(success){
4120             if(o.reset){
4121                 this.reset();
4122             }
4123             Roo.callback(o.success, o.scope, [this, action]);
4124             this.fireEvent('actioncomplete', this, action);
4125             
4126         }else{
4127             
4128             // failure condition..
4129             // we have a scenario where updates need confirming.
4130             // eg. if a locking scenario exists..
4131             // we look for { errors : { needs_confirm : true }} in the response.
4132             if (
4133                 (typeof(action.result) != 'undefined')  &&
4134                 (typeof(action.result.errors) != 'undefined')  &&
4135                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4136            ){
4137                 var _t = this;
4138                 Roo.log("not supported yet");
4139                  /*
4140                 
4141                 Roo.MessageBox.confirm(
4142                     "Change requires confirmation",
4143                     action.result.errorMsg,
4144                     function(r) {
4145                         if (r != 'yes') {
4146                             return;
4147                         }
4148                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4149                     }
4150                     
4151                 );
4152                 */
4153                 
4154                 
4155                 return;
4156             }
4157             
4158             Roo.callback(o.failure, o.scope, [this, action]);
4159             // show an error message if no failed handler is set..
4160             if (!this.hasListener('actionfailed')) {
4161                 Roo.log("need to add dialog support");
4162                 /*
4163                 Roo.MessageBox.alert("Error",
4164                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4165                         action.result.errorMsg :
4166                         "Saving Failed, please check your entries or try again"
4167                 );
4168                 */
4169             }
4170             
4171             this.fireEvent('actionfailed', this, action);
4172         }
4173         
4174     },
4175     /**
4176      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4177      * @param {String} id The value to search for
4178      * @return Field
4179      */
4180     findField : function(id){
4181         var items = this.getItems();
4182         var field = items.get(id);
4183         if(!field){
4184              items.each(function(f){
4185                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4186                     field = f;
4187                     return false;
4188                 }
4189                 return true;
4190             });
4191         }
4192         return field || null;
4193     },
4194      /**
4195      * Mark fields in this form invalid in bulk.
4196      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4197      * @return {BasicForm} this
4198      */
4199     markInvalid : function(errors){
4200         if(errors instanceof Array){
4201             for(var i = 0, len = errors.length; i < len; i++){
4202                 var fieldError = errors[i];
4203                 var f = this.findField(fieldError.id);
4204                 if(f){
4205                     f.markInvalid(fieldError.msg);
4206                 }
4207             }
4208         }else{
4209             var field, id;
4210             for(id in errors){
4211                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4212                     field.markInvalid(errors[id]);
4213                 }
4214             }
4215         }
4216         //Roo.each(this.childForms || [], function (f) {
4217         //    f.markInvalid(errors);
4218         //});
4219         
4220         return this;
4221     },
4222
4223     /**
4224      * Set values for fields in this form in bulk.
4225      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4226      * @return {BasicForm} this
4227      */
4228     setValues : function(values){
4229         if(values instanceof Array){ // array of objects
4230             for(var i = 0, len = values.length; i < len; i++){
4231                 var v = values[i];
4232                 var f = this.findField(v.id);
4233                 if(f){
4234                     f.setValue(v.value);
4235                     if(this.trackResetOnLoad){
4236                         f.originalValue = f.getValue();
4237                     }
4238                 }
4239             }
4240         }else{ // object hash
4241             var field, id;
4242             for(id in values){
4243                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4244                     
4245                     if (field.setFromData && 
4246                         field.valueField && 
4247                         field.displayField &&
4248                         // combos' with local stores can 
4249                         // be queried via setValue()
4250                         // to set their value..
4251                         (field.store && !field.store.isLocal)
4252                         ) {
4253                         // it's a combo
4254                         var sd = { };
4255                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4256                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4257                         field.setFromData(sd);
4258                         
4259                     } else {
4260                         field.setValue(values[id]);
4261                     }
4262                     
4263                     
4264                     if(this.trackResetOnLoad){
4265                         field.originalValue = field.getValue();
4266                     }
4267                 }
4268             }
4269         }
4270          
4271         //Roo.each(this.childForms || [], function (f) {
4272         //    f.setValues(values);
4273         //});
4274                 
4275         return this;
4276     },
4277
4278     /**
4279      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4280      * they are returned as an array.
4281      * @param {Boolean} asString
4282      * @return {Object}
4283      */
4284     getValues : function(asString){
4285         //if (this.childForms) {
4286             // copy values from the child forms
4287         //    Roo.each(this.childForms, function (f) {
4288         //        this.setValues(f.getValues());
4289         //    }, this);
4290         //}
4291         
4292         
4293         
4294         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4295         if(asString === true){
4296             return fs;
4297         }
4298         return Roo.urlDecode(fs);
4299     },
4300     
4301     /**
4302      * Returns the fields in this form as an object with key/value pairs. 
4303      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4304      * @return {Object}
4305      */
4306     getFieldValues : function(with_hidden)
4307     {
4308         var items = this.getItems();
4309         var ret = {};
4310         items.each(function(f){
4311             if (!f.getName()) {
4312                 return;
4313             }
4314             var v = f.getValue();
4315             if (f.inputType =='radio') {
4316                 if (typeof(ret[f.getName()]) == 'undefined') {
4317                     ret[f.getName()] = ''; // empty..
4318                 }
4319                 
4320                 if (!f.el.dom.checked) {
4321                     return;
4322                     
4323                 }
4324                 v = f.el.dom.value;
4325                 
4326             }
4327             
4328             // not sure if this supported any more..
4329             if ((typeof(v) == 'object') && f.getRawValue) {
4330                 v = f.getRawValue() ; // dates..
4331             }
4332             // combo boxes where name != hiddenName...
4333             if (f.name != f.getName()) {
4334                 ret[f.name] = f.getRawValue();
4335             }
4336             ret[f.getName()] = v;
4337         });
4338         
4339         return ret;
4340     },
4341
4342     /**
4343      * Clears all invalid messages in this form.
4344      * @return {BasicForm} this
4345      */
4346     clearInvalid : function(){
4347         var items = this.getItems();
4348         
4349         items.each(function(f){
4350            f.clearInvalid();
4351         });
4352         
4353         
4354         
4355         return this;
4356     },
4357
4358     /**
4359      * Resets this form.
4360      * @return {BasicForm} this
4361      */
4362     reset : function(){
4363         var items = this.getItems();
4364         items.each(function(f){
4365             f.reset();
4366         });
4367         
4368         Roo.each(this.childForms || [], function (f) {
4369             f.reset();
4370         });
4371        
4372         
4373         return this;
4374     },
4375     getItems : function()
4376     {
4377         var r=new Roo.util.MixedCollection(false, function(o){
4378             return o.id || (o.id = Roo.id());
4379         });
4380         var iter = function(el) {
4381             if (el.inputEl) {
4382                 r.add(el);
4383             }
4384             if (!el.items) {
4385                 return;
4386             }
4387             Roo.each(el.items,function(e) {
4388                 iter(e);
4389             });
4390             
4391             
4392         };
4393         iter(this);
4394         return r;
4395         
4396         
4397         
4398         
4399     }
4400     
4401 });
4402
4403  
4404 /*
4405  * Based on:
4406  * Ext JS Library 1.1.1
4407  * Copyright(c) 2006-2007, Ext JS, LLC.
4408  *
4409  * Originally Released Under LGPL - original licence link has changed is not relivant.
4410  *
4411  * Fork - LGPL
4412  * <script type="text/javascript">
4413  */
4414 /**
4415  * @class Roo.form.VTypes
4416  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4417  * @singleton
4418  */
4419 Roo.form.VTypes = function(){
4420     // closure these in so they are only created once.
4421     var alpha = /^[a-zA-Z_]+$/;
4422     var alphanum = /^[a-zA-Z0-9_]+$/;
4423     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4424     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4425
4426     // All these messages and functions are configurable
4427     return {
4428         /**
4429          * The function used to validate email addresses
4430          * @param {String} value The email address
4431          */
4432         'email' : function(v){
4433             return email.test(v);
4434         },
4435         /**
4436          * The error text to display when the email validation function returns false
4437          * @type String
4438          */
4439         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4440         /**
4441          * The keystroke filter mask to be applied on email input
4442          * @type RegExp
4443          */
4444         'emailMask' : /[a-z0-9_\.\-@]/i,
4445
4446         /**
4447          * The function used to validate URLs
4448          * @param {String} value The URL
4449          */
4450         'url' : function(v){
4451             return url.test(v);
4452         },
4453         /**
4454          * The error text to display when the url validation function returns false
4455          * @type String
4456          */
4457         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4458         
4459         /**
4460          * The function used to validate alpha values
4461          * @param {String} value The value
4462          */
4463         'alpha' : function(v){
4464             return alpha.test(v);
4465         },
4466         /**
4467          * The error text to display when the alpha validation function returns false
4468          * @type String
4469          */
4470         'alphaText' : 'This field should only contain letters and _',
4471         /**
4472          * The keystroke filter mask to be applied on alpha input
4473          * @type RegExp
4474          */
4475         'alphaMask' : /[a-z_]/i,
4476
4477         /**
4478          * The function used to validate alphanumeric values
4479          * @param {String} value The value
4480          */
4481         'alphanum' : function(v){
4482             return alphanum.test(v);
4483         },
4484         /**
4485          * The error text to display when the alphanumeric validation function returns false
4486          * @type String
4487          */
4488         'alphanumText' : 'This field should only contain letters, numbers and _',
4489         /**
4490          * The keystroke filter mask to be applied on alphanumeric input
4491          * @type RegExp
4492          */
4493         'alphanumMask' : /[a-z0-9_]/i
4494     };
4495 }();/*
4496  * - LGPL
4497  *
4498  * Input
4499  * 
4500  */
4501
4502 /**
4503  * @class Roo.bootstrap.Input
4504  * @extends Roo.bootstrap.Component
4505  * Bootstrap Input class
4506  * @cfg {Boolean} disabled is it disabled
4507  * @cfg {String} fieldLabel - the label associated
4508  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4509  * @cfg {String} name name of the input
4510  * @cfg {string} fieldLabel - the label associated
4511  * @cfg {string}  inputType - input / file submit ...
4512  * @cfg {string} placeholder - placeholder to put in text.
4513  * @cfg {string}  before - input group add on before
4514  * @cfg {string} after - input group add on after
4515  * @cfg {string} size - (lg|sm) or leave empty..
4516  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4517  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4518  * @cfg {Number} md colspan out of 12 for computer-sized screens
4519  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4520  * @cfg {string} value default value of the input
4521  * @cfg {Number} labelWidth set the width of label (0-12)
4522  * @cfg {String} labelAlign (top|left)
4523  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4524  * 
4525  * 
4526  * @constructor
4527  * Create a new Input
4528  * @param {Object} config The config object
4529  */
4530
4531 Roo.bootstrap.Input = function(config){
4532     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4533    
4534         this.addEvents({
4535             /**
4536              * @event focus
4537              * Fires when this field receives input focus.
4538              * @param {Roo.form.Field} this
4539              */
4540             focus : true,
4541             /**
4542              * @event blur
4543              * Fires when this field loses input focus.
4544              * @param {Roo.form.Field} this
4545              */
4546             blur : true,
4547             /**
4548              * @event specialkey
4549              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4550              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4551              * @param {Roo.form.Field} this
4552              * @param {Roo.EventObject} e The event object
4553              */
4554             specialkey : true,
4555             /**
4556              * @event change
4557              * Fires just before the field blurs if the field value has changed.
4558              * @param {Roo.form.Field} this
4559              * @param {Mixed} newValue The new value
4560              * @param {Mixed} oldValue The original value
4561              */
4562             change : true,
4563             /**
4564              * @event invalid
4565              * Fires after the field has been marked as invalid.
4566              * @param {Roo.form.Field} this
4567              * @param {String} msg The validation message
4568              */
4569             invalid : true,
4570             /**
4571              * @event valid
4572              * Fires after the field has been validated with no errors.
4573              * @param {Roo.form.Field} this
4574              */
4575             valid : true,
4576              /**
4577              * @event keyup
4578              * Fires after the key up
4579              * @param {Roo.form.Field} this
4580              * @param {Roo.EventObject}  e The event Object
4581              */
4582             keyup : true
4583         });
4584 };
4585
4586 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4587      /**
4588      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4589       automatic validation (defaults to "keyup").
4590      */
4591     validationEvent : "keyup",
4592      /**
4593      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4594      */
4595     validateOnBlur : true,
4596     /**
4597      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4598      */
4599     validationDelay : 250,
4600      /**
4601      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4602      */
4603     focusClass : "x-form-focus",  // not needed???
4604     
4605        
4606     /**
4607      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4608      */
4609     invalidClass : "has-error",
4610     
4611     /**
4612      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4613      */
4614     selectOnFocus : false,
4615     
4616      /**
4617      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4618      */
4619     maskRe : null,
4620        /**
4621      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4622      */
4623     vtype : null,
4624     
4625       /**
4626      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4627      */
4628     disableKeyFilter : false,
4629     
4630        /**
4631      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4632      */
4633     disabled : false,
4634      /**
4635      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4636      */
4637     allowBlank : true,
4638     /**
4639      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4640      */
4641     blankText : "This field is required",
4642     
4643      /**
4644      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4645      */
4646     minLength : 0,
4647     /**
4648      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4649      */
4650     maxLength : Number.MAX_VALUE,
4651     /**
4652      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4653      */
4654     minLengthText : "The minimum length for this field is {0}",
4655     /**
4656      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4657      */
4658     maxLengthText : "The maximum length for this field is {0}",
4659   
4660     
4661     /**
4662      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4663      * If available, this function will be called only after the basic validators all return true, and will be passed the
4664      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4665      */
4666     validator : null,
4667     /**
4668      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4669      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4670      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4671      */
4672     regex : null,
4673     /**
4674      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4675      */
4676     regexText : "",
4677     
4678     
4679     
4680     fieldLabel : '',
4681     inputType : 'text',
4682     
4683     name : false,
4684     placeholder: false,
4685     before : false,
4686     after : false,
4687     size : false,
4688     // private
4689     hasFocus : false,
4690     preventMark: false,
4691     isFormField : true,
4692     value : '',
4693     labelWidth : 2,
4694     labelAlign : false,
4695     readOnly : false,
4696     
4697     parentLabelAlign : function()
4698     {
4699         var parent = this;
4700         while (parent.parent()) {
4701             parent = parent.parent();
4702             if (typeof(parent.labelAlign) !='undefined') {
4703                 return parent.labelAlign;
4704             }
4705         }
4706         return 'left';
4707         
4708     },
4709     
4710     getAutoCreate : function(){
4711         
4712         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4713         
4714         var id = Roo.id();
4715         
4716         var cfg = {};
4717         
4718         if(this.inputType != 'hidden'){
4719             cfg.cls = 'form-group' //input-group
4720         }
4721         
4722         var input =  {
4723             tag: 'input',
4724             id : id,
4725             type : this.inputType,
4726             value : this.value,
4727             cls : 'form-control',
4728             placeholder : this.placeholder || ''
4729             
4730         };
4731         
4732         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4733             input.maxLength = this.maxLength;
4734         }
4735         
4736         if (this.disabled) {
4737             input.disabled=true;
4738         }
4739         
4740         if (this.readOnly) {
4741             input.readonly=true;
4742         }
4743         
4744         if (this.name) {
4745             input.name = this.name;
4746         }
4747         if (this.size) {
4748             input.cls += ' input-' + this.size;
4749         }
4750         var settings=this;
4751         ['xs','sm','md','lg'].map(function(size){
4752             if (settings[size]) {
4753                 cfg.cls += ' col-' + size + '-' + settings[size];
4754             }
4755         });
4756         
4757         var inputblock = input;
4758         
4759         if (this.before || this.after) {
4760             
4761             inputblock = {
4762                 cls : 'input-group',
4763                 cn :  [] 
4764             };
4765             if (this.before) {
4766                 inputblock.cn.push({
4767                     tag :'span',
4768                     cls : 'input-group-addon',
4769                     html : this.before
4770                 });
4771             }
4772             inputblock.cn.push(input);
4773             if (this.after) {
4774                 inputblock.cn.push({
4775                     tag :'span',
4776                     cls : 'input-group-addon',
4777                     html : this.after
4778                 });
4779             }
4780             
4781         };
4782         
4783         if (align ==='left' && this.fieldLabel.length) {
4784                 Roo.log("left and has label");
4785                 cfg.cn = [
4786                     
4787                     {
4788                         tag: 'label',
4789                         'for' :  id,
4790                         cls : 'control-label col-sm-' + this.labelWidth,
4791                         html : this.fieldLabel
4792                         
4793                     },
4794                     {
4795                         cls : "col-sm-" + (12 - this.labelWidth), 
4796                         cn: [
4797                             inputblock
4798                         ]
4799                     }
4800                     
4801                 ];
4802         } else if ( this.fieldLabel.length) {
4803                 Roo.log(" label");
4804                  cfg.cn = [
4805                    
4806                     {
4807                         tag: 'label',
4808                         //cls : 'input-group-addon',
4809                         html : this.fieldLabel
4810                         
4811                     },
4812                     
4813                     inputblock
4814                     
4815                 ];
4816
4817         } else {
4818             
4819                 Roo.log(" no label && no align");
4820                 cfg.cn = [
4821                     
4822                         inputblock
4823                     
4824                 ];
4825                 
4826                 
4827         };
4828         Roo.log('input-parentType: ' + this.parentType);
4829         
4830         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4831            cfg.cls += ' navbar-form';
4832            Roo.log(cfg);
4833         }
4834         
4835         return cfg;
4836         
4837     },
4838     /**
4839      * return the real input element.
4840      */
4841     inputEl: function ()
4842     {
4843         return this.el.select('input.form-control',true).first();
4844     },
4845     setDisabled : function(v)
4846     {
4847         var i  = this.inputEl().dom;
4848         if (!v) {
4849             i.removeAttribute('disabled');
4850             return;
4851             
4852         }
4853         i.setAttribute('disabled','true');
4854     },
4855     initEvents : function()
4856     {
4857         
4858         this.inputEl().on("keydown" , this.fireKey,  this);
4859         this.inputEl().on("focus", this.onFocus,  this);
4860         this.inputEl().on("blur", this.onBlur,  this);
4861         
4862         this.inputEl().relayEvent('keyup', this);
4863
4864         // reference to original value for reset
4865         this.originalValue = this.getValue();
4866         //Roo.form.TextField.superclass.initEvents.call(this);
4867         if(this.validationEvent == 'keyup'){
4868             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4869             this.inputEl().on('keyup', this.filterValidation, this);
4870         }
4871         else if(this.validationEvent !== false){
4872             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4873         }
4874         
4875         if(this.selectOnFocus){
4876             this.on("focus", this.preFocus, this);
4877             
4878         }
4879         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4880             this.inputEl().on("keypress", this.filterKeys, this);
4881         }
4882        /* if(this.grow){
4883             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4884             this.el.on("click", this.autoSize,  this);
4885         }
4886         */
4887         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4888             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4889         }
4890         
4891     },
4892     filterValidation : function(e){
4893         if(!e.isNavKeyPress()){
4894             this.validationTask.delay(this.validationDelay);
4895         }
4896     },
4897      /**
4898      * Validates the field value
4899      * @return {Boolean} True if the value is valid, else false
4900      */
4901     validate : function(){
4902         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4903         if(this.disabled || this.validateValue(this.getRawValue())){
4904             this.clearInvalid();
4905             return true;
4906         }
4907         return false;
4908     },
4909     
4910     
4911     /**
4912      * Validates a value according to the field's validation rules and marks the field as invalid
4913      * if the validation fails
4914      * @param {Mixed} value The value to validate
4915      * @return {Boolean} True if the value is valid, else false
4916      */
4917     validateValue : function(value){
4918         if(value.length < 1)  { // if it's blank
4919              if(this.allowBlank){
4920                 this.clearInvalid();
4921                 return true;
4922              }else{
4923                 this.markInvalid(this.blankText);
4924                 return false;
4925              }
4926         }
4927         if(value.length < this.minLength){
4928             this.markInvalid(String.format(this.minLengthText, this.minLength));
4929             return false;
4930         }
4931         if(value.length > this.maxLength){
4932             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4933             return false;
4934         }
4935         if(this.vtype){
4936             var vt = Roo.form.VTypes;
4937             if(!vt[this.vtype](value, this)){
4938                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4939                 return false;
4940             }
4941         }
4942         if(typeof this.validator == "function"){
4943             var msg = this.validator(value);
4944             if(msg !== true){
4945                 this.markInvalid(msg);
4946                 return false;
4947             }
4948         }
4949         if(this.regex && !this.regex.test(value)){
4950             this.markInvalid(this.regexText);
4951             return false;
4952         }
4953         return true;
4954     },
4955
4956     
4957     
4958      // private
4959     fireKey : function(e){
4960         //Roo.log('field ' + e.getKey());
4961         if(e.isNavKeyPress()){
4962             this.fireEvent("specialkey", this, e);
4963         }
4964     },
4965     focus : function (selectText){
4966         if(this.rendered){
4967             this.inputEl().focus();
4968             if(selectText === true){
4969                 this.inputEl().dom.select();
4970             }
4971         }
4972         return this;
4973     } ,
4974     
4975     onFocus : function(){
4976         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4977            // this.el.addClass(this.focusClass);
4978         }
4979         if(!this.hasFocus){
4980             this.hasFocus = true;
4981             this.startValue = this.getValue();
4982             this.fireEvent("focus", this);
4983         }
4984     },
4985     
4986     beforeBlur : Roo.emptyFn,
4987
4988     
4989     // private
4990     onBlur : function(){
4991         this.beforeBlur();
4992         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4993             //this.el.removeClass(this.focusClass);
4994         }
4995         this.hasFocus = false;
4996         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4997             this.validate();
4998         }
4999         var v = this.getValue();
5000         if(String(v) !== String(this.startValue)){
5001             this.fireEvent('change', this, v, this.startValue);
5002         }
5003         this.fireEvent("blur", this);
5004     },
5005     
5006     /**
5007      * Resets the current field value to the originally loaded value and clears any validation messages
5008      */
5009     reset : function(){
5010         this.setValue(this.originalValue);
5011         this.clearInvalid();
5012     },
5013      /**
5014      * Returns the name of the field
5015      * @return {Mixed} name The name field
5016      */
5017     getName: function(){
5018         return this.name;
5019     },
5020      /**
5021      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
5022      * @return {Mixed} value The field value
5023      */
5024     getValue : function(){
5025         return this.inputEl().getValue();
5026     },
5027     /**
5028      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
5029      * @return {Mixed} value The field value
5030      */
5031     getRawValue : function(){
5032         var v = this.inputEl().getValue();
5033         
5034         return v;
5035     },
5036     
5037     /**
5038      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
5039      * @param {Mixed} value The value to set
5040      */
5041     setRawValue : function(v){
5042         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5043     },
5044     
5045     selectText : function(start, end){
5046         var v = this.getRawValue();
5047         if(v.length > 0){
5048             start = start === undefined ? 0 : start;
5049             end = end === undefined ? v.length : end;
5050             var d = this.inputEl().dom;
5051             if(d.setSelectionRange){
5052                 d.setSelectionRange(start, end);
5053             }else if(d.createTextRange){
5054                 var range = d.createTextRange();
5055                 range.moveStart("character", start);
5056                 range.moveEnd("character", v.length-end);
5057                 range.select();
5058             }
5059         }
5060     },
5061     
5062     /**
5063      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
5064      * @param {Mixed} value The value to set
5065      */
5066     setValue : function(v){
5067         this.value = v;
5068         if(this.rendered){
5069             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5070             this.validate();
5071         }
5072     },
5073     
5074     /*
5075     processValue : function(value){
5076         if(this.stripCharsRe){
5077             var newValue = value.replace(this.stripCharsRe, '');
5078             if(newValue !== value){
5079                 this.setRawValue(newValue);
5080                 return newValue;
5081             }
5082         }
5083         return value;
5084     },
5085   */
5086     preFocus : function(){
5087         
5088         if(this.selectOnFocus){
5089             this.inputEl().dom.select();
5090         }
5091     },
5092     filterKeys : function(e){
5093         var k = e.getKey();
5094         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5095             return;
5096         }
5097         var c = e.getCharCode(), cc = String.fromCharCode(c);
5098         if(Roo.isIE && (e.isSpecialKey() || !cc)){
5099             return;
5100         }
5101         if(!this.maskRe.test(cc)){
5102             e.stopEvent();
5103         }
5104     },
5105      /**
5106      * Clear any invalid styles/messages for this field
5107      */
5108     clearInvalid : function(){
5109         
5110         if(!this.el || this.preventMark){ // not rendered
5111             return;
5112         }
5113         this.el.removeClass(this.invalidClass);
5114         /*
5115         switch(this.msgTarget){
5116             case 'qtip':
5117                 this.el.dom.qtip = '';
5118                 break;
5119             case 'title':
5120                 this.el.dom.title = '';
5121                 break;
5122             case 'under':
5123                 if(this.errorEl){
5124                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5125                 }
5126                 break;
5127             case 'side':
5128                 if(this.errorIcon){
5129                     this.errorIcon.dom.qtip = '';
5130                     this.errorIcon.hide();
5131                     this.un('resize', this.alignErrorIcon, this);
5132                 }
5133                 break;
5134             default:
5135                 var t = Roo.getDom(this.msgTarget);
5136                 t.innerHTML = '';
5137                 t.style.display = 'none';
5138                 break;
5139         }
5140         */
5141         this.fireEvent('valid', this);
5142     },
5143      /**
5144      * Mark this field as invalid
5145      * @param {String} msg The validation message
5146      */
5147     markInvalid : function(msg){
5148         if(!this.el  || this.preventMark){ // not rendered
5149             return;
5150         }
5151         this.el.addClass(this.invalidClass);
5152         /*
5153         msg = msg || this.invalidText;
5154         switch(this.msgTarget){
5155             case 'qtip':
5156                 this.el.dom.qtip = msg;
5157                 this.el.dom.qclass = 'x-form-invalid-tip';
5158                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5159                     Roo.QuickTips.enable();
5160                 }
5161                 break;
5162             case 'title':
5163                 this.el.dom.title = msg;
5164                 break;
5165             case 'under':
5166                 if(!this.errorEl){
5167                     var elp = this.el.findParent('.x-form-element', 5, true);
5168                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5169                     this.errorEl.setWidth(elp.getWidth(true)-20);
5170                 }
5171                 this.errorEl.update(msg);
5172                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5173                 break;
5174             case 'side':
5175                 if(!this.errorIcon){
5176                     var elp = this.el.findParent('.x-form-element', 5, true);
5177                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5178                 }
5179                 this.alignErrorIcon();
5180                 this.errorIcon.dom.qtip = msg;
5181                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5182                 this.errorIcon.show();
5183                 this.on('resize', this.alignErrorIcon, this);
5184                 break;
5185             default:
5186                 var t = Roo.getDom(this.msgTarget);
5187                 t.innerHTML = msg;
5188                 t.style.display = this.msgDisplay;
5189                 break;
5190         }
5191         */
5192         this.fireEvent('invalid', this, msg);
5193     },
5194     // private
5195     SafariOnKeyDown : function(event)
5196     {
5197         // this is a workaround for a password hang bug on chrome/ webkit.
5198         
5199         var isSelectAll = false;
5200         
5201         if(this.inputEl().dom.selectionEnd > 0){
5202             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5203         }
5204         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5205             event.preventDefault();
5206             this.setValue('');
5207             return;
5208         }
5209         
5210         if(isSelectAll){ // backspace and delete key
5211             
5212             event.preventDefault();
5213             // this is very hacky as keydown always get's upper case.
5214             //
5215             var cc = String.fromCharCode(event.getCharCode());
5216             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5217             
5218         }
5219     },
5220     adjustWidth : function(tag, w){
5221         tag = tag.toLowerCase();
5222         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5223             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5224                 if(tag == 'input'){
5225                     return w + 2;
5226                 }
5227                 if(tag == 'textarea'){
5228                     return w-2;
5229                 }
5230             }else if(Roo.isOpera){
5231                 if(tag == 'input'){
5232                     return w + 2;
5233                 }
5234                 if(tag == 'textarea'){
5235                     return w-2;
5236                 }
5237             }
5238         }
5239         return w;
5240     }
5241     
5242 });
5243
5244  
5245 /*
5246  * - LGPL
5247  *
5248  * Input
5249  * 
5250  */
5251
5252 /**
5253  * @class Roo.bootstrap.TextArea
5254  * @extends Roo.bootstrap.Input
5255  * Bootstrap TextArea class
5256  * @cfg {Number} cols Specifies the visible width of a text area
5257  * @cfg {Number} rows Specifies the visible number of lines in a text area
5258  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5259  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5260  * @cfg {string} html text
5261  * 
5262  * @constructor
5263  * Create a new TextArea
5264  * @param {Object} config The config object
5265  */
5266
5267 Roo.bootstrap.TextArea = function(config){
5268     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5269    
5270 };
5271
5272 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5273      
5274     cols : false,
5275     rows : 5,
5276     readOnly : false,
5277     warp : 'soft',
5278     resize : false,
5279     value: false,
5280     html: false,
5281     
5282     getAutoCreate : function(){
5283         
5284         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5285         
5286         var id = Roo.id();
5287         
5288         var cfg = {};
5289         
5290         var input =  {
5291             tag: 'textarea',
5292             id : id,
5293             warp : this.warp,
5294             rows : this.rows,
5295             value : this.value || '',
5296             html: this.html || '',
5297             cls : 'form-control',
5298             placeholder : this.placeholder || '' 
5299             
5300         };
5301         
5302         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5303             input.maxLength = this.maxLength;
5304         }
5305         
5306         if(this.resize){
5307             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5308         }
5309         
5310         if(this.cols){
5311             input.cols = this.cols;
5312         }
5313         
5314         if (this.readOnly) {
5315             input.readonly = true;
5316         }
5317         
5318         if (this.name) {
5319             input.name = this.name;
5320         }
5321         
5322         if (this.size) {
5323             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5324         }
5325         
5326         var settings=this;
5327         ['xs','sm','md','lg'].map(function(size){
5328             if (settings[size]) {
5329                 cfg.cls += ' col-' + size + '-' + settings[size];
5330             }
5331         });
5332         
5333         var inputblock = input;
5334         
5335         if (this.before || this.after) {
5336             
5337             inputblock = {
5338                 cls : 'input-group',
5339                 cn :  [] 
5340             };
5341             if (this.before) {
5342                 inputblock.cn.push({
5343                     tag :'span',
5344                     cls : 'input-group-addon',
5345                     html : this.before
5346                 });
5347             }
5348             inputblock.cn.push(input);
5349             if (this.after) {
5350                 inputblock.cn.push({
5351                     tag :'span',
5352                     cls : 'input-group-addon',
5353                     html : this.after
5354                 });
5355             }
5356             
5357         }
5358         
5359         if (align ==='left' && this.fieldLabel.length) {
5360                 Roo.log("left and has label");
5361                 cfg.cn = [
5362                     
5363                     {
5364                         tag: 'label',
5365                         'for' :  id,
5366                         cls : 'control-label col-sm-' + this.labelWidth,
5367                         html : this.fieldLabel
5368                         
5369                     },
5370                     {
5371                         cls : "col-sm-" + (12 - this.labelWidth), 
5372                         cn: [
5373                             inputblock
5374                         ]
5375                     }
5376                     
5377                 ];
5378         } else if ( this.fieldLabel.length) {
5379                 Roo.log(" label");
5380                  cfg.cn = [
5381                    
5382                     {
5383                         tag: 'label',
5384                         //cls : 'input-group-addon',
5385                         html : this.fieldLabel
5386                         
5387                     },
5388                     
5389                     inputblock
5390                     
5391                 ];
5392
5393         } else {
5394             
5395                    Roo.log(" no label && no align");
5396                 cfg.cn = [
5397                     
5398                         inputblock
5399                     
5400                 ];
5401                 
5402                 
5403         }
5404         
5405         if (this.disabled) {
5406             input.disabled=true;
5407         }
5408         
5409         return cfg;
5410         
5411     },
5412     /**
5413      * return the real textarea element.
5414      */
5415     inputEl: function ()
5416     {
5417         return this.el.select('textarea.form-control',true).first();
5418     }
5419 });
5420
5421  
5422 /*
5423  * - LGPL
5424  *
5425  * trigger field - base class for combo..
5426  * 
5427  */
5428  
5429 /**
5430  * @class Roo.bootstrap.TriggerField
5431  * @extends Roo.bootstrap.Input
5432  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5433  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5434  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5435  * for which you can provide a custom implementation.  For example:
5436  * <pre><code>
5437 var trigger = new Roo.bootstrap.TriggerField();
5438 trigger.onTriggerClick = myTriggerFn;
5439 trigger.applyTo('my-field');
5440 </code></pre>
5441  *
5442  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5443  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5444  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5445  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5446  * @constructor
5447  * Create a new TriggerField.
5448  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5449  * to the base TextField)
5450  */
5451 Roo.bootstrap.TriggerField = function(config){
5452     this.mimicing = false;
5453     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5454 };
5455
5456 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5457     /**
5458      * @cfg {String} triggerClass A CSS class to apply to the trigger
5459      */
5460      /**
5461      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5462      */
5463     hideTrigger:false,
5464
5465     /** @cfg {Boolean} grow @hide */
5466     /** @cfg {Number} growMin @hide */
5467     /** @cfg {Number} growMax @hide */
5468
5469     /**
5470      * @hide 
5471      * @method
5472      */
5473     autoSize: Roo.emptyFn,
5474     // private
5475     monitorTab : true,
5476     // private
5477     deferHeight : true,
5478
5479     
5480     actionMode : 'wrap',
5481     
5482     
5483     
5484     getAutoCreate : function(){
5485        
5486         var parent = this.parent();
5487         
5488         var align = this.parentLabelAlign();
5489         
5490         var id = Roo.id();
5491         
5492         var cfg = {
5493             cls: 'form-group' //input-group
5494         };
5495         
5496         
5497         var input =  {
5498             tag: 'input',
5499             id : id,
5500             type : this.inputType,
5501             cls : 'form-control',
5502             autocomplete: 'off',
5503             placeholder : this.placeholder || '' 
5504             
5505         };
5506         if (this.name) {
5507             input.name = this.name;
5508         }
5509         if (this.size) {
5510             input.cls += ' input-' + this.size;
5511         }
5512         
5513         if (this.disabled) {
5514             input.disabled=true;
5515         }
5516         
5517         var inputblock = input;
5518         
5519         if (this.before || this.after) {
5520             
5521             inputblock = {
5522                 cls : 'input-group',
5523                 cn :  [] 
5524             };
5525             if (this.before) {
5526                 inputblock.cn.push({
5527                     tag :'span',
5528                     cls : 'input-group-addon',
5529                     html : this.before
5530                 });
5531             }
5532             inputblock.cn.push(input);
5533             if (this.after) {
5534                 inputblock.cn.push({
5535                     tag :'span',
5536                     cls : 'input-group-addon',
5537                     html : this.after
5538                 });
5539             }
5540             
5541         };
5542         
5543         var box = {
5544             tag: 'div',
5545             cn: [
5546                 {
5547                     tag: 'input',
5548                     type : 'hidden',
5549                     cls: 'form-hidden-field'
5550                 },
5551                 inputblock
5552             ]
5553             
5554         };
5555         
5556         if(this.multiple){
5557             Roo.log('multiple');
5558             
5559             box = {
5560                 tag: 'div',
5561                 cn: [
5562                     {
5563                         tag: 'input',
5564                         type : 'hidden',
5565                         cls: 'form-hidden-field'
5566                     },
5567                     {
5568                         tag: 'ul',
5569                         cls: 'select2-choices',
5570                         cn:[
5571                             {
5572                                 tag: 'li',
5573                                 cls: 'select2-search-field',
5574                                 cn: [
5575
5576                                     inputblock
5577                                 ]
5578                             }
5579                         ]
5580                     }
5581                 ]
5582             }
5583         };
5584         
5585         var combobox = {
5586             cls: 'select2-container input-group',
5587             cn: [
5588                 box,
5589                 {
5590                     tag: 'ul',
5591                     cls: 'typeahead typeahead-long dropdown-menu',
5592                     style: 'display:none'
5593                 }
5594             ]
5595         };
5596         
5597         if(!this.multiple){
5598             combobox.cn.push({
5599                 tag :'span',
5600                 cls : 'input-group-addon btn dropdown-toggle',
5601                 cn : [
5602                     {
5603                         tag: 'span',
5604                         cls: 'caret'
5605                     },
5606                     {
5607                         tag: 'span',
5608                         cls: 'combobox-clear',
5609                         cn  : [
5610                             {
5611                                 tag : 'i',
5612                                 cls: 'icon-remove'
5613                             }
5614                         ]
5615                     }
5616                 ]
5617
5618             })
5619         }
5620         
5621         if(this.multiple){
5622             combobox.cls += ' select2-container-multi';
5623         }
5624         
5625         if (align ==='left' && this.fieldLabel.length) {
5626             
5627                 Roo.log("left and has label");
5628                 cfg.cn = [
5629                     
5630                     {
5631                         tag: 'label',
5632                         'for' :  id,
5633                         cls : 'control-label col-sm-' + this.labelWidth,
5634                         html : this.fieldLabel
5635                         
5636                     },
5637                     {
5638                         cls : "col-sm-" + (12 - this.labelWidth), 
5639                         cn: [
5640                             combobox
5641                         ]
5642                     }
5643                     
5644                 ];
5645         } else if ( this.fieldLabel.length) {
5646                 Roo.log(" label");
5647                  cfg.cn = [
5648                    
5649                     {
5650                         tag: 'label',
5651                         //cls : 'input-group-addon',
5652                         html : this.fieldLabel
5653                         
5654                     },
5655                     
5656                     combobox
5657                     
5658                 ];
5659
5660         } else {
5661             
5662                 Roo.log(" no label && no align");
5663                 cfg = combobox
5664                      
5665                 
5666         }
5667          
5668         var settings=this;
5669         ['xs','sm','md','lg'].map(function(size){
5670             if (settings[size]) {
5671                 cfg.cls += ' col-' + size + '-' + settings[size];
5672             }
5673         });
5674         
5675         return cfg;
5676         
5677     },
5678     
5679     
5680     
5681     // private
5682     onResize : function(w, h){
5683 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5684 //        if(typeof w == 'number'){
5685 //            var x = w - this.trigger.getWidth();
5686 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5687 //            this.trigger.setStyle('left', x+'px');
5688 //        }
5689     },
5690
5691     // private
5692     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5693
5694     // private
5695     getResizeEl : function(){
5696         return this.inputEl();
5697     },
5698
5699     // private
5700     getPositionEl : function(){
5701         return this.inputEl();
5702     },
5703
5704     // private
5705     alignErrorIcon : function(){
5706         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5707     },
5708
5709     // private
5710     initEvents : function(){
5711         
5712         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5713         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5714         if(!this.multiple){
5715             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5716             if(this.hideTrigger){
5717                 this.trigger.setDisplayed(false);
5718             }
5719             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5720         }
5721         
5722         if(this.multiple){
5723             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5724         }
5725         
5726         //this.trigger.addClassOnOver('x-form-trigger-over');
5727         //this.trigger.addClassOnClick('x-form-trigger-click');
5728         
5729         //if(!this.width){
5730         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5731         //}
5732     },
5733
5734     // private
5735     initTrigger : function(){
5736        
5737     },
5738
5739     // private
5740     onDestroy : function(){
5741         if(this.trigger){
5742             this.trigger.removeAllListeners();
5743           //  this.trigger.remove();
5744         }
5745         //if(this.wrap){
5746         //    this.wrap.remove();
5747         //}
5748         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5749     },
5750
5751     // private
5752     onFocus : function(){
5753         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5754         /*
5755         if(!this.mimicing){
5756             this.wrap.addClass('x-trigger-wrap-focus');
5757             this.mimicing = true;
5758             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5759             if(this.monitorTab){
5760                 this.el.on("keydown", this.checkTab, this);
5761             }
5762         }
5763         */
5764     },
5765
5766     // private
5767     checkTab : function(e){
5768         if(e.getKey() == e.TAB){
5769             this.triggerBlur();
5770         }
5771     },
5772
5773     // private
5774     onBlur : function(){
5775         // do nothing
5776     },
5777
5778     // private
5779     mimicBlur : function(e, t){
5780         /*
5781         if(!this.wrap.contains(t) && this.validateBlur()){
5782             this.triggerBlur();
5783         }
5784         */
5785     },
5786
5787     // private
5788     triggerBlur : function(){
5789         this.mimicing = false;
5790         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5791         if(this.monitorTab){
5792             this.el.un("keydown", this.checkTab, this);
5793         }
5794         //this.wrap.removeClass('x-trigger-wrap-focus');
5795         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5796     },
5797
5798     // private
5799     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5800     validateBlur : function(e, t){
5801         return true;
5802     },
5803
5804     // private
5805     onDisable : function(){
5806         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5807         //if(this.wrap){
5808         //    this.wrap.addClass('x-item-disabled');
5809         //}
5810     },
5811
5812     // private
5813     onEnable : function(){
5814         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5815         //if(this.wrap){
5816         //    this.el.removeClass('x-item-disabled');
5817         //}
5818     },
5819
5820     // private
5821     onShow : function(){
5822         var ae = this.getActionEl();
5823         
5824         if(ae){
5825             ae.dom.style.display = '';
5826             ae.dom.style.visibility = 'visible';
5827         }
5828     },
5829
5830     // private
5831     
5832     onHide : function(){
5833         var ae = this.getActionEl();
5834         ae.dom.style.display = 'none';
5835     },
5836
5837     /**
5838      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5839      * by an implementing function.
5840      * @method
5841      * @param {EventObject} e
5842      */
5843     onTriggerClick : Roo.emptyFn
5844 });
5845  /*
5846  * Based on:
5847  * Ext JS Library 1.1.1
5848  * Copyright(c) 2006-2007, Ext JS, LLC.
5849  *
5850  * Originally Released Under LGPL - original licence link has changed is not relivant.
5851  *
5852  * Fork - LGPL
5853  * <script type="text/javascript">
5854  */
5855
5856
5857 /**
5858  * @class Roo.data.SortTypes
5859  * @singleton
5860  * Defines the default sorting (casting?) comparison functions used when sorting data.
5861  */
5862 Roo.data.SortTypes = {
5863     /**
5864      * Default sort that does nothing
5865      * @param {Mixed} s The value being converted
5866      * @return {Mixed} The comparison value
5867      */
5868     none : function(s){
5869         return s;
5870     },
5871     
5872     /**
5873      * The regular expression used to strip tags
5874      * @type {RegExp}
5875      * @property
5876      */
5877     stripTagsRE : /<\/?[^>]+>/gi,
5878     
5879     /**
5880      * Strips all HTML tags to sort on text only
5881      * @param {Mixed} s The value being converted
5882      * @return {String} The comparison value
5883      */
5884     asText : function(s){
5885         return String(s).replace(this.stripTagsRE, "");
5886     },
5887     
5888     /**
5889      * Strips all HTML tags to sort on text only - Case insensitive
5890      * @param {Mixed} s The value being converted
5891      * @return {String} The comparison value
5892      */
5893     asUCText : function(s){
5894         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5895     },
5896     
5897     /**
5898      * Case insensitive string
5899      * @param {Mixed} s The value being converted
5900      * @return {String} The comparison value
5901      */
5902     asUCString : function(s) {
5903         return String(s).toUpperCase();
5904     },
5905     
5906     /**
5907      * Date sorting
5908      * @param {Mixed} s The value being converted
5909      * @return {Number} The comparison value
5910      */
5911     asDate : function(s) {
5912         if(!s){
5913             return 0;
5914         }
5915         if(s instanceof Date){
5916             return s.getTime();
5917         }
5918         return Date.parse(String(s));
5919     },
5920     
5921     /**
5922      * Float sorting
5923      * @param {Mixed} s The value being converted
5924      * @return {Float} The comparison value
5925      */
5926     asFloat : function(s) {
5927         var val = parseFloat(String(s).replace(/,/g, ""));
5928         if(isNaN(val)) val = 0;
5929         return val;
5930     },
5931     
5932     /**
5933      * Integer sorting
5934      * @param {Mixed} s The value being converted
5935      * @return {Number} The comparison value
5936      */
5937     asInt : function(s) {
5938         var val = parseInt(String(s).replace(/,/g, ""));
5939         if(isNaN(val)) val = 0;
5940         return val;
5941     }
5942 };/*
5943  * Based on:
5944  * Ext JS Library 1.1.1
5945  * Copyright(c) 2006-2007, Ext JS, LLC.
5946  *
5947  * Originally Released Under LGPL - original licence link has changed is not relivant.
5948  *
5949  * Fork - LGPL
5950  * <script type="text/javascript">
5951  */
5952
5953 /**
5954 * @class Roo.data.Record
5955  * Instances of this class encapsulate both record <em>definition</em> information, and record
5956  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5957  * to access Records cached in an {@link Roo.data.Store} object.<br>
5958  * <p>
5959  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5960  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5961  * objects.<br>
5962  * <p>
5963  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5964  * @constructor
5965  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5966  * {@link #create}. The parameters are the same.
5967  * @param {Array} data An associative Array of data values keyed by the field name.
5968  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5969  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5970  * not specified an integer id is generated.
5971  */
5972 Roo.data.Record = function(data, id){
5973     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5974     this.data = data;
5975 };
5976
5977 /**
5978  * Generate a constructor for a specific record layout.
5979  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5980  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5981  * Each field definition object may contain the following properties: <ul>
5982  * <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,
5983  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5984  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5985  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5986  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5987  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5988  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5989  * this may be omitted.</p></li>
5990  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5991  * <ul><li>auto (Default, implies no conversion)</li>
5992  * <li>string</li>
5993  * <li>int</li>
5994  * <li>float</li>
5995  * <li>boolean</li>
5996  * <li>date</li></ul></p></li>
5997  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5998  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5999  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6000  * by the Reader into an object that will be stored in the Record. It is passed the
6001  * following parameters:<ul>
6002  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6003  * </ul></p></li>
6004  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6005  * </ul>
6006  * <br>usage:<br><pre><code>
6007 var TopicRecord = Roo.data.Record.create(
6008     {name: 'title', mapping: 'topic_title'},
6009     {name: 'author', mapping: 'username'},
6010     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6011     {name: 'lastPost', mapping: 'post_time', type: 'date'},
6012     {name: 'lastPoster', mapping: 'user2'},
6013     {name: 'excerpt', mapping: 'post_text'}
6014 );
6015
6016 var myNewRecord = new TopicRecord({
6017     title: 'Do my job please',
6018     author: 'noobie',
6019     totalPosts: 1,
6020     lastPost: new Date(),
6021     lastPoster: 'Animal',
6022     excerpt: 'No way dude!'
6023 });
6024 myStore.add(myNewRecord);
6025 </code></pre>
6026  * @method create
6027  * @static
6028  */
6029 Roo.data.Record.create = function(o){
6030     var f = function(){
6031         f.superclass.constructor.apply(this, arguments);
6032     };
6033     Roo.extend(f, Roo.data.Record);
6034     var p = f.prototype;
6035     p.fields = new Roo.util.MixedCollection(false, function(field){
6036         return field.name;
6037     });
6038     for(var i = 0, len = o.length; i < len; i++){
6039         p.fields.add(new Roo.data.Field(o[i]));
6040     }
6041     f.getField = function(name){
6042         return p.fields.get(name);  
6043     };
6044     return f;
6045 };
6046
6047 Roo.data.Record.AUTO_ID = 1000;
6048 Roo.data.Record.EDIT = 'edit';
6049 Roo.data.Record.REJECT = 'reject';
6050 Roo.data.Record.COMMIT = 'commit';
6051
6052 Roo.data.Record.prototype = {
6053     /**
6054      * Readonly flag - true if this record has been modified.
6055      * @type Boolean
6056      */
6057     dirty : false,
6058     editing : false,
6059     error: null,
6060     modified: null,
6061
6062     // private
6063     join : function(store){
6064         this.store = store;
6065     },
6066
6067     /**
6068      * Set the named field to the specified value.
6069      * @param {String} name The name of the field to set.
6070      * @param {Object} value The value to set the field to.
6071      */
6072     set : function(name, value){
6073         if(this.data[name] == value){
6074             return;
6075         }
6076         this.dirty = true;
6077         if(!this.modified){
6078             this.modified = {};
6079         }
6080         if(typeof this.modified[name] == 'undefined'){
6081             this.modified[name] = this.data[name];
6082         }
6083         this.data[name] = value;
6084         if(!this.editing && this.store){
6085             this.store.afterEdit(this);
6086         }       
6087     },
6088
6089     /**
6090      * Get the value of the named field.
6091      * @param {String} name The name of the field to get the value of.
6092      * @return {Object} The value of the field.
6093      */
6094     get : function(name){
6095         return this.data[name]; 
6096     },
6097
6098     // private
6099     beginEdit : function(){
6100         this.editing = true;
6101         this.modified = {}; 
6102     },
6103
6104     // private
6105     cancelEdit : function(){
6106         this.editing = false;
6107         delete this.modified;
6108     },
6109
6110     // private
6111     endEdit : function(){
6112         this.editing = false;
6113         if(this.dirty && this.store){
6114             this.store.afterEdit(this);
6115         }
6116     },
6117
6118     /**
6119      * Usually called by the {@link Roo.data.Store} which owns the Record.
6120      * Rejects all changes made to the Record since either creation, or the last commit operation.
6121      * Modified fields are reverted to their original values.
6122      * <p>
6123      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6124      * of reject operations.
6125      */
6126     reject : function(){
6127         var m = this.modified;
6128         for(var n in m){
6129             if(typeof m[n] != "function"){
6130                 this.data[n] = m[n];
6131             }
6132         }
6133         this.dirty = false;
6134         delete this.modified;
6135         this.editing = false;
6136         if(this.store){
6137             this.store.afterReject(this);
6138         }
6139     },
6140
6141     /**
6142      * Usually called by the {@link Roo.data.Store} which owns the Record.
6143      * Commits all changes made to the Record since either creation, or the last commit operation.
6144      * <p>
6145      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6146      * of commit operations.
6147      */
6148     commit : function(){
6149         this.dirty = false;
6150         delete this.modified;
6151         this.editing = false;
6152         if(this.store){
6153             this.store.afterCommit(this);
6154         }
6155     },
6156
6157     // private
6158     hasError : function(){
6159         return this.error != null;
6160     },
6161
6162     // private
6163     clearError : function(){
6164         this.error = null;
6165     },
6166
6167     /**
6168      * Creates a copy of this record.
6169      * @param {String} id (optional) A new record id if you don't want to use this record's id
6170      * @return {Record}
6171      */
6172     copy : function(newId) {
6173         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6174     }
6175 };/*
6176  * Based on:
6177  * Ext JS Library 1.1.1
6178  * Copyright(c) 2006-2007, Ext JS, LLC.
6179  *
6180  * Originally Released Under LGPL - original licence link has changed is not relivant.
6181  *
6182  * Fork - LGPL
6183  * <script type="text/javascript">
6184  */
6185
6186
6187
6188 /**
6189  * @class Roo.data.Store
6190  * @extends Roo.util.Observable
6191  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6192  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6193  * <p>
6194  * 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
6195  * has no knowledge of the format of the data returned by the Proxy.<br>
6196  * <p>
6197  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6198  * instances from the data object. These records are cached and made available through accessor functions.
6199  * @constructor
6200  * Creates a new Store.
6201  * @param {Object} config A config object containing the objects needed for the Store to access data,
6202  * and read the data into Records.
6203  */
6204 Roo.data.Store = function(config){
6205     this.data = new Roo.util.MixedCollection(false);
6206     this.data.getKey = function(o){
6207         return o.id;
6208     };
6209     this.baseParams = {};
6210     // private
6211     this.paramNames = {
6212         "start" : "start",
6213         "limit" : "limit",
6214         "sort" : "sort",
6215         "dir" : "dir",
6216         "multisort" : "_multisort"
6217     };
6218
6219     if(config && config.data){
6220         this.inlineData = config.data;
6221         delete config.data;
6222     }
6223
6224     Roo.apply(this, config);
6225     
6226     if(this.reader){ // reader passed
6227         this.reader = Roo.factory(this.reader, Roo.data);
6228         this.reader.xmodule = this.xmodule || false;
6229         if(!this.recordType){
6230             this.recordType = this.reader.recordType;
6231         }
6232         if(this.reader.onMetaChange){
6233             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6234         }
6235     }
6236
6237     if(this.recordType){
6238         this.fields = this.recordType.prototype.fields;
6239     }
6240     this.modified = [];
6241
6242     this.addEvents({
6243         /**
6244          * @event datachanged
6245          * Fires when the data cache has changed, and a widget which is using this Store
6246          * as a Record cache should refresh its view.
6247          * @param {Store} this
6248          */
6249         datachanged : true,
6250         /**
6251          * @event metachange
6252          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6253          * @param {Store} this
6254          * @param {Object} meta The JSON metadata
6255          */
6256         metachange : true,
6257         /**
6258          * @event add
6259          * Fires when Records have been added to the Store
6260          * @param {Store} this
6261          * @param {Roo.data.Record[]} records The array of Records added
6262          * @param {Number} index The index at which the record(s) were added
6263          */
6264         add : true,
6265         /**
6266          * @event remove
6267          * Fires when a Record has been removed from the Store
6268          * @param {Store} this
6269          * @param {Roo.data.Record} record The Record that was removed
6270          * @param {Number} index The index at which the record was removed
6271          */
6272         remove : true,
6273         /**
6274          * @event update
6275          * Fires when a Record has been updated
6276          * @param {Store} this
6277          * @param {Roo.data.Record} record The Record that was updated
6278          * @param {String} operation The update operation being performed.  Value may be one of:
6279          * <pre><code>
6280  Roo.data.Record.EDIT
6281  Roo.data.Record.REJECT
6282  Roo.data.Record.COMMIT
6283          * </code></pre>
6284          */
6285         update : true,
6286         /**
6287          * @event clear
6288          * Fires when the data cache has been cleared.
6289          * @param {Store} this
6290          */
6291         clear : true,
6292         /**
6293          * @event beforeload
6294          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6295          * the load action will be canceled.
6296          * @param {Store} this
6297          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6298          */
6299         beforeload : true,
6300         /**
6301          * @event beforeloadadd
6302          * Fires after a new set of Records has been loaded.
6303          * @param {Store} this
6304          * @param {Roo.data.Record[]} records The Records that were loaded
6305          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6306          */
6307         beforeloadadd : true,
6308         /**
6309          * @event load
6310          * Fires after a new set of Records has been loaded, before they are added to the store.
6311          * @param {Store} this
6312          * @param {Roo.data.Record[]} records The Records that were loaded
6313          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6314          * @params {Object} return from reader
6315          */
6316         load : true,
6317         /**
6318          * @event loadexception
6319          * Fires if an exception occurs in the Proxy during loading.
6320          * Called with the signature of the Proxy's "loadexception" event.
6321          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6322          * 
6323          * @param {Proxy} 
6324          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6325          * @param {Object} load options 
6326          * @param {Object} jsonData from your request (normally this contains the Exception)
6327          */
6328         loadexception : true
6329     });
6330     
6331     if(this.proxy){
6332         this.proxy = Roo.factory(this.proxy, Roo.data);
6333         this.proxy.xmodule = this.xmodule || false;
6334         this.relayEvents(this.proxy,  ["loadexception"]);
6335     }
6336     this.sortToggle = {};
6337     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6338
6339     Roo.data.Store.superclass.constructor.call(this);
6340
6341     if(this.inlineData){
6342         this.loadData(this.inlineData);
6343         delete this.inlineData;
6344     }
6345 };
6346
6347 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6348      /**
6349     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6350     * without a remote query - used by combo/forms at present.
6351     */
6352     
6353     /**
6354     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6355     */
6356     /**
6357     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6358     */
6359     /**
6360     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6361     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6362     */
6363     /**
6364     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6365     * on any HTTP request
6366     */
6367     /**
6368     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6369     */
6370     /**
6371     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6372     */
6373     multiSort: false,
6374     /**
6375     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6376     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6377     */
6378     remoteSort : false,
6379
6380     /**
6381     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6382      * loaded or when a record is removed. (defaults to false).
6383     */
6384     pruneModifiedRecords : false,
6385
6386     // private
6387     lastOptions : null,
6388
6389     /**
6390      * Add Records to the Store and fires the add event.
6391      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6392      */
6393     add : function(records){
6394         records = [].concat(records);
6395         for(var i = 0, len = records.length; i < len; i++){
6396             records[i].join(this);
6397         }
6398         var index = this.data.length;
6399         this.data.addAll(records);
6400         this.fireEvent("add", this, records, index);
6401     },
6402
6403     /**
6404      * Remove a Record from the Store and fires the remove event.
6405      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6406      */
6407     remove : function(record){
6408         var index = this.data.indexOf(record);
6409         this.data.removeAt(index);
6410         if(this.pruneModifiedRecords){
6411             this.modified.remove(record);
6412         }
6413         this.fireEvent("remove", this, record, index);
6414     },
6415
6416     /**
6417      * Remove all Records from the Store and fires the clear event.
6418      */
6419     removeAll : function(){
6420         this.data.clear();
6421         if(this.pruneModifiedRecords){
6422             this.modified = [];
6423         }
6424         this.fireEvent("clear", this);
6425     },
6426
6427     /**
6428      * Inserts Records to the Store at the given index and fires the add event.
6429      * @param {Number} index The start index at which to insert the passed Records.
6430      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6431      */
6432     insert : function(index, records){
6433         records = [].concat(records);
6434         for(var i = 0, len = records.length; i < len; i++){
6435             this.data.insert(index, records[i]);
6436             records[i].join(this);
6437         }
6438         this.fireEvent("add", this, records, index);
6439     },
6440
6441     /**
6442      * Get the index within the cache of the passed Record.
6443      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6444      * @return {Number} The index of the passed Record. Returns -1 if not found.
6445      */
6446     indexOf : function(record){
6447         return this.data.indexOf(record);
6448     },
6449
6450     /**
6451      * Get the index within the cache of the Record with the passed id.
6452      * @param {String} id The id of the Record to find.
6453      * @return {Number} The index of the Record. Returns -1 if not found.
6454      */
6455     indexOfId : function(id){
6456         return this.data.indexOfKey(id);
6457     },
6458
6459     /**
6460      * Get the Record with the specified id.
6461      * @param {String} id The id of the Record to find.
6462      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6463      */
6464     getById : function(id){
6465         return this.data.key(id);
6466     },
6467
6468     /**
6469      * Get the Record at the specified index.
6470      * @param {Number} index The index of the Record to find.
6471      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6472      */
6473     getAt : function(index){
6474         return this.data.itemAt(index);
6475     },
6476
6477     /**
6478      * Returns a range of Records between specified indices.
6479      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6480      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6481      * @return {Roo.data.Record[]} An array of Records
6482      */
6483     getRange : function(start, end){
6484         return this.data.getRange(start, end);
6485     },
6486
6487     // private
6488     storeOptions : function(o){
6489         o = Roo.apply({}, o);
6490         delete o.callback;
6491         delete o.scope;
6492         this.lastOptions = o;
6493     },
6494
6495     /**
6496      * Loads the Record cache from the configured Proxy using the configured Reader.
6497      * <p>
6498      * If using remote paging, then the first load call must specify the <em>start</em>
6499      * and <em>limit</em> properties in the options.params property to establish the initial
6500      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6501      * <p>
6502      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6503      * and this call will return before the new data has been loaded. Perform any post-processing
6504      * in a callback function, or in a "load" event handler.</strong>
6505      * <p>
6506      * @param {Object} options An object containing properties which control loading options:<ul>
6507      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6508      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6509      * passed the following arguments:<ul>
6510      * <li>r : Roo.data.Record[]</li>
6511      * <li>options: Options object from the load call</li>
6512      * <li>success: Boolean success indicator</li></ul></li>
6513      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6514      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6515      * </ul>
6516      */
6517     load : function(options){
6518         options = options || {};
6519         if(this.fireEvent("beforeload", this, options) !== false){
6520             this.storeOptions(options);
6521             var p = Roo.apply(options.params || {}, this.baseParams);
6522             // if meta was not loaded from remote source.. try requesting it.
6523             if (!this.reader.metaFromRemote) {
6524                 p._requestMeta = 1;
6525             }
6526             if(this.sortInfo && this.remoteSort){
6527                 var pn = this.paramNames;
6528                 p[pn["sort"]] = this.sortInfo.field;
6529                 p[pn["dir"]] = this.sortInfo.direction;
6530             }
6531             if (this.multiSort) {
6532                 var pn = this.paramNames;
6533                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6534             }
6535             
6536             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6537         }
6538     },
6539
6540     /**
6541      * Reloads the Record cache from the configured Proxy using the configured Reader and
6542      * the options from the last load operation performed.
6543      * @param {Object} options (optional) An object containing properties which may override the options
6544      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6545      * the most recently used options are reused).
6546      */
6547     reload : function(options){
6548         this.load(Roo.applyIf(options||{}, this.lastOptions));
6549     },
6550
6551     // private
6552     // Called as a callback by the Reader during a load operation.
6553     loadRecords : function(o, options, success){
6554         if(!o || success === false){
6555             if(success !== false){
6556                 this.fireEvent("load", this, [], options, o);
6557             }
6558             if(options.callback){
6559                 options.callback.call(options.scope || this, [], options, false);
6560             }
6561             return;
6562         }
6563         // if data returned failure - throw an exception.
6564         if (o.success === false) {
6565             // show a message if no listener is registered.
6566             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6567                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6568             }
6569             // loadmask wil be hooked into this..
6570             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6571             return;
6572         }
6573         var r = o.records, t = o.totalRecords || r.length;
6574         
6575         this.fireEvent("beforeloadadd", this, r, options, o);
6576         
6577         if(!options || options.add !== true){
6578             if(this.pruneModifiedRecords){
6579                 this.modified = [];
6580             }
6581             for(var i = 0, len = r.length; i < len; i++){
6582                 r[i].join(this);
6583             }
6584             if(this.snapshot){
6585                 this.data = this.snapshot;
6586                 delete this.snapshot;
6587             }
6588             this.data.clear();
6589             this.data.addAll(r);
6590             this.totalLength = t;
6591             this.applySort();
6592             this.fireEvent("datachanged", this);
6593         }else{
6594             this.totalLength = Math.max(t, this.data.length+r.length);
6595             this.add(r);
6596         }
6597         this.fireEvent("load", this, r, options, o);
6598         if(options.callback){
6599             options.callback.call(options.scope || this, r, options, true);
6600         }
6601     },
6602
6603
6604     /**
6605      * Loads data from a passed data block. A Reader which understands the format of the data
6606      * must have been configured in the constructor.
6607      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6608      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6609      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6610      */
6611     loadData : function(o, append){
6612         var r = this.reader.readRecords(o);
6613         this.loadRecords(r, {add: append}, true);
6614     },
6615
6616     /**
6617      * Gets the number of cached records.
6618      * <p>
6619      * <em>If using paging, this may not be the total size of the dataset. If the data object
6620      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6621      * the data set size</em>
6622      */
6623     getCount : function(){
6624         return this.data.length || 0;
6625     },
6626
6627     /**
6628      * Gets the total number of records in the dataset as returned by the server.
6629      * <p>
6630      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6631      * the dataset size</em>
6632      */
6633     getTotalCount : function(){
6634         return this.totalLength || 0;
6635     },
6636
6637     /**
6638      * Returns the sort state of the Store as an object with two properties:
6639      * <pre><code>
6640  field {String} The name of the field by which the Records are sorted
6641  direction {String} The sort order, "ASC" or "DESC"
6642      * </code></pre>
6643      */
6644     getSortState : function(){
6645         return this.sortInfo;
6646     },
6647
6648     // private
6649     applySort : function(){
6650         if(this.sortInfo && !this.remoteSort){
6651             var s = this.sortInfo, f = s.field;
6652             var st = this.fields.get(f).sortType;
6653             var fn = function(r1, r2){
6654                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6655                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6656             };
6657             this.data.sort(s.direction, fn);
6658             if(this.snapshot && this.snapshot != this.data){
6659                 this.snapshot.sort(s.direction, fn);
6660             }
6661         }
6662     },
6663
6664     /**
6665      * Sets the default sort column and order to be used by the next load operation.
6666      * @param {String} fieldName The name of the field to sort by.
6667      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6668      */
6669     setDefaultSort : function(field, dir){
6670         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6671     },
6672
6673     /**
6674      * Sort the Records.
6675      * If remote sorting is used, the sort is performed on the server, and the cache is
6676      * reloaded. If local sorting is used, the cache is sorted internally.
6677      * @param {String} fieldName The name of the field to sort by.
6678      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6679      */
6680     sort : function(fieldName, dir){
6681         var f = this.fields.get(fieldName);
6682         if(!dir){
6683             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6684             
6685             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6686                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6687             }else{
6688                 dir = f.sortDir;
6689             }
6690         }
6691         this.sortToggle[f.name] = dir;
6692         this.sortInfo = {field: f.name, direction: dir};
6693         if(!this.remoteSort){
6694             this.applySort();
6695             this.fireEvent("datachanged", this);
6696         }else{
6697             this.load(this.lastOptions);
6698         }
6699     },
6700
6701     /**
6702      * Calls the specified function for each of the Records in the cache.
6703      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6704      * Returning <em>false</em> aborts and exits the iteration.
6705      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6706      */
6707     each : function(fn, scope){
6708         this.data.each(fn, scope);
6709     },
6710
6711     /**
6712      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6713      * (e.g., during paging).
6714      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6715      */
6716     getModifiedRecords : function(){
6717         return this.modified;
6718     },
6719
6720     // private
6721     createFilterFn : function(property, value, anyMatch){
6722         if(!value.exec){ // not a regex
6723             value = String(value);
6724             if(value.length == 0){
6725                 return false;
6726             }
6727             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6728         }
6729         return function(r){
6730             return value.test(r.data[property]);
6731         };
6732     },
6733
6734     /**
6735      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6736      * @param {String} property A field on your records
6737      * @param {Number} start The record index to start at (defaults to 0)
6738      * @param {Number} end The last record index to include (defaults to length - 1)
6739      * @return {Number} The sum
6740      */
6741     sum : function(property, start, end){
6742         var rs = this.data.items, v = 0;
6743         start = start || 0;
6744         end = (end || end === 0) ? end : rs.length-1;
6745
6746         for(var i = start; i <= end; i++){
6747             v += (rs[i].data[property] || 0);
6748         }
6749         return v;
6750     },
6751
6752     /**
6753      * Filter the records by a specified property.
6754      * @param {String} field A field on your records
6755      * @param {String/RegExp} value Either a string that the field
6756      * should start with or a RegExp to test against the field
6757      * @param {Boolean} anyMatch True to match any part not just the beginning
6758      */
6759     filter : function(property, value, anyMatch){
6760         var fn = this.createFilterFn(property, value, anyMatch);
6761         return fn ? this.filterBy(fn) : this.clearFilter();
6762     },
6763
6764     /**
6765      * Filter by a function. The specified function will be called with each
6766      * record in this data source. If the function returns true the record is included,
6767      * otherwise it is filtered.
6768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6769      * @param {Object} scope (optional) The scope of the function (defaults to this)
6770      */
6771     filterBy : function(fn, scope){
6772         this.snapshot = this.snapshot || this.data;
6773         this.data = this.queryBy(fn, scope||this);
6774         this.fireEvent("datachanged", this);
6775     },
6776
6777     /**
6778      * Query the records by a specified property.
6779      * @param {String} field A field on your records
6780      * @param {String/RegExp} value Either a string that the field
6781      * should start with or a RegExp to test against the field
6782      * @param {Boolean} anyMatch True to match any part not just the beginning
6783      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6784      */
6785     query : function(property, value, anyMatch){
6786         var fn = this.createFilterFn(property, value, anyMatch);
6787         return fn ? this.queryBy(fn) : this.data.clone();
6788     },
6789
6790     /**
6791      * Query by a function. The specified function will be called with each
6792      * record in this data source. If the function returns true the record is included
6793      * in the results.
6794      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6795      * @param {Object} scope (optional) The scope of the function (defaults to this)
6796       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6797      **/
6798     queryBy : function(fn, scope){
6799         var data = this.snapshot || this.data;
6800         return data.filterBy(fn, scope||this);
6801     },
6802
6803     /**
6804      * Collects unique values for a particular dataIndex from this store.
6805      * @param {String} dataIndex The property to collect
6806      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6807      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6808      * @return {Array} An array of the unique values
6809      **/
6810     collect : function(dataIndex, allowNull, bypassFilter){
6811         var d = (bypassFilter === true && this.snapshot) ?
6812                 this.snapshot.items : this.data.items;
6813         var v, sv, r = [], l = {};
6814         for(var i = 0, len = d.length; i < len; i++){
6815             v = d[i].data[dataIndex];
6816             sv = String(v);
6817             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6818                 l[sv] = true;
6819                 r[r.length] = v;
6820             }
6821         }
6822         return r;
6823     },
6824
6825     /**
6826      * Revert to a view of the Record cache with no filtering applied.
6827      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6828      */
6829     clearFilter : function(suppressEvent){
6830         if(this.snapshot && this.snapshot != this.data){
6831             this.data = this.snapshot;
6832             delete this.snapshot;
6833             if(suppressEvent !== true){
6834                 this.fireEvent("datachanged", this);
6835             }
6836         }
6837     },
6838
6839     // private
6840     afterEdit : function(record){
6841         if(this.modified.indexOf(record) == -1){
6842             this.modified.push(record);
6843         }
6844         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6845     },
6846     
6847     // private
6848     afterReject : function(record){
6849         this.modified.remove(record);
6850         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6851     },
6852
6853     // private
6854     afterCommit : function(record){
6855         this.modified.remove(record);
6856         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6857     },
6858
6859     /**
6860      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6861      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6862      */
6863     commitChanges : function(){
6864         var m = this.modified.slice(0);
6865         this.modified = [];
6866         for(var i = 0, len = m.length; i < len; i++){
6867             m[i].commit();
6868         }
6869     },
6870
6871     /**
6872      * Cancel outstanding changes on all changed records.
6873      */
6874     rejectChanges : function(){
6875         var m = this.modified.slice(0);
6876         this.modified = [];
6877         for(var i = 0, len = m.length; i < len; i++){
6878             m[i].reject();
6879         }
6880     },
6881
6882     onMetaChange : function(meta, rtype, o){
6883         this.recordType = rtype;
6884         this.fields = rtype.prototype.fields;
6885         delete this.snapshot;
6886         this.sortInfo = meta.sortInfo || this.sortInfo;
6887         this.modified = [];
6888         this.fireEvent('metachange', this, this.reader.meta);
6889     },
6890     
6891     moveIndex : function(data, type)
6892     {
6893         var index = this.indexOf(data);
6894         
6895         var newIndex = index + type;
6896         
6897         this.remove(data);
6898         
6899         this.insert(newIndex, data);
6900         
6901     }
6902 });/*
6903  * Based on:
6904  * Ext JS Library 1.1.1
6905  * Copyright(c) 2006-2007, Ext JS, LLC.
6906  *
6907  * Originally Released Under LGPL - original licence link has changed is not relivant.
6908  *
6909  * Fork - LGPL
6910  * <script type="text/javascript">
6911  */
6912
6913 /**
6914  * @class Roo.data.SimpleStore
6915  * @extends Roo.data.Store
6916  * Small helper class to make creating Stores from Array data easier.
6917  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6918  * @cfg {Array} fields An array of field definition objects, or field name strings.
6919  * @cfg {Array} data The multi-dimensional array of data
6920  * @constructor
6921  * @param {Object} config
6922  */
6923 Roo.data.SimpleStore = function(config){
6924     Roo.data.SimpleStore.superclass.constructor.call(this, {
6925         isLocal : true,
6926         reader: new Roo.data.ArrayReader({
6927                 id: config.id
6928             },
6929             Roo.data.Record.create(config.fields)
6930         ),
6931         proxy : new Roo.data.MemoryProxy(config.data)
6932     });
6933     this.load();
6934 };
6935 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6936  * Based on:
6937  * Ext JS Library 1.1.1
6938  * Copyright(c) 2006-2007, Ext JS, LLC.
6939  *
6940  * Originally Released Under LGPL - original licence link has changed is not relivant.
6941  *
6942  * Fork - LGPL
6943  * <script type="text/javascript">
6944  */
6945
6946 /**
6947 /**
6948  * @extends Roo.data.Store
6949  * @class Roo.data.JsonStore
6950  * Small helper class to make creating Stores for JSON data easier. <br/>
6951 <pre><code>
6952 var store = new Roo.data.JsonStore({
6953     url: 'get-images.php',
6954     root: 'images',
6955     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6956 });
6957 </code></pre>
6958  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6959  * JsonReader and HttpProxy (unless inline data is provided).</b>
6960  * @cfg {Array} fields An array of field definition objects, or field name strings.
6961  * @constructor
6962  * @param {Object} config
6963  */
6964 Roo.data.JsonStore = function(c){
6965     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6966         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6967         reader: new Roo.data.JsonReader(c, c.fields)
6968     }));
6969 };
6970 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6971  * Based on:
6972  * Ext JS Library 1.1.1
6973  * Copyright(c) 2006-2007, Ext JS, LLC.
6974  *
6975  * Originally Released Under LGPL - original licence link has changed is not relivant.
6976  *
6977  * Fork - LGPL
6978  * <script type="text/javascript">
6979  */
6980
6981  
6982 Roo.data.Field = function(config){
6983     if(typeof config == "string"){
6984         config = {name: config};
6985     }
6986     Roo.apply(this, config);
6987     
6988     if(!this.type){
6989         this.type = "auto";
6990     }
6991     
6992     var st = Roo.data.SortTypes;
6993     // named sortTypes are supported, here we look them up
6994     if(typeof this.sortType == "string"){
6995         this.sortType = st[this.sortType];
6996     }
6997     
6998     // set default sortType for strings and dates
6999     if(!this.sortType){
7000         switch(this.type){
7001             case "string":
7002                 this.sortType = st.asUCString;
7003                 break;
7004             case "date":
7005                 this.sortType = st.asDate;
7006                 break;
7007             default:
7008                 this.sortType = st.none;
7009         }
7010     }
7011
7012     // define once
7013     var stripRe = /[\$,%]/g;
7014
7015     // prebuilt conversion function for this field, instead of
7016     // switching every time we're reading a value
7017     if(!this.convert){
7018         var cv, dateFormat = this.dateFormat;
7019         switch(this.type){
7020             case "":
7021             case "auto":
7022             case undefined:
7023                 cv = function(v){ return v; };
7024                 break;
7025             case "string":
7026                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7027                 break;
7028             case "int":
7029                 cv = function(v){
7030                     return v !== undefined && v !== null && v !== '' ?
7031                            parseInt(String(v).replace(stripRe, ""), 10) : '';
7032                     };
7033                 break;
7034             case "float":
7035                 cv = function(v){
7036                     return v !== undefined && v !== null && v !== '' ?
7037                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
7038                     };
7039                 break;
7040             case "bool":
7041             case "boolean":
7042                 cv = function(v){ return v === true || v === "true" || v == 1; };
7043                 break;
7044             case "date":
7045                 cv = function(v){
7046                     if(!v){
7047                         return '';
7048                     }
7049                     if(v instanceof Date){
7050                         return v;
7051                     }
7052                     if(dateFormat){
7053                         if(dateFormat == "timestamp"){
7054                             return new Date(v*1000);
7055                         }
7056                         return Date.parseDate(v, dateFormat);
7057                     }
7058                     var parsed = Date.parse(v);
7059                     return parsed ? new Date(parsed) : null;
7060                 };
7061              break;
7062             
7063         }
7064         this.convert = cv;
7065     }
7066 };
7067
7068 Roo.data.Field.prototype = {
7069     dateFormat: null,
7070     defaultValue: "",
7071     mapping: null,
7072     sortType : null,
7073     sortDir : "ASC"
7074 };/*
7075  * Based on:
7076  * Ext JS Library 1.1.1
7077  * Copyright(c) 2006-2007, Ext JS, LLC.
7078  *
7079  * Originally Released Under LGPL - original licence link has changed is not relivant.
7080  *
7081  * Fork - LGPL
7082  * <script type="text/javascript">
7083  */
7084  
7085 // Base class for reading structured data from a data source.  This class is intended to be
7086 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7087
7088 /**
7089  * @class Roo.data.DataReader
7090  * Base class for reading structured data from a data source.  This class is intended to be
7091  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7092  */
7093
7094 Roo.data.DataReader = function(meta, recordType){
7095     
7096     this.meta = meta;
7097     
7098     this.recordType = recordType instanceof Array ? 
7099         Roo.data.Record.create(recordType) : recordType;
7100 };
7101
7102 Roo.data.DataReader.prototype = {
7103      /**
7104      * Create an empty record
7105      * @param {Object} data (optional) - overlay some values
7106      * @return {Roo.data.Record} record created.
7107      */
7108     newRow :  function(d) {
7109         var da =  {};
7110         this.recordType.prototype.fields.each(function(c) {
7111             switch( c.type) {
7112                 case 'int' : da[c.name] = 0; break;
7113                 case 'date' : da[c.name] = new Date(); break;
7114                 case 'float' : da[c.name] = 0.0; break;
7115                 case 'boolean' : da[c.name] = false; break;
7116                 default : da[c.name] = ""; break;
7117             }
7118             
7119         });
7120         return new this.recordType(Roo.apply(da, d));
7121     }
7122     
7123 };/*
7124  * Based on:
7125  * Ext JS Library 1.1.1
7126  * Copyright(c) 2006-2007, Ext JS, LLC.
7127  *
7128  * Originally Released Under LGPL - original licence link has changed is not relivant.
7129  *
7130  * Fork - LGPL
7131  * <script type="text/javascript">
7132  */
7133
7134 /**
7135  * @class Roo.data.DataProxy
7136  * @extends Roo.data.Observable
7137  * This class is an abstract base class for implementations which provide retrieval of
7138  * unformatted data objects.<br>
7139  * <p>
7140  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7141  * (of the appropriate type which knows how to parse the data object) to provide a block of
7142  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7143  * <p>
7144  * Custom implementations must implement the load method as described in
7145  * {@link Roo.data.HttpProxy#load}.
7146  */
7147 Roo.data.DataProxy = function(){
7148     this.addEvents({
7149         /**
7150          * @event beforeload
7151          * Fires before a network request is made to retrieve a data object.
7152          * @param {Object} This DataProxy object.
7153          * @param {Object} params The params parameter to the load function.
7154          */
7155         beforeload : true,
7156         /**
7157          * @event load
7158          * Fires before the load method's callback is called.
7159          * @param {Object} This DataProxy object.
7160          * @param {Object} o The data object.
7161          * @param {Object} arg The callback argument object passed to the load function.
7162          */
7163         load : true,
7164         /**
7165          * @event loadexception
7166          * Fires if an Exception occurs during data retrieval.
7167          * @param {Object} This DataProxy object.
7168          * @param {Object} o The data object.
7169          * @param {Object} arg The callback argument object passed to the load function.
7170          * @param {Object} e The Exception.
7171          */
7172         loadexception : true
7173     });
7174     Roo.data.DataProxy.superclass.constructor.call(this);
7175 };
7176
7177 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7178
7179     /**
7180      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7181      */
7182 /*
7183  * Based on:
7184  * Ext JS Library 1.1.1
7185  * Copyright(c) 2006-2007, Ext JS, LLC.
7186  *
7187  * Originally Released Under LGPL - original licence link has changed is not relivant.
7188  *
7189  * Fork - LGPL
7190  * <script type="text/javascript">
7191  */
7192 /**
7193  * @class Roo.data.MemoryProxy
7194  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7195  * to the Reader when its load method is called.
7196  * @constructor
7197  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7198  */
7199 Roo.data.MemoryProxy = function(data){
7200     if (data.data) {
7201         data = data.data;
7202     }
7203     Roo.data.MemoryProxy.superclass.constructor.call(this);
7204     this.data = data;
7205 };
7206
7207 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7208     /**
7209      * Load data from the requested source (in this case an in-memory
7210      * data object passed to the constructor), read the data object into
7211      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7212      * process that block using the passed callback.
7213      * @param {Object} params This parameter is not used by the MemoryProxy class.
7214      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7215      * object into a block of Roo.data.Records.
7216      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7217      * The function must be passed <ul>
7218      * <li>The Record block object</li>
7219      * <li>The "arg" argument from the load function</li>
7220      * <li>A boolean success indicator</li>
7221      * </ul>
7222      * @param {Object} scope The scope in which to call the callback
7223      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7224      */
7225     load : function(params, reader, callback, scope, arg){
7226         params = params || {};
7227         var result;
7228         try {
7229             result = reader.readRecords(this.data);
7230         }catch(e){
7231             this.fireEvent("loadexception", this, arg, null, e);
7232             callback.call(scope, null, arg, false);
7233             return;
7234         }
7235         callback.call(scope, result, arg, true);
7236     },
7237     
7238     // private
7239     update : function(params, records){
7240         
7241     }
7242 });/*
7243  * Based on:
7244  * Ext JS Library 1.1.1
7245  * Copyright(c) 2006-2007, Ext JS, LLC.
7246  *
7247  * Originally Released Under LGPL - original licence link has changed is not relivant.
7248  *
7249  * Fork - LGPL
7250  * <script type="text/javascript">
7251  */
7252 /**
7253  * @class Roo.data.HttpProxy
7254  * @extends Roo.data.DataProxy
7255  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7256  * configured to reference a certain URL.<br><br>
7257  * <p>
7258  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7259  * from which the running page was served.<br><br>
7260  * <p>
7261  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7262  * <p>
7263  * Be aware that to enable the browser to parse an XML document, the server must set
7264  * the Content-Type header in the HTTP response to "text/xml".
7265  * @constructor
7266  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7267  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7268  * will be used to make the request.
7269  */
7270 Roo.data.HttpProxy = function(conn){
7271     Roo.data.HttpProxy.superclass.constructor.call(this);
7272     // is conn a conn config or a real conn?
7273     this.conn = conn;
7274     this.useAjax = !conn || !conn.events;
7275   
7276 };
7277
7278 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7279     // thse are take from connection...
7280     
7281     /**
7282      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7283      */
7284     /**
7285      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7286      * extra parameters to each request made by this object. (defaults to undefined)
7287      */
7288     /**
7289      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7290      *  to each request made by this object. (defaults to undefined)
7291      */
7292     /**
7293      * @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)
7294      */
7295     /**
7296      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7297      */
7298      /**
7299      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7300      * @type Boolean
7301      */
7302   
7303
7304     /**
7305      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7306      * @type Boolean
7307      */
7308     /**
7309      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7310      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7311      * a finer-grained basis than the DataProxy events.
7312      */
7313     getConnection : function(){
7314         return this.useAjax ? Roo.Ajax : this.conn;
7315     },
7316
7317     /**
7318      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7319      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7320      * process that block using the passed callback.
7321      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7322      * for the request to the remote server.
7323      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7324      * object into a block of Roo.data.Records.
7325      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7326      * The function must be passed <ul>
7327      * <li>The Record block object</li>
7328      * <li>The "arg" argument from the load function</li>
7329      * <li>A boolean success indicator</li>
7330      * </ul>
7331      * @param {Object} scope The scope in which to call the callback
7332      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7333      */
7334     load : function(params, reader, callback, scope, arg){
7335         if(this.fireEvent("beforeload", this, params) !== false){
7336             var  o = {
7337                 params : params || {},
7338                 request: {
7339                     callback : callback,
7340                     scope : scope,
7341                     arg : arg
7342                 },
7343                 reader: reader,
7344                 callback : this.loadResponse,
7345                 scope: this
7346             };
7347             if(this.useAjax){
7348                 Roo.applyIf(o, this.conn);
7349                 if(this.activeRequest){
7350                     Roo.Ajax.abort(this.activeRequest);
7351                 }
7352                 this.activeRequest = Roo.Ajax.request(o);
7353             }else{
7354                 this.conn.request(o);
7355             }
7356         }else{
7357             callback.call(scope||this, null, arg, false);
7358         }
7359     },
7360
7361     // private
7362     loadResponse : function(o, success, response){
7363         delete this.activeRequest;
7364         if(!success){
7365             this.fireEvent("loadexception", this, o, response);
7366             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7367             return;
7368         }
7369         var result;
7370         try {
7371             result = o.reader.read(response);
7372         }catch(e){
7373             this.fireEvent("loadexception", this, o, response, e);
7374             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7375             return;
7376         }
7377         
7378         this.fireEvent("load", this, o, o.request.arg);
7379         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7380     },
7381
7382     // private
7383     update : function(dataSet){
7384
7385     },
7386
7387     // private
7388     updateResponse : function(dataSet){
7389
7390     }
7391 });/*
7392  * Based on:
7393  * Ext JS Library 1.1.1
7394  * Copyright(c) 2006-2007, Ext JS, LLC.
7395  *
7396  * Originally Released Under LGPL - original licence link has changed is not relivant.
7397  *
7398  * Fork - LGPL
7399  * <script type="text/javascript">
7400  */
7401
7402 /**
7403  * @class Roo.data.ScriptTagProxy
7404  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7405  * other than the originating domain of the running page.<br><br>
7406  * <p>
7407  * <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
7408  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7409  * <p>
7410  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7411  * source code that is used as the source inside a &lt;script> tag.<br><br>
7412  * <p>
7413  * In order for the browser to process the returned data, the server must wrap the data object
7414  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7415  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7416  * depending on whether the callback name was passed:
7417  * <p>
7418  * <pre><code>
7419 boolean scriptTag = false;
7420 String cb = request.getParameter("callback");
7421 if (cb != null) {
7422     scriptTag = true;
7423     response.setContentType("text/javascript");
7424 } else {
7425     response.setContentType("application/x-json");
7426 }
7427 Writer out = response.getWriter();
7428 if (scriptTag) {
7429     out.write(cb + "(");
7430 }
7431 out.print(dataBlock.toJsonString());
7432 if (scriptTag) {
7433     out.write(");");
7434 }
7435 </pre></code>
7436  *
7437  * @constructor
7438  * @param {Object} config A configuration object.
7439  */
7440 Roo.data.ScriptTagProxy = function(config){
7441     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7442     Roo.apply(this, config);
7443     this.head = document.getElementsByTagName("head")[0];
7444 };
7445
7446 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7447
7448 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7449     /**
7450      * @cfg {String} url The URL from which to request the data object.
7451      */
7452     /**
7453      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7454      */
7455     timeout : 30000,
7456     /**
7457      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7458      * the server the name of the callback function set up by the load call to process the returned data object.
7459      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7460      * javascript output which calls this named function passing the data object as its only parameter.
7461      */
7462     callbackParam : "callback",
7463     /**
7464      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7465      * name to the request.
7466      */
7467     nocache : true,
7468
7469     /**
7470      * Load data from the configured URL, read the data object into
7471      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7472      * process that block using the passed callback.
7473      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7474      * for the request to the remote server.
7475      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7476      * object into a block of Roo.data.Records.
7477      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7478      * The function must be passed <ul>
7479      * <li>The Record block object</li>
7480      * <li>The "arg" argument from the load function</li>
7481      * <li>A boolean success indicator</li>
7482      * </ul>
7483      * @param {Object} scope The scope in which to call the callback
7484      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7485      */
7486     load : function(params, reader, callback, scope, arg){
7487         if(this.fireEvent("beforeload", this, params) !== false){
7488
7489             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7490
7491             var url = this.url;
7492             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7493             if(this.nocache){
7494                 url += "&_dc=" + (new Date().getTime());
7495             }
7496             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7497             var trans = {
7498                 id : transId,
7499                 cb : "stcCallback"+transId,
7500                 scriptId : "stcScript"+transId,
7501                 params : params,
7502                 arg : arg,
7503                 url : url,
7504                 callback : callback,
7505                 scope : scope,
7506                 reader : reader
7507             };
7508             var conn = this;
7509
7510             window[trans.cb] = function(o){
7511                 conn.handleResponse(o, trans);
7512             };
7513
7514             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7515
7516             if(this.autoAbort !== false){
7517                 this.abort();
7518             }
7519
7520             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7521
7522             var script = document.createElement("script");
7523             script.setAttribute("src", url);
7524             script.setAttribute("type", "text/javascript");
7525             script.setAttribute("id", trans.scriptId);
7526             this.head.appendChild(script);
7527
7528             this.trans = trans;
7529         }else{
7530             callback.call(scope||this, null, arg, false);
7531         }
7532     },
7533
7534     // private
7535     isLoading : function(){
7536         return this.trans ? true : false;
7537     },
7538
7539     /**
7540      * Abort the current server request.
7541      */
7542     abort : function(){
7543         if(this.isLoading()){
7544             this.destroyTrans(this.trans);
7545         }
7546     },
7547
7548     // private
7549     destroyTrans : function(trans, isLoaded){
7550         this.head.removeChild(document.getElementById(trans.scriptId));
7551         clearTimeout(trans.timeoutId);
7552         if(isLoaded){
7553             window[trans.cb] = undefined;
7554             try{
7555                 delete window[trans.cb];
7556             }catch(e){}
7557         }else{
7558             // if hasn't been loaded, wait for load to remove it to prevent script error
7559             window[trans.cb] = function(){
7560                 window[trans.cb] = undefined;
7561                 try{
7562                     delete window[trans.cb];
7563                 }catch(e){}
7564             };
7565         }
7566     },
7567
7568     // private
7569     handleResponse : function(o, trans){
7570         this.trans = false;
7571         this.destroyTrans(trans, true);
7572         var result;
7573         try {
7574             result = trans.reader.readRecords(o);
7575         }catch(e){
7576             this.fireEvent("loadexception", this, o, trans.arg, e);
7577             trans.callback.call(trans.scope||window, null, trans.arg, false);
7578             return;
7579         }
7580         this.fireEvent("load", this, o, trans.arg);
7581         trans.callback.call(trans.scope||window, result, trans.arg, true);
7582     },
7583
7584     // private
7585     handleFailure : function(trans){
7586         this.trans = false;
7587         this.destroyTrans(trans, false);
7588         this.fireEvent("loadexception", this, null, trans.arg);
7589         trans.callback.call(trans.scope||window, null, trans.arg, false);
7590     }
7591 });/*
7592  * Based on:
7593  * Ext JS Library 1.1.1
7594  * Copyright(c) 2006-2007, Ext JS, LLC.
7595  *
7596  * Originally Released Under LGPL - original licence link has changed is not relivant.
7597  *
7598  * Fork - LGPL
7599  * <script type="text/javascript">
7600  */
7601
7602 /**
7603  * @class Roo.data.JsonReader
7604  * @extends Roo.data.DataReader
7605  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7606  * based on mappings in a provided Roo.data.Record constructor.
7607  * 
7608  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7609  * in the reply previously. 
7610  * 
7611  * <p>
7612  * Example code:
7613  * <pre><code>
7614 var RecordDef = Roo.data.Record.create([
7615     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7616     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7617 ]);
7618 var myReader = new Roo.data.JsonReader({
7619     totalProperty: "results",    // The property which contains the total dataset size (optional)
7620     root: "rows",                // The property which contains an Array of row objects
7621     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7622 }, RecordDef);
7623 </code></pre>
7624  * <p>
7625  * This would consume a JSON file like this:
7626  * <pre><code>
7627 { 'results': 2, 'rows': [
7628     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7629     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7630 }
7631 </code></pre>
7632  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7633  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7634  * paged from the remote server.
7635  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7636  * @cfg {String} root name of the property which contains the Array of row objects.
7637  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7638  * @constructor
7639  * Create a new JsonReader
7640  * @param {Object} meta Metadata configuration options
7641  * @param {Object} recordType Either an Array of field definition objects,
7642  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7643  */
7644 Roo.data.JsonReader = function(meta, recordType){
7645     
7646     meta = meta || {};
7647     // set some defaults:
7648     Roo.applyIf(meta, {
7649         totalProperty: 'total',
7650         successProperty : 'success',
7651         root : 'data',
7652         id : 'id'
7653     });
7654     
7655     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7656 };
7657 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7658     
7659     /**
7660      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7661      * Used by Store query builder to append _requestMeta to params.
7662      * 
7663      */
7664     metaFromRemote : false,
7665     /**
7666      * This method is only used by a DataProxy which has retrieved data from a remote server.
7667      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7668      * @return {Object} data A data block which is used by an Roo.data.Store object as
7669      * a cache of Roo.data.Records.
7670      */
7671     read : function(response){
7672         var json = response.responseText;
7673        
7674         var o = /* eval:var:o */ eval("("+json+")");
7675         if(!o) {
7676             throw {message: "JsonReader.read: Json object not found"};
7677         }
7678         
7679         if(o.metaData){
7680             
7681             delete this.ef;
7682             this.metaFromRemote = true;
7683             this.meta = o.metaData;
7684             this.recordType = Roo.data.Record.create(o.metaData.fields);
7685             this.onMetaChange(this.meta, this.recordType, o);
7686         }
7687         return this.readRecords(o);
7688     },
7689
7690     // private function a store will implement
7691     onMetaChange : function(meta, recordType, o){
7692
7693     },
7694
7695     /**
7696          * @ignore
7697          */
7698     simpleAccess: function(obj, subsc) {
7699         return obj[subsc];
7700     },
7701
7702         /**
7703          * @ignore
7704          */
7705     getJsonAccessor: function(){
7706         var re = /[\[\.]/;
7707         return function(expr) {
7708             try {
7709                 return(re.test(expr))
7710                     ? new Function("obj", "return obj." + expr)
7711                     : function(obj){
7712                         return obj[expr];
7713                     };
7714             } catch(e){}
7715             return Roo.emptyFn;
7716         };
7717     }(),
7718
7719     /**
7720      * Create a data block containing Roo.data.Records from an XML document.
7721      * @param {Object} o An object which contains an Array of row objects in the property specified
7722      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7723      * which contains the total size of the dataset.
7724      * @return {Object} data A data block which is used by an Roo.data.Store object as
7725      * a cache of Roo.data.Records.
7726      */
7727     readRecords : function(o){
7728         /**
7729          * After any data loads, the raw JSON data is available for further custom processing.
7730          * @type Object
7731          */
7732         this.o = o;
7733         var s = this.meta, Record = this.recordType,
7734             f = Record.prototype.fields, fi = f.items, fl = f.length;
7735
7736 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7737         if (!this.ef) {
7738             if(s.totalProperty) {
7739                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7740                 }
7741                 if(s.successProperty) {
7742                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7743                 }
7744                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7745                 if (s.id) {
7746                         var g = this.getJsonAccessor(s.id);
7747                         this.getId = function(rec) {
7748                                 var r = g(rec);
7749                                 return (r === undefined || r === "") ? null : r;
7750                         };
7751                 } else {
7752                         this.getId = function(){return null;};
7753                 }
7754             this.ef = [];
7755             for(var jj = 0; jj < fl; jj++){
7756                 f = fi[jj];
7757                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7758                 this.ef[jj] = this.getJsonAccessor(map);
7759             }
7760         }
7761
7762         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7763         if(s.totalProperty){
7764             var vt = parseInt(this.getTotal(o), 10);
7765             if(!isNaN(vt)){
7766                 totalRecords = vt;
7767             }
7768         }
7769         if(s.successProperty){
7770             var vs = this.getSuccess(o);
7771             if(vs === false || vs === 'false'){
7772                 success = false;
7773             }
7774         }
7775         var records = [];
7776             for(var i = 0; i < c; i++){
7777                     var n = root[i];
7778                 var values = {};
7779                 var id = this.getId(n);
7780                 for(var j = 0; j < fl; j++){
7781                     f = fi[j];
7782                 var v = this.ef[j](n);
7783                 if (!f.convert) {
7784                     Roo.log('missing convert for ' + f.name);
7785                     Roo.log(f);
7786                     continue;
7787                 }
7788                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7789                 }
7790                 var record = new Record(values, id);
7791                 record.json = n;
7792                 records[i] = record;
7793             }
7794             return {
7795             raw : o,
7796                 success : success,
7797                 records : records,
7798                 totalRecords : totalRecords
7799             };
7800     }
7801 });/*
7802  * Based on:
7803  * Ext JS Library 1.1.1
7804  * Copyright(c) 2006-2007, Ext JS, LLC.
7805  *
7806  * Originally Released Under LGPL - original licence link has changed is not relivant.
7807  *
7808  * Fork - LGPL
7809  * <script type="text/javascript">
7810  */
7811
7812 /**
7813  * @class Roo.data.ArrayReader
7814  * @extends Roo.data.DataReader
7815  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7816  * Each element of that Array represents a row of data fields. The
7817  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7818  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7819  * <p>
7820  * Example code:.
7821  * <pre><code>
7822 var RecordDef = Roo.data.Record.create([
7823     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7824     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7825 ]);
7826 var myReader = new Roo.data.ArrayReader({
7827     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7828 }, RecordDef);
7829 </code></pre>
7830  * <p>
7831  * This would consume an Array like this:
7832  * <pre><code>
7833 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7834   </code></pre>
7835  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7836  * @constructor
7837  * Create a new JsonReader
7838  * @param {Object} meta Metadata configuration options.
7839  * @param {Object} recordType Either an Array of field definition objects
7840  * as specified to {@link Roo.data.Record#create},
7841  * or an {@link Roo.data.Record} object
7842  * created using {@link Roo.data.Record#create}.
7843  */
7844 Roo.data.ArrayReader = function(meta, recordType){
7845     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7846 };
7847
7848 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7849     /**
7850      * Create a data block containing Roo.data.Records from an XML document.
7851      * @param {Object} o An Array of row objects which represents the dataset.
7852      * @return {Object} data A data block which is used by an Roo.data.Store object as
7853      * a cache of Roo.data.Records.
7854      */
7855     readRecords : function(o){
7856         var sid = this.meta ? this.meta.id : null;
7857         var recordType = this.recordType, fields = recordType.prototype.fields;
7858         var records = [];
7859         var root = o;
7860             for(var i = 0; i < root.length; i++){
7861                     var n = root[i];
7862                 var values = {};
7863                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7864                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7865                 var f = fields.items[j];
7866                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7867                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7868                 v = f.convert(v);
7869                 values[f.name] = v;
7870             }
7871                 var record = new recordType(values, id);
7872                 record.json = n;
7873                 records[records.length] = record;
7874             }
7875             return {
7876                 records : records,
7877                 totalRecords : records.length
7878             };
7879     }
7880 });/*
7881  * - LGPL
7882  * * 
7883  */
7884
7885 /**
7886  * @class Roo.bootstrap.ComboBox
7887  * @extends Roo.bootstrap.TriggerField
7888  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7889  * @cfg {Boolean} append (true|false) default false
7890  * @constructor
7891  * Create a new ComboBox.
7892  * @param {Object} config Configuration options
7893  */
7894 Roo.bootstrap.ComboBox = function(config){
7895     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7896     this.addEvents({
7897         /**
7898          * @event expand
7899          * Fires when the dropdown list is expanded
7900              * @param {Roo.bootstrap.ComboBox} combo This combo box
7901              */
7902         'expand' : true,
7903         /**
7904          * @event collapse
7905          * Fires when the dropdown list is collapsed
7906              * @param {Roo.bootstrap.ComboBox} combo This combo box
7907              */
7908         'collapse' : true,
7909         /**
7910          * @event beforeselect
7911          * Fires before a list item is selected. Return false to cancel the selection.
7912              * @param {Roo.bootstrap.ComboBox} combo This combo box
7913              * @param {Roo.data.Record} record The data record returned from the underlying store
7914              * @param {Number} index The index of the selected item in the dropdown list
7915              */
7916         'beforeselect' : true,
7917         /**
7918          * @event select
7919          * Fires when a list item is selected
7920              * @param {Roo.bootstrap.ComboBox} combo This combo box
7921              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7922              * @param {Number} index The index of the selected item in the dropdown list
7923              */
7924         'select' : true,
7925         /**
7926          * @event beforequery
7927          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7928          * The event object passed has these properties:
7929              * @param {Roo.bootstrap.ComboBox} combo This combo box
7930              * @param {String} query The query
7931              * @param {Boolean} forceAll true to force "all" query
7932              * @param {Boolean} cancel true to cancel the query
7933              * @param {Object} e The query event object
7934              */
7935         'beforequery': true,
7936          /**
7937          * @event add
7938          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7939              * @param {Roo.bootstrap.ComboBox} combo This combo box
7940              */
7941         'add' : true,
7942         /**
7943          * @event edit
7944          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7945              * @param {Roo.bootstrap.ComboBox} combo This combo box
7946              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7947              */
7948         'edit' : true,
7949         /**
7950          * @event remove
7951          * Fires when the remove value from the combobox array
7952              * @param {Roo.bootstrap.ComboBox} combo This combo box
7953              */
7954         'remove' : true
7955         
7956     });
7957     
7958     
7959     this.selectedIndex = -1;
7960     if(this.mode == 'local'){
7961         if(config.queryDelay === undefined){
7962             this.queryDelay = 10;
7963         }
7964         if(config.minChars === undefined){
7965             this.minChars = 0;
7966         }
7967     }
7968 };
7969
7970 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7971      
7972     /**
7973      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7974      * rendering into an Roo.Editor, defaults to false)
7975      */
7976     /**
7977      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7978      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7979      */
7980     /**
7981      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7982      */
7983     /**
7984      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7985      * the dropdown list (defaults to undefined, with no header element)
7986      */
7987
7988      /**
7989      * @cfg {String/Roo.Template} tpl The template to use to render the output
7990      */
7991      
7992      /**
7993      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7994      */
7995     listWidth: undefined,
7996     /**
7997      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7998      * mode = 'remote' or 'text' if mode = 'local')
7999      */
8000     displayField: undefined,
8001     /**
8002      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8003      * mode = 'remote' or 'value' if mode = 'local'). 
8004      * Note: use of a valueField requires the user make a selection
8005      * in order for a value to be mapped.
8006      */
8007     valueField: undefined,
8008     
8009     
8010     /**
8011      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8012      * field's data value (defaults to the underlying DOM element's name)
8013      */
8014     hiddenName: undefined,
8015     /**
8016      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8017      */
8018     listClass: '',
8019     /**
8020      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8021      */
8022     selectedClass: 'active',
8023     
8024     /**
8025      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8026      */
8027     shadow:'sides',
8028     /**
8029      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8030      * anchor positions (defaults to 'tl-bl')
8031      */
8032     listAlign: 'tl-bl?',
8033     /**
8034      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8035      */
8036     maxHeight: 300,
8037     /**
8038      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
8039      * query specified by the allQuery config option (defaults to 'query')
8040      */
8041     triggerAction: 'query',
8042     /**
8043      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8044      * (defaults to 4, does not apply if editable = false)
8045      */
8046     minChars : 4,
8047     /**
8048      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8049      * delay (typeAheadDelay) if it matches a known value (defaults to false)
8050      */
8051     typeAhead: false,
8052     /**
8053      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8054      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8055      */
8056     queryDelay: 500,
8057     /**
8058      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8059      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
8060      */
8061     pageSize: 0,
8062     /**
8063      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
8064      * when editable = true (defaults to false)
8065      */
8066     selectOnFocus:false,
8067     /**
8068      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8069      */
8070     queryParam: 'query',
8071     /**
8072      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
8073      * when mode = 'remote' (defaults to 'Loading...')
8074      */
8075     loadingText: 'Loading...',
8076     /**
8077      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8078      */
8079     resizable: false,
8080     /**
8081      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8082      */
8083     handleHeight : 8,
8084     /**
8085      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8086      * traditional select (defaults to true)
8087      */
8088     editable: true,
8089     /**
8090      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8091      */
8092     allQuery: '',
8093     /**
8094      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8095      */
8096     mode: 'remote',
8097     /**
8098      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8099      * listWidth has a higher value)
8100      */
8101     minListWidth : 70,
8102     /**
8103      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8104      * allow the user to set arbitrary text into the field (defaults to false)
8105      */
8106     forceSelection:false,
8107     /**
8108      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8109      * if typeAhead = true (defaults to 250)
8110      */
8111     typeAheadDelay : 250,
8112     /**
8113      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8114      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8115      */
8116     valueNotFoundText : undefined,
8117     /**
8118      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8119      */
8120     blockFocus : false,
8121     
8122     /**
8123      * @cfg {Boolean} disableClear Disable showing of clear button.
8124      */
8125     disableClear : false,
8126     /**
8127      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8128      */
8129     alwaysQuery : false,
8130     
8131     /**
8132      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8133      */
8134     multiple : false,
8135     
8136     //private
8137     addicon : false,
8138     editicon: false,
8139     
8140     page: 0,
8141     hasQuery: false,
8142     append: false,
8143     loadNext: false,
8144     item: [],
8145     
8146     // element that contains real text value.. (when hidden is used..)
8147      
8148     // private
8149     initEvents: function(){
8150         
8151         if (!this.store) {
8152             throw "can not find store for combo";
8153         }
8154         this.store = Roo.factory(this.store, Roo.data);
8155         
8156         
8157         
8158         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8159         
8160         
8161         if(this.hiddenName){
8162             
8163             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8164             
8165             this.hiddenField.dom.value =
8166                 this.hiddenValue !== undefined ? this.hiddenValue :
8167                 this.value !== undefined ? this.value : '';
8168
8169             // prevent input submission
8170             this.el.dom.removeAttribute('name');
8171             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8172              
8173              
8174         }
8175         //if(Roo.isGecko){
8176         //    this.el.dom.setAttribute('autocomplete', 'off');
8177         //}
8178
8179         var cls = 'x-combo-list';
8180         this.list = this.el.select('ul.dropdown-menu',true).first();
8181
8182         //this.list = new Roo.Layer({
8183         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8184         //});
8185         
8186         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8187         this.list.setWidth(lw);
8188         
8189         this.list.on('mouseover', this.onViewOver, this);
8190         this.list.on('mousemove', this.onViewMove, this);
8191         
8192         this.list.on('scroll', this.onViewScroll, this);
8193         
8194         /*
8195         this.list.swallowEvent('mousewheel');
8196         this.assetHeight = 0;
8197
8198         if(this.title){
8199             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8200             this.assetHeight += this.header.getHeight();
8201         }
8202
8203         this.innerList = this.list.createChild({cls:cls+'-inner'});
8204         this.innerList.on('mouseover', this.onViewOver, this);
8205         this.innerList.on('mousemove', this.onViewMove, this);
8206         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8207         
8208         if(this.allowBlank && !this.pageSize && !this.disableClear){
8209             this.footer = this.list.createChild({cls:cls+'-ft'});
8210             this.pageTb = new Roo.Toolbar(this.footer);
8211            
8212         }
8213         if(this.pageSize){
8214             this.footer = this.list.createChild({cls:cls+'-ft'});
8215             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8216                     {pageSize: this.pageSize});
8217             
8218         }
8219         
8220         if (this.pageTb && this.allowBlank && !this.disableClear) {
8221             var _this = this;
8222             this.pageTb.add(new Roo.Toolbar.Fill(), {
8223                 cls: 'x-btn-icon x-btn-clear',
8224                 text: '&#160;',
8225                 handler: function()
8226                 {
8227                     _this.collapse();
8228                     _this.clearValue();
8229                     _this.onSelect(false, -1);
8230                 }
8231             });
8232         }
8233         if (this.footer) {
8234             this.assetHeight += this.footer.getHeight();
8235         }
8236         */
8237             
8238         if(!this.tpl){
8239             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8240         }
8241
8242         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8243             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8244         });
8245         //this.view.wrapEl.setDisplayed(false);
8246         this.view.on('click', this.onViewClick, this);
8247         
8248         
8249         
8250         this.store.on('beforeload', this.onBeforeLoad, this);
8251         this.store.on('load', this.onLoad, this);
8252         this.store.on('loadexception', this.onLoadException, this);
8253         /*
8254         if(this.resizable){
8255             this.resizer = new Roo.Resizable(this.list,  {
8256                pinned:true, handles:'se'
8257             });
8258             this.resizer.on('resize', function(r, w, h){
8259                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8260                 this.listWidth = w;
8261                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8262                 this.restrictHeight();
8263             }, this);
8264             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8265         }
8266         */
8267         if(!this.editable){
8268             this.editable = true;
8269             this.setEditable(false);
8270         }
8271         
8272         /*
8273         
8274         if (typeof(this.events.add.listeners) != 'undefined') {
8275             
8276             this.addicon = this.wrap.createChild(
8277                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8278        
8279             this.addicon.on('click', function(e) {
8280                 this.fireEvent('add', this);
8281             }, this);
8282         }
8283         if (typeof(this.events.edit.listeners) != 'undefined') {
8284             
8285             this.editicon = this.wrap.createChild(
8286                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8287             if (this.addicon) {
8288                 this.editicon.setStyle('margin-left', '40px');
8289             }
8290             this.editicon.on('click', function(e) {
8291                 
8292                 // we fire even  if inothing is selected..
8293                 this.fireEvent('edit', this, this.lastData );
8294                 
8295             }, this);
8296         }
8297         */
8298         
8299         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8300             "up" : function(e){
8301                 this.inKeyMode = true;
8302                 this.selectPrev();
8303             },
8304
8305             "down" : function(e){
8306                 if(!this.isExpanded()){
8307                     this.onTriggerClick();
8308                 }else{
8309                     this.inKeyMode = true;
8310                     this.selectNext();
8311                 }
8312             },
8313
8314             "enter" : function(e){
8315                 this.onViewClick();
8316                 //return true;
8317             },
8318
8319             "esc" : function(e){
8320                 this.collapse();
8321             },
8322
8323             "tab" : function(e){
8324                 this.collapse();
8325                 
8326                 if(this.fireEvent("specialkey", this, e)){
8327                     this.onViewClick(false);
8328                 }
8329                 
8330                 return true;
8331             },
8332
8333             scope : this,
8334
8335             doRelay : function(foo, bar, hname){
8336                 if(hname == 'down' || this.scope.isExpanded()){
8337                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8338                 }
8339                 return true;
8340             },
8341
8342             forceKeyDown: true
8343         });
8344         
8345         
8346         this.queryDelay = Math.max(this.queryDelay || 10,
8347                 this.mode == 'local' ? 10 : 250);
8348         
8349         
8350         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8351         
8352         if(this.typeAhead){
8353             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8354         }
8355         if(this.editable !== false){
8356             this.inputEl().on("keyup", this.onKeyUp, this);
8357         }
8358         if(this.forceSelection){
8359             this.on('blur', this.doForce, this);
8360         }
8361         
8362         if(this.multiple){
8363             this.choices = this.el.select('ul.select2-choices', true).first();
8364             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8365         }
8366     },
8367
8368     onDestroy : function(){
8369         if(this.view){
8370             this.view.setStore(null);
8371             this.view.el.removeAllListeners();
8372             this.view.el.remove();
8373             this.view.purgeListeners();
8374         }
8375         if(this.list){
8376             this.list.dom.innerHTML  = '';
8377         }
8378         if(this.store){
8379             this.store.un('beforeload', this.onBeforeLoad, this);
8380             this.store.un('load', this.onLoad, this);
8381             this.store.un('loadexception', this.onLoadException, this);
8382         }
8383         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8384     },
8385
8386     // private
8387     fireKey : function(e){
8388         if(e.isNavKeyPress() && !this.list.isVisible()){
8389             this.fireEvent("specialkey", this, e);
8390         }
8391     },
8392
8393     // private
8394     onResize: function(w, h){
8395 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8396 //        
8397 //        if(typeof w != 'number'){
8398 //            // we do not handle it!?!?
8399 //            return;
8400 //        }
8401 //        var tw = this.trigger.getWidth();
8402 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8403 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8404 //        var x = w - tw;
8405 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8406 //            
8407 //        //this.trigger.setStyle('left', x+'px');
8408 //        
8409 //        if(this.list && this.listWidth === undefined){
8410 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8411 //            this.list.setWidth(lw);
8412 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8413 //        }
8414         
8415     
8416         
8417     },
8418
8419     /**
8420      * Allow or prevent the user from directly editing the field text.  If false is passed,
8421      * the user will only be able to select from the items defined in the dropdown list.  This method
8422      * is the runtime equivalent of setting the 'editable' config option at config time.
8423      * @param {Boolean} value True to allow the user to directly edit the field text
8424      */
8425     setEditable : function(value){
8426         if(value == this.editable){
8427             return;
8428         }
8429         this.editable = value;
8430         if(!value){
8431             this.inputEl().dom.setAttribute('readOnly', true);
8432             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8433             this.inputEl().addClass('x-combo-noedit');
8434         }else{
8435             this.inputEl().dom.setAttribute('readOnly', false);
8436             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8437             this.inputEl().removeClass('x-combo-noedit');
8438         }
8439     },
8440
8441     // private
8442     
8443     onBeforeLoad : function(combo,opts){
8444         if(!this.hasFocus){
8445             return;
8446         }
8447          if (!opts.add) {
8448             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8449          }
8450         this.restrictHeight();
8451         this.selectedIndex = -1;
8452     },
8453
8454     // private
8455     onLoad : function(){
8456         
8457         this.hasQuery = false;
8458         
8459         if(!this.hasFocus){
8460             return;
8461         }
8462         
8463         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8464             this.loading.hide();
8465         }
8466         
8467         if(this.store.getCount() > 0){
8468             this.expand();
8469             this.restrictHeight();
8470             if(this.lastQuery == this.allQuery){
8471                 if(this.editable){
8472                     this.inputEl().dom.select();
8473                 }
8474                 if(!this.selectByValue(this.value, true)){
8475                     this.select(0, true);
8476                 }
8477             }else{
8478                 this.selectNext();
8479                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8480                     this.taTask.delay(this.typeAheadDelay);
8481                 }
8482             }
8483         }else{
8484             this.onEmptyResults();
8485         }
8486         
8487         //this.el.focus();
8488     },
8489     // private
8490     onLoadException : function()
8491     {
8492         this.hasQuery = false;
8493         
8494         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8495             this.loading.hide();
8496         }
8497         
8498         this.collapse();
8499         Roo.log(this.store.reader.jsonData);
8500         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8501             // fixme
8502             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8503         }
8504         
8505         
8506     },
8507     // private
8508     onTypeAhead : function(){
8509         if(this.store.getCount() > 0){
8510             var r = this.store.getAt(0);
8511             var newValue = r.data[this.displayField];
8512             var len = newValue.length;
8513             var selStart = this.getRawValue().length;
8514             
8515             if(selStart != len){
8516                 this.setRawValue(newValue);
8517                 this.selectText(selStart, newValue.length);
8518             }
8519         }
8520     },
8521
8522     // private
8523     onSelect : function(record, index){
8524         
8525         if(this.fireEvent('beforeselect', this, record, index) !== false){
8526         
8527             this.setFromData(index > -1 ? record.data : false);
8528             
8529             this.collapse();
8530             this.fireEvent('select', this, record, index);
8531         }
8532     },
8533
8534     /**
8535      * Returns the currently selected field value or empty string if no value is set.
8536      * @return {String} value The selected value
8537      */
8538     getValue : function(){
8539         
8540         if(this.multiple){
8541             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8542         }
8543         
8544         if(this.valueField){
8545             return typeof this.value != 'undefined' ? this.value : '';
8546         }else{
8547             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8548         }
8549     },
8550
8551     /**
8552      * Clears any text/value currently set in the field
8553      */
8554     clearValue : function(){
8555         if(this.hiddenField){
8556             this.hiddenField.dom.value = '';
8557         }
8558         this.value = '';
8559         this.setRawValue('');
8560         this.lastSelectionText = '';
8561         
8562     },
8563
8564     /**
8565      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8566      * will be displayed in the field.  If the value does not match the data value of an existing item,
8567      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8568      * Otherwise the field will be blank (although the value will still be set).
8569      * @param {String} value The value to match
8570      */
8571     setValue : function(v){
8572         if(this.multiple){
8573             this.syncValue();
8574             return;
8575         }
8576         
8577         var text = v;
8578         if(this.valueField){
8579             var r = this.findRecord(this.valueField, v);
8580             if(r){
8581                 text = r.data[this.displayField];
8582             }else if(this.valueNotFoundText !== undefined){
8583                 text = this.valueNotFoundText;
8584             }
8585         }
8586         this.lastSelectionText = text;
8587         if(this.hiddenField){
8588             this.hiddenField.dom.value = v;
8589         }
8590         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8591         this.value = v;
8592     },
8593     /**
8594      * @property {Object} the last set data for the element
8595      */
8596     
8597     lastData : false,
8598     /**
8599      * Sets the value of the field based on a object which is related to the record format for the store.
8600      * @param {Object} value the value to set as. or false on reset?
8601      */
8602     setFromData : function(o){
8603         
8604         if(this.multiple){
8605             this.addItem(o);
8606             return;
8607         }
8608             
8609         var dv = ''; // display value
8610         var vv = ''; // value value..
8611         this.lastData = o;
8612         if (this.displayField) {
8613             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8614         } else {
8615             // this is an error condition!!!
8616             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8617         }
8618         
8619         if(this.valueField){
8620             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8621         }
8622         
8623         if(this.hiddenField){
8624             this.hiddenField.dom.value = vv;
8625             
8626             this.lastSelectionText = dv;
8627             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8628             this.value = vv;
8629             return;
8630         }
8631         // no hidden field.. - we store the value in 'value', but still display
8632         // display field!!!!
8633         this.lastSelectionText = dv;
8634         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8635         this.value = vv;
8636         
8637         
8638     },
8639     // private
8640     reset : function(){
8641         // overridden so that last data is reset..
8642         this.setValue(this.originalValue);
8643         this.clearInvalid();
8644         this.lastData = false;
8645         if (this.view) {
8646             this.view.clearSelections();
8647         }
8648     },
8649     // private
8650     findRecord : function(prop, value){
8651         var record;
8652         if(this.store.getCount() > 0){
8653             this.store.each(function(r){
8654                 if(r.data[prop] == value){
8655                     record = r;
8656                     return false;
8657                 }
8658                 return true;
8659             });
8660         }
8661         return record;
8662     },
8663     
8664     getName: function()
8665     {
8666         // returns hidden if it's set..
8667         if (!this.rendered) {return ''};
8668         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8669         
8670     },
8671     // private
8672     onViewMove : function(e, t){
8673         this.inKeyMode = false;
8674     },
8675
8676     // private
8677     onViewOver : function(e, t){
8678         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8679             return;
8680         }
8681         var item = this.view.findItemFromChild(t);
8682         if(item){
8683             var index = this.view.indexOf(item);
8684             this.select(index, false);
8685         }
8686     },
8687
8688     // private
8689     onViewClick : function(doFocus)
8690     {
8691         var index = this.view.getSelectedIndexes()[0];
8692         var r = this.store.getAt(index);
8693         if(r){
8694             this.onSelect(r, index);
8695         }
8696         if(doFocus !== false && !this.blockFocus){
8697             this.inputEl().focus();
8698         }
8699     },
8700
8701     // private
8702     restrictHeight : function(){
8703         //this.innerList.dom.style.height = '';
8704         //var inner = this.innerList.dom;
8705         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8706         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8707         //this.list.beginUpdate();
8708         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8709         this.list.alignTo(this.inputEl(), this.listAlign);
8710         //this.list.endUpdate();
8711     },
8712
8713     // private
8714     onEmptyResults : function(){
8715         this.collapse();
8716     },
8717
8718     /**
8719      * Returns true if the dropdown list is expanded, else false.
8720      */
8721     isExpanded : function(){
8722         return this.list.isVisible();
8723     },
8724
8725     /**
8726      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8727      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8728      * @param {String} value The data value of the item to select
8729      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8730      * selected item if it is not currently in view (defaults to true)
8731      * @return {Boolean} True if the value matched an item in the list, else false
8732      */
8733     selectByValue : function(v, scrollIntoView){
8734         if(v !== undefined && v !== null){
8735             var r = this.findRecord(this.valueField || this.displayField, v);
8736             if(r){
8737                 this.select(this.store.indexOf(r), scrollIntoView);
8738                 return true;
8739             }
8740         }
8741         return false;
8742     },
8743
8744     /**
8745      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8746      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8747      * @param {Number} index The zero-based index of the list item to select
8748      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8749      * selected item if it is not currently in view (defaults to true)
8750      */
8751     select : function(index, scrollIntoView){
8752         this.selectedIndex = index;
8753         this.view.select(index);
8754         if(scrollIntoView !== false){
8755             var el = this.view.getNode(index);
8756             if(el){
8757                 //this.innerList.scrollChildIntoView(el, false);
8758                 
8759             }
8760         }
8761     },
8762
8763     // private
8764     selectNext : function(){
8765         var ct = this.store.getCount();
8766         if(ct > 0){
8767             if(this.selectedIndex == -1){
8768                 this.select(0);
8769             }else if(this.selectedIndex < ct-1){
8770                 this.select(this.selectedIndex+1);
8771             }
8772         }
8773     },
8774
8775     // private
8776     selectPrev : function(){
8777         var ct = this.store.getCount();
8778         if(ct > 0){
8779             if(this.selectedIndex == -1){
8780                 this.select(0);
8781             }else if(this.selectedIndex != 0){
8782                 this.select(this.selectedIndex-1);
8783             }
8784         }
8785     },
8786
8787     // private
8788     onKeyUp : function(e){
8789         if(this.editable !== false && !e.isSpecialKey()){
8790             this.lastKey = e.getKey();
8791             this.dqTask.delay(this.queryDelay);
8792         }
8793     },
8794
8795     // private
8796     validateBlur : function(){
8797         return !this.list || !this.list.isVisible();   
8798     },
8799
8800     // private
8801     initQuery : function(){
8802         this.doQuery(this.getRawValue());
8803     },
8804
8805     // private
8806     doForce : function(){
8807         if(this.el.dom.value.length > 0){
8808             this.el.dom.value =
8809                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8810              
8811         }
8812     },
8813
8814     /**
8815      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8816      * query allowing the query action to be canceled if needed.
8817      * @param {String} query The SQL query to execute
8818      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8819      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8820      * saved in the current store (defaults to false)
8821      */
8822     doQuery : function(q, forceAll){
8823         
8824         if(q === undefined || q === null){
8825             q = '';
8826         }
8827         var qe = {
8828             query: q,
8829             forceAll: forceAll,
8830             combo: this,
8831             cancel:false
8832         };
8833         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8834             return false;
8835         }
8836         q = qe.query;
8837         
8838         forceAll = qe.forceAll;
8839         if(forceAll === true || (q.length >= this.minChars)){
8840             
8841             this.hasQuery = true;
8842             
8843             if(this.lastQuery != q || this.alwaysQuery){
8844                 this.lastQuery = q;
8845                 if(this.mode == 'local'){
8846                     this.selectedIndex = -1;
8847                     if(forceAll){
8848                         this.store.clearFilter();
8849                     }else{
8850                         this.store.filter(this.displayField, q);
8851                     }
8852                     this.onLoad();
8853                 }else{
8854                     this.store.baseParams[this.queryParam] = q;
8855                     
8856                     var options = {params : this.getParams(q)};
8857                     
8858                     if(this.loadNext){
8859                         options.add = true;
8860                         options.params.start = this.page * this.pageSize;
8861                     }
8862                     
8863                     this.store.load(options);
8864                     this.expand();
8865                 }
8866             }else{
8867                 this.selectedIndex = -1;
8868                 this.onLoad();   
8869             }
8870         }
8871         
8872         this.loadNext = false;
8873     },
8874
8875     // private
8876     getParams : function(q){
8877         var p = {};
8878         //p[this.queryParam] = q;
8879         
8880         if(this.pageSize){
8881             p.start = 0;
8882             p.limit = this.pageSize;
8883         }
8884         return p;
8885     },
8886
8887     /**
8888      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8889      */
8890     collapse : function(){
8891         if(!this.isExpanded()){
8892             return;
8893         }
8894         
8895         this.list.hide();
8896         Roo.get(document).un('mousedown', this.collapseIf, this);
8897         Roo.get(document).un('mousewheel', this.collapseIf, this);
8898         if (!this.editable) {
8899             Roo.get(document).un('keydown', this.listKeyPress, this);
8900         }
8901         this.fireEvent('collapse', this);
8902     },
8903
8904     // private
8905     collapseIf : function(e){
8906         var in_combo  = e.within(this.el);
8907         var in_list =  e.within(this.list);
8908         
8909         if (in_combo || in_list) {
8910             //e.stopPropagation();
8911             return;
8912         }
8913
8914         this.collapse();
8915         
8916     },
8917
8918     /**
8919      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8920      */
8921     expand : function(){
8922        
8923         if(this.isExpanded() || !this.hasFocus){
8924             return;
8925         }
8926          Roo.log('expand');
8927         this.list.alignTo(this.inputEl(), this.listAlign);
8928         this.list.show();
8929         Roo.get(document).on('mousedown', this.collapseIf, this);
8930         Roo.get(document).on('mousewheel', this.collapseIf, this);
8931         if (!this.editable) {
8932             Roo.get(document).on('keydown', this.listKeyPress, this);
8933         }
8934         
8935         this.fireEvent('expand', this);
8936     },
8937
8938     // private
8939     // Implements the default empty TriggerField.onTriggerClick function
8940     onTriggerClick : function()
8941     {
8942         Roo.log('trigger click');
8943         
8944         if(this.disabled){
8945             return;
8946         }
8947         
8948         this.page = 0;
8949         this.loadNext = false;
8950         
8951         if(this.isExpanded()){
8952             this.collapse();
8953             if (!this.blockFocus) {
8954                 this.inputEl().focus();
8955             }
8956             
8957         }else {
8958             this.hasFocus = true;
8959             if(this.triggerAction == 'all') {
8960                 this.doQuery(this.allQuery, true);
8961             } else {
8962                 this.doQuery(this.getRawValue());
8963             }
8964             if (!this.blockFocus) {
8965                 this.inputEl().focus();
8966             }
8967         }
8968     },
8969     listKeyPress : function(e)
8970     {
8971         //Roo.log('listkeypress');
8972         // scroll to first matching element based on key pres..
8973         if (e.isSpecialKey()) {
8974             return false;
8975         }
8976         var k = String.fromCharCode(e.getKey()).toUpperCase();
8977         //Roo.log(k);
8978         var match  = false;
8979         var csel = this.view.getSelectedNodes();
8980         var cselitem = false;
8981         if (csel.length) {
8982             var ix = this.view.indexOf(csel[0]);
8983             cselitem  = this.store.getAt(ix);
8984             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8985                 cselitem = false;
8986             }
8987             
8988         }
8989         
8990         this.store.each(function(v) { 
8991             if (cselitem) {
8992                 // start at existing selection.
8993                 if (cselitem.id == v.id) {
8994                     cselitem = false;
8995                 }
8996                 return true;
8997             }
8998                 
8999             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9000                 match = this.store.indexOf(v);
9001                 return false;
9002             }
9003             return true;
9004         }, this);
9005         
9006         if (match === false) {
9007             return true; // no more action?
9008         }
9009         // scroll to?
9010         this.view.select(match);
9011         var sn = Roo.get(this.view.getSelectedNodes()[0])
9012         //sn.scrollIntoView(sn.dom.parentNode, false);
9013     },
9014     
9015     onViewScroll : function(e, t){
9016         
9017         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9018             return;
9019         }
9020         
9021         this.hasQuery = true;
9022         
9023         this.loading = this.list.select('.loading', true).first();
9024         
9025         if(this.loading === null){
9026             this.list.createChild({
9027                 tag: 'div',
9028                 cls: 'loading select2-more-results select2-active',
9029                 html: 'Loading more results...'
9030             })
9031             
9032             this.loading = this.list.select('.loading', true).first();
9033             
9034             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9035             
9036             this.loading.hide();
9037         }
9038         
9039         this.loading.show();
9040         
9041         var _combo = this;
9042         
9043         this.page++;
9044         this.loadNext = true;
9045         
9046         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9047         
9048         return;
9049     },
9050     
9051     addItem : function(o)
9052     {   
9053         var dv = ''; // display value
9054         
9055         if (this.displayField) {
9056             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9057         } else {
9058             // this is an error condition!!!
9059             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9060         }
9061         
9062         if(!dv.length){
9063             return;
9064         }
9065         
9066         var choice = this.choices.createChild({
9067             tag: 'li',
9068             cls: 'select2-search-choice',
9069             cn: [
9070                 {
9071                     tag: 'div',
9072                     html: dv
9073                 },
9074                 {
9075                     tag: 'a',
9076                     href: '#',
9077                     cls: 'select2-search-choice-close',
9078                     tabindex: '-1'
9079                 }
9080             ]
9081             
9082         }, this.searchField);
9083         
9084         var close = choice.select('a.select2-search-choice-close', true).first()
9085         
9086         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9087         
9088         this.item.push(o);
9089         this.lastData = o;
9090         
9091         this.syncValue();
9092         
9093         this.inputEl().dom.value = '';
9094         
9095     },
9096     
9097     onRemoveItem : function(e, _self, o)
9098     {
9099         Roo.log('remove item');
9100         var index = this.item.indexOf(o.data) * 1;
9101         
9102         if( index < 0){
9103             Roo.log('not this item?!');
9104             return;
9105         }
9106         
9107         this.item.splice(index, 1);
9108         o.item.remove();
9109         
9110         this.syncValue();
9111         
9112         this.fireEvent('remove', this);
9113         
9114     },
9115     
9116     syncValue : function()
9117     {
9118         if(!this.item.length){
9119             this.clearValue();
9120             return;
9121         }
9122             
9123         var value = [];
9124         var _this = this;
9125         Roo.each(this.item, function(i){
9126             if(_this.valueField){
9127                 value.push(i[_this.valueField]);
9128                 return;
9129             }
9130
9131             value.push(i);
9132         });
9133
9134         this.value = value.join(',');
9135
9136         if(this.hiddenField){
9137             this.hiddenField.dom.value = this.value;
9138         }
9139     },
9140     
9141     clearItem : function()
9142     {
9143         if(!this.multiple){
9144             return;
9145         }
9146         
9147         this.item = [];
9148         
9149         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9150            c.remove();
9151         });
9152         
9153         this.syncValue();
9154     }
9155     
9156     
9157
9158     /** 
9159     * @cfg {Boolean} grow 
9160     * @hide 
9161     */
9162     /** 
9163     * @cfg {Number} growMin 
9164     * @hide 
9165     */
9166     /** 
9167     * @cfg {Number} growMax 
9168     * @hide 
9169     */
9170     /**
9171      * @hide
9172      * @method autoSize
9173      */
9174 });
9175 /*
9176  * Based on:
9177  * Ext JS Library 1.1.1
9178  * Copyright(c) 2006-2007, Ext JS, LLC.
9179  *
9180  * Originally Released Under LGPL - original licence link has changed is not relivant.
9181  *
9182  * Fork - LGPL
9183  * <script type="text/javascript">
9184  */
9185
9186 /**
9187  * @class Roo.View
9188  * @extends Roo.util.Observable
9189  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9190  * This class also supports single and multi selection modes. <br>
9191  * Create a data model bound view:
9192  <pre><code>
9193  var store = new Roo.data.Store(...);
9194
9195  var view = new Roo.View({
9196     el : "my-element",
9197     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9198  
9199     singleSelect: true,
9200     selectedClass: "ydataview-selected",
9201     store: store
9202  });
9203
9204  // listen for node click?
9205  view.on("click", function(vw, index, node, e){
9206  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9207  });
9208
9209  // load XML data
9210  dataModel.load("foobar.xml");
9211  </code></pre>
9212  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9213  * <br><br>
9214  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9215  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9216  * 
9217  * Note: old style constructor is still suported (container, template, config)
9218  * 
9219  * @constructor
9220  * Create a new View
9221  * @param {Object} config The config object
9222  * 
9223  */
9224 Roo.View = function(config, depreciated_tpl, depreciated_config){
9225     
9226     if (typeof(depreciated_tpl) == 'undefined') {
9227         // new way.. - universal constructor.
9228         Roo.apply(this, config);
9229         this.el  = Roo.get(this.el);
9230     } else {
9231         // old format..
9232         this.el  = Roo.get(config);
9233         this.tpl = depreciated_tpl;
9234         Roo.apply(this, depreciated_config);
9235     }
9236     this.wrapEl  = this.el.wrap().wrap();
9237     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9238     
9239     
9240     if(typeof(this.tpl) == "string"){
9241         this.tpl = new Roo.Template(this.tpl);
9242     } else {
9243         // support xtype ctors..
9244         this.tpl = new Roo.factory(this.tpl, Roo);
9245     }
9246     
9247     
9248     this.tpl.compile();
9249    
9250   
9251     
9252      
9253     /** @private */
9254     this.addEvents({
9255         /**
9256          * @event beforeclick
9257          * Fires before a click is processed. Returns false to cancel the default action.
9258          * @param {Roo.View} this
9259          * @param {Number} index The index of the target node
9260          * @param {HTMLElement} node The target node
9261          * @param {Roo.EventObject} e The raw event object
9262          */
9263             "beforeclick" : true,
9264         /**
9265          * @event click
9266          * Fires when a template node is clicked.
9267          * @param {Roo.View} this
9268          * @param {Number} index The index of the target node
9269          * @param {HTMLElement} node The target node
9270          * @param {Roo.EventObject} e The raw event object
9271          */
9272             "click" : true,
9273         /**
9274          * @event dblclick
9275          * Fires when a template node is double clicked.
9276          * @param {Roo.View} this
9277          * @param {Number} index The index of the target node
9278          * @param {HTMLElement} node The target node
9279          * @param {Roo.EventObject} e The raw event object
9280          */
9281             "dblclick" : true,
9282         /**
9283          * @event contextmenu
9284          * Fires when a template node is right clicked.
9285          * @param {Roo.View} this
9286          * @param {Number} index The index of the target node
9287          * @param {HTMLElement} node The target node
9288          * @param {Roo.EventObject} e The raw event object
9289          */
9290             "contextmenu" : true,
9291         /**
9292          * @event selectionchange
9293          * Fires when the selected nodes change.
9294          * @param {Roo.View} this
9295          * @param {Array} selections Array of the selected nodes
9296          */
9297             "selectionchange" : true,
9298     
9299         /**
9300          * @event beforeselect
9301          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9302          * @param {Roo.View} this
9303          * @param {HTMLElement} node The node to be selected
9304          * @param {Array} selections Array of currently selected nodes
9305          */
9306             "beforeselect" : true,
9307         /**
9308          * @event preparedata
9309          * Fires on every row to render, to allow you to change the data.
9310          * @param {Roo.View} this
9311          * @param {Object} data to be rendered (change this)
9312          */
9313           "preparedata" : true
9314           
9315           
9316         });
9317
9318
9319
9320     this.el.on({
9321         "click": this.onClick,
9322         "dblclick": this.onDblClick,
9323         "contextmenu": this.onContextMenu,
9324         scope:this
9325     });
9326
9327     this.selections = [];
9328     this.nodes = [];
9329     this.cmp = new Roo.CompositeElementLite([]);
9330     if(this.store){
9331         this.store = Roo.factory(this.store, Roo.data);
9332         this.setStore(this.store, true);
9333     }
9334     
9335     if ( this.footer && this.footer.xtype) {
9336            
9337          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9338         
9339         this.footer.dataSource = this.store
9340         this.footer.container = fctr;
9341         this.footer = Roo.factory(this.footer, Roo);
9342         fctr.insertFirst(this.el);
9343         
9344         // this is a bit insane - as the paging toolbar seems to detach the el..
9345 //        dom.parentNode.parentNode.parentNode
9346          // they get detached?
9347     }
9348     
9349     
9350     Roo.View.superclass.constructor.call(this);
9351     
9352     
9353 };
9354
9355 Roo.extend(Roo.View, Roo.util.Observable, {
9356     
9357      /**
9358      * @cfg {Roo.data.Store} store Data store to load data from.
9359      */
9360     store : false,
9361     
9362     /**
9363      * @cfg {String|Roo.Element} el The container element.
9364      */
9365     el : '',
9366     
9367     /**
9368      * @cfg {String|Roo.Template} tpl The template used by this View 
9369      */
9370     tpl : false,
9371     /**
9372      * @cfg {String} dataName the named area of the template to use as the data area
9373      *                          Works with domtemplates roo-name="name"
9374      */
9375     dataName: false,
9376     /**
9377      * @cfg {String} selectedClass The css class to add to selected nodes
9378      */
9379     selectedClass : "x-view-selected",
9380      /**
9381      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9382      */
9383     emptyText : "",
9384     
9385     /**
9386      * @cfg {String} text to display on mask (default Loading)
9387      */
9388     mask : false,
9389     /**
9390      * @cfg {Boolean} multiSelect Allow multiple selection
9391      */
9392     multiSelect : false,
9393     /**
9394      * @cfg {Boolean} singleSelect Allow single selection
9395      */
9396     singleSelect:  false,
9397     
9398     /**
9399      * @cfg {Boolean} toggleSelect - selecting 
9400      */
9401     toggleSelect : false,
9402     
9403     /**
9404      * Returns the element this view is bound to.
9405      * @return {Roo.Element}
9406      */
9407     getEl : function(){
9408         return this.wrapEl;
9409     },
9410     
9411     
9412
9413     /**
9414      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9415      */
9416     refresh : function(){
9417         Roo.log('refresh');
9418         var t = this.tpl;
9419         
9420         // if we are using something like 'domtemplate', then
9421         // the what gets used is:
9422         // t.applySubtemplate(NAME, data, wrapping data..)
9423         // the outer template then get' applied with
9424         //     the store 'extra data'
9425         // and the body get's added to the
9426         //      roo-name="data" node?
9427         //      <span class='roo-tpl-{name}'></span> ?????
9428         
9429         
9430         
9431         this.clearSelections();
9432         this.el.update("");
9433         var html = [];
9434         var records = this.store.getRange();
9435         if(records.length < 1) {
9436             
9437             // is this valid??  = should it render a template??
9438             
9439             this.el.update(this.emptyText);
9440             return;
9441         }
9442         var el = this.el;
9443         if (this.dataName) {
9444             this.el.update(t.apply(this.store.meta)); //????
9445             el = this.el.child('.roo-tpl-' + this.dataName);
9446         }
9447         
9448         for(var i = 0, len = records.length; i < len; i++){
9449             var data = this.prepareData(records[i].data, i, records[i]);
9450             this.fireEvent("preparedata", this, data, i, records[i]);
9451             html[html.length] = Roo.util.Format.trim(
9452                 this.dataName ?
9453                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9454                     t.apply(data)
9455             );
9456         }
9457         
9458         
9459         
9460         el.update(html.join(""));
9461         this.nodes = el.dom.childNodes;
9462         this.updateIndexes(0);
9463     },
9464     
9465
9466     /**
9467      * Function to override to reformat the data that is sent to
9468      * the template for each node.
9469      * DEPRICATED - use the preparedata event handler.
9470      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9471      * a JSON object for an UpdateManager bound view).
9472      */
9473     prepareData : function(data, index, record)
9474     {
9475         this.fireEvent("preparedata", this, data, index, record);
9476         return data;
9477     },
9478
9479     onUpdate : function(ds, record){
9480          Roo.log('on update');   
9481         this.clearSelections();
9482         var index = this.store.indexOf(record);
9483         var n = this.nodes[index];
9484         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9485         n.parentNode.removeChild(n);
9486         this.updateIndexes(index, index);
9487     },
9488
9489     
9490     
9491 // --------- FIXME     
9492     onAdd : function(ds, records, index)
9493     {
9494         Roo.log(['on Add', ds, records, index] );        
9495         this.clearSelections();
9496         if(this.nodes.length == 0){
9497             this.refresh();
9498             return;
9499         }
9500         var n = this.nodes[index];
9501         for(var i = 0, len = records.length; i < len; i++){
9502             var d = this.prepareData(records[i].data, i, records[i]);
9503             if(n){
9504                 this.tpl.insertBefore(n, d);
9505             }else{
9506                 
9507                 this.tpl.append(this.el, d);
9508             }
9509         }
9510         this.updateIndexes(index);
9511     },
9512
9513     onRemove : function(ds, record, index){
9514         Roo.log('onRemove');
9515         this.clearSelections();
9516         var el = this.dataName  ?
9517             this.el.child('.roo-tpl-' + this.dataName) :
9518             this.el; 
9519         
9520         el.dom.removeChild(this.nodes[index]);
9521         this.updateIndexes(index);
9522     },
9523
9524     /**
9525      * Refresh an individual node.
9526      * @param {Number} index
9527      */
9528     refreshNode : function(index){
9529         this.onUpdate(this.store, this.store.getAt(index));
9530     },
9531
9532     updateIndexes : function(startIndex, endIndex){
9533         var ns = this.nodes;
9534         startIndex = startIndex || 0;
9535         endIndex = endIndex || ns.length - 1;
9536         for(var i = startIndex; i <= endIndex; i++){
9537             ns[i].nodeIndex = i;
9538         }
9539     },
9540
9541     /**
9542      * Changes the data store this view uses and refresh the view.
9543      * @param {Store} store
9544      */
9545     setStore : function(store, initial){
9546         if(!initial && this.store){
9547             this.store.un("datachanged", this.refresh);
9548             this.store.un("add", this.onAdd);
9549             this.store.un("remove", this.onRemove);
9550             this.store.un("update", this.onUpdate);
9551             this.store.un("clear", this.refresh);
9552             this.store.un("beforeload", this.onBeforeLoad);
9553             this.store.un("load", this.onLoad);
9554             this.store.un("loadexception", this.onLoad);
9555         }
9556         if(store){
9557           
9558             store.on("datachanged", this.refresh, this);
9559             store.on("add", this.onAdd, this);
9560             store.on("remove", this.onRemove, this);
9561             store.on("update", this.onUpdate, this);
9562             store.on("clear", this.refresh, this);
9563             store.on("beforeload", this.onBeforeLoad, this);
9564             store.on("load", this.onLoad, this);
9565             store.on("loadexception", this.onLoad, this);
9566         }
9567         
9568         if(store){
9569             this.refresh();
9570         }
9571     },
9572     /**
9573      * onbeforeLoad - masks the loading area.
9574      *
9575      */
9576     onBeforeLoad : function(store,opts)
9577     {
9578          Roo.log('onBeforeLoad');   
9579         if (!opts.add) {
9580             this.el.update("");
9581         }
9582         this.el.mask(this.mask ? this.mask : "Loading" ); 
9583     },
9584     onLoad : function ()
9585     {
9586         this.el.unmask();
9587     },
9588     
9589
9590     /**
9591      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9592      * @param {HTMLElement} node
9593      * @return {HTMLElement} The template node
9594      */
9595     findItemFromChild : function(node){
9596         var el = this.dataName  ?
9597             this.el.child('.roo-tpl-' + this.dataName,true) :
9598             this.el.dom; 
9599         
9600         if(!node || node.parentNode == el){
9601                     return node;
9602             }
9603             var p = node.parentNode;
9604             while(p && p != el){
9605             if(p.parentNode == el){
9606                 return p;
9607             }
9608             p = p.parentNode;
9609         }
9610             return null;
9611     },
9612
9613     /** @ignore */
9614     onClick : function(e){
9615         var item = this.findItemFromChild(e.getTarget());
9616         if(item){
9617             var index = this.indexOf(item);
9618             if(this.onItemClick(item, index, e) !== false){
9619                 this.fireEvent("click", this, index, item, e);
9620             }
9621         }else{
9622             this.clearSelections();
9623         }
9624     },
9625
9626     /** @ignore */
9627     onContextMenu : function(e){
9628         var item = this.findItemFromChild(e.getTarget());
9629         if(item){
9630             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9631         }
9632     },
9633
9634     /** @ignore */
9635     onDblClick : function(e){
9636         var item = this.findItemFromChild(e.getTarget());
9637         if(item){
9638             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9639         }
9640     },
9641
9642     onItemClick : function(item, index, e)
9643     {
9644         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9645             return false;
9646         }
9647         if (this.toggleSelect) {
9648             var m = this.isSelected(item) ? 'unselect' : 'select';
9649             Roo.log(m);
9650             var _t = this;
9651             _t[m](item, true, false);
9652             return true;
9653         }
9654         if(this.multiSelect || this.singleSelect){
9655             if(this.multiSelect && e.shiftKey && this.lastSelection){
9656                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9657             }else{
9658                 this.select(item, this.multiSelect && e.ctrlKey);
9659                 this.lastSelection = item;
9660             }
9661             e.preventDefault();
9662         }
9663         return true;
9664     },
9665
9666     /**
9667      * Get the number of selected nodes.
9668      * @return {Number}
9669      */
9670     getSelectionCount : function(){
9671         return this.selections.length;
9672     },
9673
9674     /**
9675      * Get the currently selected nodes.
9676      * @return {Array} An array of HTMLElements
9677      */
9678     getSelectedNodes : function(){
9679         return this.selections;
9680     },
9681
9682     /**
9683      * Get the indexes of the selected nodes.
9684      * @return {Array}
9685      */
9686     getSelectedIndexes : function(){
9687         var indexes = [], s = this.selections;
9688         for(var i = 0, len = s.length; i < len; i++){
9689             indexes.push(s[i].nodeIndex);
9690         }
9691         return indexes;
9692     },
9693
9694     /**
9695      * Clear all selections
9696      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9697      */
9698     clearSelections : function(suppressEvent){
9699         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9700             this.cmp.elements = this.selections;
9701             this.cmp.removeClass(this.selectedClass);
9702             this.selections = [];
9703             if(!suppressEvent){
9704                 this.fireEvent("selectionchange", this, this.selections);
9705             }
9706         }
9707     },
9708
9709     /**
9710      * Returns true if the passed node is selected
9711      * @param {HTMLElement/Number} node The node or node index
9712      * @return {Boolean}
9713      */
9714     isSelected : function(node){
9715         var s = this.selections;
9716         if(s.length < 1){
9717             return false;
9718         }
9719         node = this.getNode(node);
9720         return s.indexOf(node) !== -1;
9721     },
9722
9723     /**
9724      * Selects nodes.
9725      * @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
9726      * @param {Boolean} keepExisting (optional) true to keep existing selections
9727      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9728      */
9729     select : function(nodeInfo, keepExisting, suppressEvent){
9730         if(nodeInfo instanceof Array){
9731             if(!keepExisting){
9732                 this.clearSelections(true);
9733             }
9734             for(var i = 0, len = nodeInfo.length; i < len; i++){
9735                 this.select(nodeInfo[i], true, true);
9736             }
9737             return;
9738         } 
9739         var node = this.getNode(nodeInfo);
9740         if(!node || this.isSelected(node)){
9741             return; // already selected.
9742         }
9743         if(!keepExisting){
9744             this.clearSelections(true);
9745         }
9746         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9747             Roo.fly(node).addClass(this.selectedClass);
9748             this.selections.push(node);
9749             if(!suppressEvent){
9750                 this.fireEvent("selectionchange", this, this.selections);
9751             }
9752         }
9753         
9754         
9755     },
9756       /**
9757      * Unselects nodes.
9758      * @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
9759      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9760      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9761      */
9762     unselect : function(nodeInfo, keepExisting, suppressEvent)
9763     {
9764         if(nodeInfo instanceof Array){
9765             Roo.each(this.selections, function(s) {
9766                 this.unselect(s, nodeInfo);
9767             }, this);
9768             return;
9769         }
9770         var node = this.getNode(nodeInfo);
9771         if(!node || !this.isSelected(node)){
9772             Roo.log("not selected");
9773             return; // not selected.
9774         }
9775         // fireevent???
9776         var ns = [];
9777         Roo.each(this.selections, function(s) {
9778             if (s == node ) {
9779                 Roo.fly(node).removeClass(this.selectedClass);
9780
9781                 return;
9782             }
9783             ns.push(s);
9784         },this);
9785         
9786         this.selections= ns;
9787         this.fireEvent("selectionchange", this, this.selections);
9788     },
9789
9790     /**
9791      * Gets a template node.
9792      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9793      * @return {HTMLElement} The node or null if it wasn't found
9794      */
9795     getNode : function(nodeInfo){
9796         if(typeof nodeInfo == "string"){
9797             return document.getElementById(nodeInfo);
9798         }else if(typeof nodeInfo == "number"){
9799             return this.nodes[nodeInfo];
9800         }
9801         return nodeInfo;
9802     },
9803
9804     /**
9805      * Gets a range template nodes.
9806      * @param {Number} startIndex
9807      * @param {Number} endIndex
9808      * @return {Array} An array of nodes
9809      */
9810     getNodes : function(start, end){
9811         var ns = this.nodes;
9812         start = start || 0;
9813         end = typeof end == "undefined" ? ns.length - 1 : end;
9814         var nodes = [];
9815         if(start <= end){
9816             for(var i = start; i <= end; i++){
9817                 nodes.push(ns[i]);
9818             }
9819         } else{
9820             for(var i = start; i >= end; i--){
9821                 nodes.push(ns[i]);
9822             }
9823         }
9824         return nodes;
9825     },
9826
9827     /**
9828      * Finds the index of the passed node
9829      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9830      * @return {Number} The index of the node or -1
9831      */
9832     indexOf : function(node){
9833         node = this.getNode(node);
9834         if(typeof node.nodeIndex == "number"){
9835             return node.nodeIndex;
9836         }
9837         var ns = this.nodes;
9838         for(var i = 0, len = ns.length; i < len; i++){
9839             if(ns[i] == node){
9840                 return i;
9841             }
9842         }
9843         return -1;
9844     }
9845 });
9846 /*
9847  * - LGPL
9848  *
9849  * based on jquery fullcalendar
9850  * 
9851  */
9852
9853 Roo.bootstrap = Roo.bootstrap || {};
9854 /**
9855  * @class Roo.bootstrap.Calendar
9856  * @extends Roo.bootstrap.Component
9857  * Bootstrap Calendar class
9858  * @cfg {Boolean} loadMask (true|false) default false
9859     
9860  * @constructor
9861  * Create a new Container
9862  * @param {Object} config The config object
9863  */
9864
9865
9866
9867 Roo.bootstrap.Calendar = function(config){
9868     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9869      this.addEvents({
9870         /**
9871              * @event select
9872              * Fires when a date is selected
9873              * @param {DatePicker} this
9874              * @param {Date} date The selected date
9875              */
9876         'select': true,
9877         /**
9878              * @event monthchange
9879              * Fires when the displayed month changes 
9880              * @param {DatePicker} this
9881              * @param {Date} date The selected month
9882              */
9883         'monthchange': true,
9884         /**
9885              * @event evententer
9886              * Fires when mouse over an event
9887              * @param {Calendar} this
9888              * @param {event} Event
9889              */
9890         'evententer': true,
9891         /**
9892              * @event eventleave
9893              * Fires when the mouse leaves an
9894              * @param {Calendar} this
9895              * @param {event}
9896              */
9897         'eventleave': true,
9898         /**
9899              * @event eventclick
9900              * Fires when the mouse click an
9901              * @param {Calendar} this
9902              * @param {event}
9903              */
9904         'eventclick': true
9905         
9906     });
9907
9908 };
9909
9910 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9911     
9912      /**
9913      * @cfg {Number} startDay
9914      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9915      */
9916     startDay : 0,
9917     
9918     loadMask : false,
9919       
9920     getAutoCreate : function(){
9921         
9922         
9923         var fc_button = function(name, corner, style, content ) {
9924             return Roo.apply({},{
9925                 tag : 'span',
9926                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9927                          (corner.length ?
9928                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9929                             ''
9930                         ),
9931                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9932                 unselectable: 'on'
9933             });
9934         };
9935         
9936         var header = {
9937             tag : 'table',
9938             cls : 'fc-header',
9939             style : 'width:100%',
9940             cn : [
9941                 {
9942                     tag: 'tr',
9943                     cn : [
9944                         {
9945                             tag : 'td',
9946                             cls : 'fc-header-left',
9947                             cn : [
9948                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9949                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9950                                 { tag: 'span', cls: 'fc-header-space' },
9951                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9952                                 
9953                                 
9954                             ]
9955                         },
9956                         
9957                         {
9958                             tag : 'td',
9959                             cls : 'fc-header-center',
9960                             cn : [
9961                                 {
9962                                     tag: 'span',
9963                                     cls: 'fc-header-title',
9964                                     cn : {
9965                                         tag: 'H2',
9966                                         html : 'month / year'
9967                                     }
9968                                 }
9969                                 
9970                             ]
9971                         },
9972                         {
9973                             tag : 'td',
9974                             cls : 'fc-header-right',
9975                             cn : [
9976                           /*      fc_button('month', 'left', '', 'month' ),
9977                                 fc_button('week', '', '', 'week' ),
9978                                 fc_button('day', 'right', '', 'day' )
9979                             */    
9980                                 
9981                             ]
9982                         }
9983                         
9984                     ]
9985                 }
9986             ]
9987         };
9988         
9989        
9990         var cal_heads = function() {
9991             var ret = [];
9992             // fixme - handle this.
9993             
9994             for (var i =0; i < Date.dayNames.length; i++) {
9995                 var d = Date.dayNames[i];
9996                 ret.push({
9997                     tag: 'th',
9998                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9999                     html : d.substring(0,3)
10000                 });
10001                 
10002             }
10003             ret[0].cls += ' fc-first';
10004             ret[6].cls += ' fc-last';
10005             return ret;
10006         };
10007         var cal_cell = function(n) {
10008             return  {
10009                 tag: 'td',
10010                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10011                 cn : [
10012                     {
10013                         cn : [
10014                             {
10015                                 cls: 'fc-day-number',
10016                                 html: 'D'
10017                             },
10018                             {
10019                                 cls: 'fc-day-content',
10020                              
10021                                 cn : [
10022                                      {
10023                                         style: 'position: relative;' // height: 17px;
10024                                     }
10025                                 ]
10026                             }
10027                             
10028                             
10029                         ]
10030                     }
10031                 ]
10032                 
10033             }
10034         };
10035         var cal_rows = function() {
10036             
10037             var ret = []
10038             for (var r = 0; r < 6; r++) {
10039                 var row= {
10040                     tag : 'tr',
10041                     cls : 'fc-week',
10042                     cn : []
10043                 };
10044                 
10045                 for (var i =0; i < Date.dayNames.length; i++) {
10046                     var d = Date.dayNames[i];
10047                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10048
10049                 }
10050                 row.cn[0].cls+=' fc-first';
10051                 row.cn[0].cn[0].style = 'min-height:90px';
10052                 row.cn[6].cls+=' fc-last';
10053                 ret.push(row);
10054                 
10055             }
10056             ret[0].cls += ' fc-first';
10057             ret[4].cls += ' fc-prev-last';
10058             ret[5].cls += ' fc-last';
10059             return ret;
10060             
10061         };
10062         
10063         var cal_table = {
10064             tag: 'table',
10065             cls: 'fc-border-separate',
10066             style : 'width:100%',
10067             cellspacing  : 0,
10068             cn : [
10069                 { 
10070                     tag: 'thead',
10071                     cn : [
10072                         { 
10073                             tag: 'tr',
10074                             cls : 'fc-first fc-last',
10075                             cn : cal_heads()
10076                         }
10077                     ]
10078                 },
10079                 { 
10080                     tag: 'tbody',
10081                     cn : cal_rows()
10082                 }
10083                   
10084             ]
10085         };
10086          
10087          var cfg = {
10088             cls : 'fc fc-ltr',
10089             cn : [
10090                 header,
10091                 {
10092                     cls : 'fc-content',
10093                     style : "position: relative;",
10094                     cn : [
10095                         {
10096                             cls : 'fc-view fc-view-month fc-grid',
10097                             style : 'position: relative',
10098                             unselectable : 'on',
10099                             cn : [
10100                                 {
10101                                     cls : 'fc-event-container',
10102                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10103                                 },
10104                                 cal_table
10105                             ]
10106                         }
10107                     ]
10108     
10109                 }
10110            ] 
10111             
10112         };
10113         
10114          
10115         
10116         return cfg;
10117     },
10118     
10119     
10120     initEvents : function()
10121     {
10122         if(!this.store){
10123             throw "can not find store for calendar";
10124         }
10125         
10126         var mark = {
10127             tag: "div",
10128             cls:"x-dlg-mask",
10129             style: "text-align:center",
10130             cn: [
10131                 {
10132                     tag: "div",
10133                     style: "background-color:white;width:50%;margin:250 auto",
10134                     cn: [
10135                         {
10136                             tag: "img",
10137                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10138                         },
10139                         {
10140                             tag: "span",
10141                             html: "Loading"
10142                         }
10143                         
10144                     ]
10145                 }
10146             ]
10147         }
10148         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10149         
10150         var size = this.el.select('.fc-content', true).first().getSize();
10151         this.maskEl.setSize(size.width, size.height);
10152         this.maskEl.enableDisplayMode("block");
10153         if(!this.loadMask){
10154             this.maskEl.hide();
10155         }
10156         
10157         this.store = Roo.factory(this.store, Roo.data);
10158         this.store.on('load', this.onLoad, this);
10159         this.store.on('beforeload', this.onBeforeLoad, this);
10160         
10161         this.resize();
10162         
10163         this.cells = this.el.select('.fc-day',true);
10164         //Roo.log(this.cells);
10165         this.textNodes = this.el.query('.fc-day-number');
10166         this.cells.addClassOnOver('fc-state-hover');
10167         
10168         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10169         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10170         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10171         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10172         
10173         this.on('monthchange', this.onMonthChange, this);
10174         
10175         this.update(new Date().clearTime());
10176     },
10177     
10178     resize : function() {
10179         var sz  = this.el.getSize();
10180         
10181         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10182         this.el.select('.fc-day-content div',true).setHeight(34);
10183     },
10184     
10185     
10186     // private
10187     showPrevMonth : function(e){
10188         this.update(this.activeDate.add("mo", -1));
10189     },
10190     showToday : function(e){
10191         this.update(new Date().clearTime());
10192     },
10193     // private
10194     showNextMonth : function(e){
10195         this.update(this.activeDate.add("mo", 1));
10196     },
10197
10198     // private
10199     showPrevYear : function(){
10200         this.update(this.activeDate.add("y", -1));
10201     },
10202
10203     // private
10204     showNextYear : function(){
10205         this.update(this.activeDate.add("y", 1));
10206     },
10207
10208     
10209    // private
10210     update : function(date)
10211     {
10212         var vd = this.activeDate;
10213         this.activeDate = date;
10214 //        if(vd && this.el){
10215 //            var t = date.getTime();
10216 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10217 //                Roo.log('using add remove');
10218 //                
10219 //                this.fireEvent('monthchange', this, date);
10220 //                
10221 //                this.cells.removeClass("fc-state-highlight");
10222 //                this.cells.each(function(c){
10223 //                   if(c.dateValue == t){
10224 //                       c.addClass("fc-state-highlight");
10225 //                       setTimeout(function(){
10226 //                            try{c.dom.firstChild.focus();}catch(e){}
10227 //                       }, 50);
10228 //                       return false;
10229 //                   }
10230 //                   return true;
10231 //                });
10232 //                return;
10233 //            }
10234 //        }
10235         
10236         var days = date.getDaysInMonth();
10237         
10238         var firstOfMonth = date.getFirstDateOfMonth();
10239         var startingPos = firstOfMonth.getDay()-this.startDay;
10240         
10241         if(startingPos < this.startDay){
10242             startingPos += 7;
10243         }
10244         
10245         var pm = date.add(Date.MONTH, -1);
10246         var prevStart = pm.getDaysInMonth()-startingPos;
10247 //        
10248         this.cells = this.el.select('.fc-day',true);
10249         this.textNodes = this.el.query('.fc-day-number');
10250         this.cells.addClassOnOver('fc-state-hover');
10251         
10252         var cells = this.cells.elements;
10253         var textEls = this.textNodes;
10254         
10255         Roo.each(cells, function(cell){
10256             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10257         });
10258         
10259         days += startingPos;
10260
10261         // convert everything to numbers so it's fast
10262         var day = 86400000;
10263         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10264         //Roo.log(d);
10265         //Roo.log(pm);
10266         //Roo.log(prevStart);
10267         
10268         var today = new Date().clearTime().getTime();
10269         var sel = date.clearTime().getTime();
10270         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10271         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10272         var ddMatch = this.disabledDatesRE;
10273         var ddText = this.disabledDatesText;
10274         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10275         var ddaysText = this.disabledDaysText;
10276         var format = this.format;
10277         
10278         var setCellClass = function(cal, cell){
10279             
10280             //Roo.log('set Cell Class');
10281             cell.title = "";
10282             var t = d.getTime();
10283             
10284             //Roo.log(d);
10285             
10286             cell.dateValue = t;
10287             if(t == today){
10288                 cell.className += " fc-today";
10289                 cell.className += " fc-state-highlight";
10290                 cell.title = cal.todayText;
10291             }
10292             if(t == sel){
10293                 // disable highlight in other month..
10294                 //cell.className += " fc-state-highlight";
10295                 
10296             }
10297             // disabling
10298             if(t < min) {
10299                 cell.className = " fc-state-disabled";
10300                 cell.title = cal.minText;
10301                 return;
10302             }
10303             if(t > max) {
10304                 cell.className = " fc-state-disabled";
10305                 cell.title = cal.maxText;
10306                 return;
10307             }
10308             if(ddays){
10309                 if(ddays.indexOf(d.getDay()) != -1){
10310                     cell.title = ddaysText;
10311                     cell.className = " fc-state-disabled";
10312                 }
10313             }
10314             if(ddMatch && format){
10315                 var fvalue = d.dateFormat(format);
10316                 if(ddMatch.test(fvalue)){
10317                     cell.title = ddText.replace("%0", fvalue);
10318                     cell.className = " fc-state-disabled";
10319                 }
10320             }
10321             
10322             if (!cell.initialClassName) {
10323                 cell.initialClassName = cell.dom.className;
10324             }
10325             
10326             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10327         };
10328
10329         var i = 0;
10330         
10331         for(; i < startingPos; i++) {
10332             textEls[i].innerHTML = (++prevStart);
10333             d.setDate(d.getDate()+1);
10334             
10335             cells[i].className = "fc-past fc-other-month";
10336             setCellClass(this, cells[i]);
10337         }
10338         
10339         var intDay = 0;
10340         
10341         for(; i < days; i++){
10342             intDay = i - startingPos + 1;
10343             textEls[i].innerHTML = (intDay);
10344             d.setDate(d.getDate()+1);
10345             
10346             cells[i].className = ''; // "x-date-active";
10347             setCellClass(this, cells[i]);
10348         }
10349         var extraDays = 0;
10350         
10351         for(; i < 42; i++) {
10352             textEls[i].innerHTML = (++extraDays);
10353             d.setDate(d.getDate()+1);
10354             
10355             cells[i].className = "fc-future fc-other-month";
10356             setCellClass(this, cells[i]);
10357         }
10358         
10359         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10360         
10361         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10362         
10363         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10364         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10365         
10366         if(totalRows != 6){
10367             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10368             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10369         }
10370         
10371         this.fireEvent('monthchange', this, date);
10372         
10373         
10374         /*
10375         if(!this.internalRender){
10376             var main = this.el.dom.firstChild;
10377             var w = main.offsetWidth;
10378             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10379             Roo.fly(main).setWidth(w);
10380             this.internalRender = true;
10381             // opera does not respect the auto grow header center column
10382             // then, after it gets a width opera refuses to recalculate
10383             // without a second pass
10384             if(Roo.isOpera && !this.secondPass){
10385                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10386                 this.secondPass = true;
10387                 this.update.defer(10, this, [date]);
10388             }
10389         }
10390         */
10391         
10392     },
10393     
10394     findCell : function(dt) {
10395         dt = dt.clearTime().getTime();
10396         var ret = false;
10397         this.cells.each(function(c){
10398             //Roo.log("check " +c.dateValue + '?=' + dt);
10399             if(c.dateValue == dt){
10400                 ret = c;
10401                 return false;
10402             }
10403             return true;
10404         });
10405         
10406         return ret;
10407     },
10408     
10409     findCells : function(ev) {
10410         var s = ev.start.clone().clearTime().getTime();
10411        // Roo.log(s);
10412         var e= ev.end.clone().clearTime().getTime();
10413        // Roo.log(e);
10414         var ret = [];
10415         this.cells.each(function(c){
10416              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10417             
10418             if(c.dateValue > e){
10419                 return ;
10420             }
10421             if(c.dateValue < s){
10422                 return ;
10423             }
10424             ret.push(c);
10425         });
10426         
10427         return ret;    
10428     },
10429     
10430     findBestRow: function(cells)
10431     {
10432         var ret = 0;
10433         
10434         for (var i =0 ; i < cells.length;i++) {
10435             ret  = Math.max(cells[i].rows || 0,ret);
10436         }
10437         return ret;
10438         
10439     },
10440     
10441     
10442     addItem : function(ev)
10443     {
10444         // look for vertical location slot in
10445         var cells = this.findCells(ev);
10446         
10447         ev.row = this.findBestRow(cells);
10448         
10449         // work out the location.
10450         
10451         var crow = false;
10452         var rows = [];
10453         for(var i =0; i < cells.length; i++) {
10454             if (!crow) {
10455                 crow = {
10456                     start : cells[i],
10457                     end :  cells[i]
10458                 };
10459                 continue;
10460             }
10461             if (crow.start.getY() == cells[i].getY()) {
10462                 // on same row.
10463                 crow.end = cells[i];
10464                 continue;
10465             }
10466             // different row.
10467             rows.push(crow);
10468             crow = {
10469                 start: cells[i],
10470                 end : cells[i]
10471             };
10472             
10473         }
10474         
10475         rows.push(crow);
10476         ev.els = [];
10477         ev.rows = rows;
10478         ev.cells = cells;
10479         for (var i = 0; i < cells.length;i++) {
10480             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10481             
10482         }
10483         
10484         this.calevents.push(ev);
10485     },
10486     
10487     clearEvents: function() {
10488         
10489         if(!this.calevents){
10490             return;
10491         }
10492         
10493         Roo.each(this.cells.elements, function(c){
10494             c.rows = 0;
10495         });
10496         
10497         Roo.each(this.calevents, function(e) {
10498             Roo.each(e.els, function(el) {
10499                 el.un('mouseenter' ,this.onEventEnter, this);
10500                 el.un('mouseleave' ,this.onEventLeave, this);
10501                 el.remove();
10502             },this);
10503         },this);
10504         
10505     },
10506     
10507     renderEvents: function()
10508     {   
10509         // first make sure there is enough space..
10510         
10511         this.cells.each(function(c) {
10512         
10513             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10514         });
10515         
10516         for (var e = 0; e < this.calevents.length; e++) {
10517             var ev = this.calevents[e];
10518             var cells = ev.cells;
10519             var rows = ev.rows;
10520             
10521             for(var i =0; i < rows.length; i++) {
10522                 
10523                  
10524                 // how many rows should it span..
10525                 
10526                 var  cfg = {
10527                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10528                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10529                     
10530                     unselectable : "on",
10531                     cn : [
10532                         {
10533                             cls: 'fc-event-inner',
10534                             cn : [
10535 //                                {
10536 //                                  tag:'span',
10537 //                                  cls: 'fc-event-time',
10538 //                                  html : cells.length > 1 ? '' : ev.time
10539 //                                },
10540                                 {
10541                                   tag:'span',
10542                                   cls: 'fc-event-title',
10543                                   html : String.format('{0}', ev.title)
10544                                 }
10545                                 
10546                                 
10547                             ]
10548                         },
10549                         {
10550                             cls: 'ui-resizable-handle ui-resizable-e',
10551                             html : '&nbsp;&nbsp;&nbsp'
10552                         }
10553                         
10554                     ]
10555                 };
10556                 if (i == 0) {
10557                     cfg.cls += ' fc-event-start';
10558                 }
10559                 if ((i+1) == rows.length) {
10560                     cfg.cls += ' fc-event-end';
10561                 }
10562                 
10563                 var ctr = this.el.select('.fc-event-container',true).first();
10564                 var cg = ctr.createChild(cfg);
10565                 
10566                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10567                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10568                 cg.on('click', this.onEventClick, this, ev);
10569                 
10570                 ev.els.push(cg);
10571                 
10572                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10573                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10574                 //Roo.log(cg);
10575                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10576                 cg.setWidth(ebox.right - sbox.x -2);
10577             }
10578             
10579             
10580         }
10581         
10582     },
10583     
10584     onEventEnter: function (e, el,event,d) {
10585         this.fireEvent('evententer', this, el, event);
10586     },
10587     
10588     onEventLeave: function (e, el,event,d) {
10589         this.fireEvent('eventleave', this, el, event);
10590     },
10591     
10592     onEventClick: function (e, el,event,d) {
10593         this.fireEvent('eventclick', this, el, event);
10594     },
10595     
10596     onMonthChange: function () {
10597         this.store.load();
10598     },
10599     
10600     onLoad: function () 
10601     {   
10602         this.calevents = [];
10603         var cal = this;
10604         
10605         if(this.store.getCount() > 0){
10606             this.store.data.each(function(d){
10607                cal.addItem({
10608                     id : d.data.id,
10609                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10610                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10611                     time : d.data.start_time,
10612                     title : d.data.title,
10613                     description : d.data.description,
10614                     venue : d.data.venue
10615                 });
10616             });
10617         }
10618         
10619         this.renderEvents();
10620         
10621         if(this.loadMask){
10622             this.maskEl.hide();
10623         }
10624     },
10625     
10626     onBeforeLoad: function()
10627     {
10628         this.clearEvents();
10629         
10630         if(this.loadMask){
10631             this.maskEl.show();
10632         }
10633     }
10634 });
10635
10636  
10637  /*
10638  * - LGPL
10639  *
10640  * element
10641  * 
10642  */
10643
10644 /**
10645  * @class Roo.bootstrap.Popover
10646  * @extends Roo.bootstrap.Component
10647  * Bootstrap Popover class
10648  * @cfg {String} html contents of the popover   (or false to use children..)
10649  * @cfg {String} title of popover (or false to hide)
10650  * @cfg {String} placement how it is placed
10651  * @cfg {String} trigger click || hover (or false to trigger manually)
10652  * @cfg {String} over what (parent or false to trigger manually.)
10653  * 
10654  * @constructor
10655  * Create a new Popover
10656  * @param {Object} config The config object
10657  */
10658
10659 Roo.bootstrap.Popover = function(config){
10660     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10661 };
10662
10663 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10664     
10665     title: 'Fill in a title',
10666     html: false,
10667     
10668     placement : 'right',
10669     trigger : 'hover', // hover
10670     
10671     over: 'parent',
10672     
10673     can_build_overlaid : false,
10674     
10675     getChildContainer : function()
10676     {
10677         return this.el.select('.popover-content',true).first();
10678     },
10679     
10680     getAutoCreate : function(){
10681          Roo.log('make popover?');
10682         var cfg = {
10683            cls : 'popover roo-dynamic',
10684            style: 'display:block',
10685            cn : [
10686                 {
10687                     cls : 'arrow'
10688                 },
10689                 {
10690                     cls : 'popover-inner',
10691                     cn : [
10692                         {
10693                             tag: 'h3',
10694                             cls: 'popover-title',
10695                             html : this.title
10696                         },
10697                         {
10698                             cls : 'popover-content',
10699                             html : this.html
10700                         }
10701                     ]
10702                     
10703                 }
10704            ]
10705         };
10706         
10707         return cfg;
10708     },
10709     setTitle: function(str)
10710     {
10711         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10712     },
10713     setContent: function(str)
10714     {
10715         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10716     },
10717     // as it get's added to the bottom of the page.
10718     onRender : function(ct, position)
10719     {
10720         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10721         if(!this.el){
10722             var cfg = Roo.apply({},  this.getAutoCreate());
10723             cfg.id = Roo.id();
10724             
10725             if (this.cls) {
10726                 cfg.cls += ' ' + this.cls;
10727             }
10728             if (this.style) {
10729                 cfg.style = this.style;
10730             }
10731             Roo.log("adding to ")
10732             this.el = Roo.get(document.body).createChild(cfg, position);
10733             Roo.log(this.el);
10734         }
10735         this.initEvents();
10736     },
10737     
10738     initEvents : function()
10739     {
10740         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10741         this.el.enableDisplayMode('block');
10742         this.el.hide();
10743         if (this.over === false) {
10744             return; 
10745         }
10746         if (this.triggers === false) {
10747             return;
10748         }
10749         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10750         var triggers = this.trigger ? this.trigger.split(' ') : [];
10751         Roo.each(triggers, function(trigger) {
10752         
10753             if (trigger == 'click') {
10754                 on_el.on('click', this.toggle, this);
10755             } else if (trigger != 'manual') {
10756                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10757                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10758       
10759                 on_el.on(eventIn  ,this.enter, this);
10760                 on_el.on(eventOut, this.leave, this);
10761             }
10762         }, this);
10763         
10764     },
10765     
10766     
10767     // private
10768     timeout : null,
10769     hoverState : null,
10770     
10771     toggle : function () {
10772         this.hoverState == 'in' ? this.leave() : this.enter();
10773     },
10774     
10775     enter : function () {
10776        
10777     
10778         clearTimeout(this.timeout);
10779     
10780         this.hoverState = 'in'
10781     
10782         if (!this.delay || !this.delay.show) {
10783             this.show();
10784             return 
10785         }
10786         var _t = this;
10787         this.timeout = setTimeout(function () {
10788             if (_t.hoverState == 'in') {
10789                 _t.show();
10790             }
10791         }, this.delay.show)
10792     },
10793     leave : function() {
10794         clearTimeout(this.timeout);
10795     
10796         this.hoverState = 'out'
10797     
10798         if (!this.delay || !this.delay.hide) {
10799             this.hide();
10800             return 
10801         }
10802         var _t = this;
10803         this.timeout = setTimeout(function () {
10804             if (_t.hoverState == 'out') {
10805                 _t.hide();
10806             }
10807         }, this.delay.hide)
10808     },
10809     
10810     show : function (on_el)
10811     {
10812         if (!on_el) {
10813             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10814         }
10815         // set content.
10816         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10817         if (this.html !== false) {
10818             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10819         }
10820         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10821         if (!this.title.length) {
10822             this.el.select('.popover-title',true).hide();
10823         }
10824         
10825         var placement = typeof this.placement == 'function' ?
10826             this.placement.call(this, this.el, on_el) :
10827             this.placement;
10828             
10829         var autoToken = /\s?auto?\s?/i;
10830         var autoPlace = autoToken.test(placement);
10831         if (autoPlace) {
10832             placement = placement.replace(autoToken, '') || 'top';
10833         }
10834         
10835         //this.el.detach()
10836         //this.el.setXY([0,0]);
10837         this.el.show();
10838         this.el.dom.style.display='block';
10839         this.el.addClass(placement);
10840         
10841         //this.el.appendTo(on_el);
10842         
10843         var p = this.getPosition();
10844         var box = this.el.getBox();
10845         
10846         if (autoPlace) {
10847             // fixme..
10848         }
10849         var align = Roo.bootstrap.Popover.alignment[placement]
10850         this.el.alignTo(on_el, align[0],align[1]);
10851         //var arrow = this.el.select('.arrow',true).first();
10852         //arrow.set(align[2], 
10853         
10854         this.el.addClass('in');
10855         this.hoverState = null;
10856         
10857         if (this.el.hasClass('fade')) {
10858             // fade it?
10859         }
10860         
10861     },
10862     hide : function()
10863     {
10864         this.el.setXY([0,0]);
10865         this.el.removeClass('in');
10866         this.el.hide();
10867         
10868     }
10869     
10870 });
10871
10872 Roo.bootstrap.Popover.alignment = {
10873     'left' : ['r-l', [-10,0], 'right'],
10874     'right' : ['l-r', [10,0], 'left'],
10875     'bottom' : ['t-b', [0,10], 'top'],
10876     'top' : [ 'b-t', [0,-10], 'bottom']
10877 };
10878
10879  /*
10880  * - LGPL
10881  *
10882  * Progress
10883  * 
10884  */
10885
10886 /**
10887  * @class Roo.bootstrap.Progress
10888  * @extends Roo.bootstrap.Component
10889  * Bootstrap Progress class
10890  * @cfg {Boolean} striped striped of the progress bar
10891  * @cfg {Boolean} active animated of the progress bar
10892  * 
10893  * 
10894  * @constructor
10895  * Create a new Progress
10896  * @param {Object} config The config object
10897  */
10898
10899 Roo.bootstrap.Progress = function(config){
10900     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10901 };
10902
10903 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10904     
10905     striped : false,
10906     active: false,
10907     
10908     getAutoCreate : function(){
10909         var cfg = {
10910             tag: 'div',
10911             cls: 'progress'
10912         };
10913         
10914         
10915         if(this.striped){
10916             cfg.cls += ' progress-striped';
10917         }
10918       
10919         if(this.active){
10920             cfg.cls += ' active';
10921         }
10922         
10923         
10924         return cfg;
10925     }
10926    
10927 });
10928
10929  
10930
10931  /*
10932  * - LGPL
10933  *
10934  * ProgressBar
10935  * 
10936  */
10937
10938 /**
10939  * @class Roo.bootstrap.ProgressBar
10940  * @extends Roo.bootstrap.Component
10941  * Bootstrap ProgressBar class
10942  * @cfg {Number} aria_valuenow aria-value now
10943  * @cfg {Number} aria_valuemin aria-value min
10944  * @cfg {Number} aria_valuemax aria-value max
10945  * @cfg {String} label label for the progress bar
10946  * @cfg {String} panel (success | info | warning | danger )
10947  * @cfg {String} role role of the progress bar
10948  * @cfg {String} sr_only text
10949  * 
10950  * 
10951  * @constructor
10952  * Create a new ProgressBar
10953  * @param {Object} config The config object
10954  */
10955
10956 Roo.bootstrap.ProgressBar = function(config){
10957     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10958 };
10959
10960 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10961     
10962     aria_valuenow : 0,
10963     aria_valuemin : 0,
10964     aria_valuemax : 100,
10965     label : false,
10966     panel : false,
10967     role : false,
10968     sr_only: false,
10969     
10970     getAutoCreate : function()
10971     {
10972         
10973         var cfg = {
10974             tag: 'div',
10975             cls: 'progress-bar',
10976             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10977         };
10978         
10979         if(this.sr_only){
10980             cfg.cn = {
10981                 tag: 'span',
10982                 cls: 'sr-only',
10983                 html: this.sr_only
10984             }
10985         }
10986         
10987         if(this.role){
10988             cfg.role = this.role;
10989         }
10990         
10991         if(this.aria_valuenow){
10992             cfg['aria-valuenow'] = this.aria_valuenow;
10993         }
10994         
10995         if(this.aria_valuemin){
10996             cfg['aria-valuemin'] = this.aria_valuemin;
10997         }
10998         
10999         if(this.aria_valuemax){
11000             cfg['aria-valuemax'] = this.aria_valuemax;
11001         }
11002         
11003         if(this.label && !this.sr_only){
11004             cfg.html = this.label;
11005         }
11006         
11007         if(this.panel){
11008             cfg.cls += ' progress-bar-' + this.panel;
11009         }
11010         
11011         return cfg;
11012     },
11013     
11014     update : function(aria_valuenow)
11015     {
11016         this.aria_valuenow = aria_valuenow;
11017         
11018         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11019     }
11020    
11021 });
11022
11023  
11024
11025  /*
11026  * - LGPL
11027  *
11028  * TabPanel
11029  * 
11030  */
11031
11032 /**
11033  * @class Roo.bootstrap.TabPanel
11034  * @extends Roo.bootstrap.Component
11035  * Bootstrap TabPanel class
11036  * @cfg {Boolean} active panel active
11037  * @cfg {String} html panel content
11038  * @cfg {String} tabId tab relate id
11039  * @cfg {String} navId The navbar which triggers show hide
11040  * 
11041  * 
11042  * @constructor
11043  * Create a new TabPanel
11044  * @param {Object} config The config object
11045  */
11046
11047 Roo.bootstrap.TabPanel = function(config){
11048     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11049 };
11050
11051 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11052     
11053     active: false,
11054     html: false,
11055     tabId: false,
11056     navId : false,
11057     
11058     getAutoCreate : function(){
11059         var cfg = {
11060             tag: 'div',
11061             cls: 'tab-pane',
11062             html: this.html || ''
11063         };
11064         
11065         if(this.active){
11066             cfg.cls += ' active';
11067         }
11068         
11069         if(this.tabId){
11070             cfg.tabId = this.tabId;
11071         }
11072         
11073         return cfg;
11074     },
11075     onRender : function(ct, position)
11076     {
11077        // Roo.log("Call onRender: " + this.xtype);
11078         
11079         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11080         
11081         if (this.navId && this.tabId) {
11082             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11083             if (!item) {
11084                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11085             } else {
11086                 item.on('changed', function(item, state) {
11087                     this.setActive(state);
11088                 }, this);
11089             }
11090         }
11091         
11092     },
11093     setActive: function(state)
11094     {
11095         Roo.log("panel - set active " + this.tabId + "=" + state);
11096         
11097         this.active = state;
11098         if (!state) {
11099             this.el.removeClass('active');
11100             
11101         } else  if (!this.el.hasClass('active')) {
11102             this.el.addClass('active');
11103         }
11104         this.fireEvent('changed', this, state);
11105     }
11106     
11107     
11108 });
11109  
11110
11111  
11112
11113  /*
11114  * - LGPL
11115  *
11116  * DateField
11117  * 
11118  */
11119
11120 /**
11121  * @class Roo.bootstrap.DateField
11122  * @extends Roo.bootstrap.Input
11123  * Bootstrap DateField class
11124  * @cfg {Number} weekStart default 0
11125  * @cfg {Number} weekStart default 0
11126  * @cfg {Number} viewMode default empty, (months|years)
11127  * @cfg {Number} minViewMode default empty, (months|years)
11128  * @cfg {Number} startDate default -Infinity
11129  * @cfg {Number} endDate default Infinity
11130  * @cfg {Boolean} todayHighlight default false
11131  * @cfg {Boolean} todayBtn default false
11132  * @cfg {Boolean} calendarWeeks default false
11133  * @cfg {Object} daysOfWeekDisabled default empty
11134  * 
11135  * @cfg {Boolean} keyboardNavigation default true
11136  * @cfg {String} language default en
11137  * 
11138  * @constructor
11139  * Create a new DateField
11140  * @param {Object} config The config object
11141  */
11142
11143 Roo.bootstrap.DateField = function(config){
11144     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11145      this.addEvents({
11146             /**
11147              * @event show
11148              * Fires when this field show.
11149              * @param {Roo.bootstrap.DateField} this
11150              * @param {Mixed} date The date value
11151              */
11152             show : true,
11153             /**
11154              * @event show
11155              * Fires when this field hide.
11156              * @param {Roo.bootstrap.DateField} this
11157              * @param {Mixed} date The date value
11158              */
11159             hide : true,
11160             /**
11161              * @event select
11162              * Fires when select a date.
11163              * @param {Roo.bootstrap.DateField} this
11164              * @param {Mixed} date The date value
11165              */
11166             select : true
11167         });
11168 };
11169
11170 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11171     
11172     /**
11173      * @cfg {String} format
11174      * The default date format string which can be overriden for localization support.  The format must be
11175      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11176      */
11177     format : "m/d/y",
11178     /**
11179      * @cfg {String} altFormats
11180      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11181      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11182      */
11183     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11184     
11185     weekStart : 0,
11186     
11187     viewMode : '',
11188     
11189     minViewMode : '',
11190     
11191     todayHighlight : false,
11192     
11193     todayBtn: false,
11194     
11195     language: 'en',
11196     
11197     keyboardNavigation: true,
11198     
11199     calendarWeeks: false,
11200     
11201     startDate: -Infinity,
11202     
11203     endDate: Infinity,
11204     
11205     daysOfWeekDisabled: [],
11206     
11207     _events: [],
11208     
11209     UTCDate: function()
11210     {
11211         return new Date(Date.UTC.apply(Date, arguments));
11212     },
11213     
11214     UTCToday: function()
11215     {
11216         var today = new Date();
11217         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11218     },
11219     
11220     getDate: function() {
11221             var d = this.getUTCDate();
11222             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11223     },
11224     
11225     getUTCDate: function() {
11226             return this.date;
11227     },
11228     
11229     setDate: function(d) {
11230             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11231     },
11232     
11233     setUTCDate: function(d) {
11234             this.date = d;
11235             this.setValue(this.formatDate(this.date));
11236     },
11237         
11238     onRender: function(ct, position)
11239     {
11240         
11241         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11242         
11243         this.language = this.language || 'en';
11244         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11245         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11246         
11247         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11248         this.format = this.format || 'm/d/y';
11249         this.isInline = false;
11250         this.isInput = true;
11251         this.component = this.el.select('.add-on', true).first() || false;
11252         this.component = (this.component && this.component.length === 0) ? false : this.component;
11253         this.hasInput = this.component && this.inputEL().length;
11254         
11255         if (typeof(this.minViewMode === 'string')) {
11256             switch (this.minViewMode) {
11257                 case 'months':
11258                     this.minViewMode = 1;
11259                     break;
11260                 case 'years':
11261                     this.minViewMode = 2;
11262                     break;
11263                 default:
11264                     this.minViewMode = 0;
11265                     break;
11266             }
11267         }
11268         
11269         if (typeof(this.viewMode === 'string')) {
11270             switch (this.viewMode) {
11271                 case 'months':
11272                     this.viewMode = 1;
11273                     break;
11274                 case 'years':
11275                     this.viewMode = 2;
11276                     break;
11277                 default:
11278                     this.viewMode = 0;
11279                     break;
11280             }
11281         }
11282                 
11283         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11284         
11285         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11286         
11287         this.picker().on('mousedown', this.onMousedown, this);
11288         this.picker().on('click', this.onClick, this);
11289         
11290         this.picker().addClass('datepicker-dropdown');
11291         
11292         this.startViewMode = this.viewMode;
11293         
11294         
11295         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11296             if(!this.calendarWeeks){
11297                 v.remove();
11298                 return;
11299             };
11300             
11301             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11302             v.attr('colspan', function(i, val){
11303                 return parseInt(val) + 1;
11304             });
11305         })
11306                         
11307         
11308         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11309         
11310         this.setStartDate(this.startDate);
11311         this.setEndDate(this.endDate);
11312         
11313         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11314         
11315         this.fillDow();
11316         this.fillMonths();
11317         this.update();
11318         this.showMode();
11319         
11320         if(this.isInline) {
11321             this.show();
11322         }
11323     },
11324     
11325     picker : function()
11326     {
11327         return this.el.select('.datepicker', true).first();
11328     },
11329     
11330     fillDow: function()
11331     {
11332         var dowCnt = this.weekStart;
11333         
11334         var dow = {
11335             tag: 'tr',
11336             cn: [
11337                 
11338             ]
11339         };
11340         
11341         if(this.calendarWeeks){
11342             dow.cn.push({
11343                 tag: 'th',
11344                 cls: 'cw',
11345                 html: '&nbsp;'
11346             })
11347         }
11348         
11349         while (dowCnt < this.weekStart + 7) {
11350             dow.cn.push({
11351                 tag: 'th',
11352                 cls: 'dow',
11353                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11354             });
11355         }
11356         
11357         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11358     },
11359     
11360     fillMonths: function()
11361     {    
11362         var i = 0
11363         var months = this.picker().select('>.datepicker-months td', true).first();
11364         
11365         months.dom.innerHTML = '';
11366         
11367         while (i < 12) {
11368             var month = {
11369                 tag: 'span',
11370                 cls: 'month',
11371                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11372             }
11373             
11374             months.createChild(month);
11375         }
11376         
11377     },
11378     
11379     update: function(){
11380         
11381         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11382         
11383         if (this.date < this.startDate) {
11384             this.viewDate = new Date(this.startDate);
11385         } else if (this.date > this.endDate) {
11386             this.viewDate = new Date(this.endDate);
11387         } else {
11388             this.viewDate = new Date(this.date);
11389         }
11390         
11391         this.fill();
11392     },
11393     
11394     fill: function() {
11395         var d = new Date(this.viewDate),
11396                 year = d.getUTCFullYear(),
11397                 month = d.getUTCMonth(),
11398                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11399                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11400                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11401                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11402                 currentDate = this.date && this.date.valueOf(),
11403                 today = this.UTCToday();
11404         
11405         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11406         
11407 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11408         
11409 //        this.picker.select('>tfoot th.today').
11410 //                                              .text(dates[this.language].today)
11411 //                                              .toggle(this.todayBtn !== false);
11412     
11413         this.updateNavArrows();
11414         this.fillMonths();
11415                                                 
11416         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11417         
11418         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11419          
11420         prevMonth.setUTCDate(day);
11421         
11422         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11423         
11424         var nextMonth = new Date(prevMonth);
11425         
11426         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11427         
11428         nextMonth = nextMonth.valueOf();
11429         
11430         var fillMonths = false;
11431         
11432         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11433         
11434         while(prevMonth.valueOf() < nextMonth) {
11435             var clsName = '';
11436             
11437             if (prevMonth.getUTCDay() === this.weekStart) {
11438                 if(fillMonths){
11439                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11440                 }
11441                     
11442                 fillMonths = {
11443                     tag: 'tr',
11444                     cn: []
11445                 };
11446                 
11447                 if(this.calendarWeeks){
11448                     // ISO 8601: First week contains first thursday.
11449                     // ISO also states week starts on Monday, but we can be more abstract here.
11450                     var
11451                     // Start of current week: based on weekstart/current date
11452                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11453                     // Thursday of this week
11454                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11455                     // First Thursday of year, year from thursday
11456                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11457                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11458                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11459                     
11460                     fillMonths.cn.push({
11461                         tag: 'td',
11462                         cls: 'cw',
11463                         html: calWeek
11464                     });
11465                 }
11466             }
11467             
11468             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11469                 clsName += ' old';
11470             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11471                 clsName += ' new';
11472             }
11473             if (this.todayHighlight &&
11474                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11475                 prevMonth.getUTCMonth() == today.getMonth() &&
11476                 prevMonth.getUTCDate() == today.getDate()) {
11477                 clsName += ' today';
11478             }
11479             
11480             if (currentDate && prevMonth.valueOf() === currentDate) {
11481                 clsName += ' active';
11482             }
11483             
11484             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11485                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11486                     clsName += ' disabled';
11487             }
11488             
11489             fillMonths.cn.push({
11490                 tag: 'td',
11491                 cls: 'day ' + clsName,
11492                 html: prevMonth.getDate()
11493             })
11494             
11495             prevMonth.setDate(prevMonth.getDate()+1);
11496         }
11497           
11498         var currentYear = this.date && this.date.getUTCFullYear();
11499         var currentMonth = this.date && this.date.getUTCMonth();
11500         
11501         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11502         
11503         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11504             v.removeClass('active');
11505             
11506             if(currentYear === year && k === currentMonth){
11507                 v.addClass('active');
11508             }
11509             
11510             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11511                 v.addClass('disabled');
11512             }
11513             
11514         });
11515         
11516         
11517         year = parseInt(year/10, 10) * 10;
11518         
11519         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11520         
11521         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11522         
11523         year -= 1;
11524         for (var i = -1; i < 11; i++) {
11525             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11526                 tag: 'span',
11527                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11528                 html: year
11529             })
11530             
11531             year += 1;
11532         }
11533     },
11534     
11535     showMode: function(dir) {
11536         if (dir) {
11537             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11538         }
11539         Roo.each(this.picker().select('>div',true).elements, function(v){
11540             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11541             v.hide();
11542         });
11543         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11544     },
11545     
11546     place: function()
11547     {
11548         if(this.isInline) return;
11549         
11550         this.picker().removeClass(['bottom', 'top']);
11551         
11552         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11553             /*
11554              * place to the top of element!
11555              *
11556              */
11557             
11558             this.picker().addClass('top');
11559             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11560             
11561             return;
11562         }
11563         
11564         this.picker().addClass('bottom');
11565         
11566         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11567     },
11568     
11569     parseDate : function(value){
11570         if(!value || value instanceof Date){
11571             return value;
11572         }
11573         var v = Date.parseDate(value, this.format);
11574         if (!v && this.useIso) {
11575             v = Date.parseDate(value, 'Y-m-d');
11576         }
11577         if(!v && this.altFormats){
11578             if(!this.altFormatsArray){
11579                 this.altFormatsArray = this.altFormats.split("|");
11580             }
11581             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11582                 v = Date.parseDate(value, this.altFormatsArray[i]);
11583             }
11584         }
11585         return v;
11586     },
11587     
11588     formatDate : function(date, fmt){
11589         return (!date || !(date instanceof Date)) ?
11590         date : date.dateFormat(fmt || this.format);
11591     },
11592     
11593     onFocus : function()
11594     {
11595         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11596         this.show();
11597     },
11598     
11599     onBlur : function()
11600     {
11601         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11602         this.hide();
11603     },
11604     
11605     show : function()
11606     {
11607         this.picker().show();
11608         this.update();
11609         this.place();
11610         
11611         this.fireEvent('show', this, this.date);
11612     },
11613     
11614     hide : function()
11615     {
11616         if(this.isInline) return;
11617         this.picker().hide();
11618         this.viewMode = this.startViewMode;
11619         this.showMode();
11620         
11621         this.fireEvent('hide', this, this.date);
11622         
11623     },
11624     
11625     onMousedown: function(e){
11626         e.stopPropagation();
11627         e.preventDefault();
11628     },
11629     
11630     keyup: function(e){
11631         Roo.bootstrap.DateField.superclass.keyup.call(this);
11632         this.update();
11633         
11634     },
11635
11636     setValue: function(v){
11637         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11638         
11639         this.fireEvent('select', this, this.date);
11640         
11641     },
11642     
11643     fireKey: function(e){
11644         if (!this.picker().isVisible()){
11645             if (e.keyCode == 27) // allow escape to hide and re-show picker
11646                 this.show();
11647             return;
11648         }
11649         var dateChanged = false,
11650         dir, day, month,
11651         newDate, newViewDate;
11652         switch(e.keyCode){
11653             case 27: // escape
11654                 this.hide();
11655                 e.preventDefault();
11656                 break;
11657             case 37: // left
11658             case 39: // right
11659                 if (!this.keyboardNavigation) break;
11660                 dir = e.keyCode == 37 ? -1 : 1;
11661                 
11662                 if (e.ctrlKey){
11663                     newDate = this.moveYear(this.date, dir);
11664                     newViewDate = this.moveYear(this.viewDate, dir);
11665                 } else if (e.shiftKey){
11666                     newDate = this.moveMonth(this.date, dir);
11667                     newViewDate = this.moveMonth(this.viewDate, dir);
11668                 } else {
11669                     newDate = new Date(this.date);
11670                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11671                     newViewDate = new Date(this.viewDate);
11672                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11673                 }
11674                 if (this.dateWithinRange(newDate)){
11675                     this.date = newDate;
11676                     this.viewDate = newViewDate;
11677                     this.setValue(this.formatDate(this.date));
11678                     this.update();
11679                     e.preventDefault();
11680                     dateChanged = true;
11681                 }
11682                 break;
11683             case 38: // up
11684             case 40: // down
11685                 if (!this.keyboardNavigation) break;
11686                 dir = e.keyCode == 38 ? -1 : 1;
11687                 if (e.ctrlKey){
11688                     newDate = this.moveYear(this.date, dir);
11689                     newViewDate = this.moveYear(this.viewDate, dir);
11690                 } else if (e.shiftKey){
11691                     newDate = this.moveMonth(this.date, dir);
11692                     newViewDate = this.moveMonth(this.viewDate, dir);
11693                 } else {
11694                     newDate = new Date(this.date);
11695                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11696                     newViewDate = new Date(this.viewDate);
11697                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11698                 }
11699                 if (this.dateWithinRange(newDate)){
11700                     this.date = newDate;
11701                     this.viewDate = newViewDate;
11702                     this.setValue(this.formatDate(this.date));
11703                     this.update();
11704                     e.preventDefault();
11705                     dateChanged = true;
11706                 }
11707                 break;
11708             case 13: // enter
11709                 this.setValue(this.formatDate(this.date));
11710                 this.hide();
11711                 e.preventDefault();
11712                 break;
11713             case 9: // tab
11714                 this.setValue(this.formatDate(this.date));
11715                 this.hide();
11716                 break;
11717         }
11718     },
11719     
11720     
11721     onClick: function(e) {
11722         e.stopPropagation();
11723         e.preventDefault();
11724         
11725         var target = e.getTarget();
11726         
11727         if(target.nodeName.toLowerCase() === 'i'){
11728             target = Roo.get(target).dom.parentNode;
11729         }
11730         
11731         var nodeName = target.nodeName;
11732         var className = target.className;
11733         var html = target.innerHTML;
11734         
11735         switch(nodeName.toLowerCase()) {
11736             case 'th':
11737                 switch(className) {
11738                     case 'switch':
11739                         this.showMode(1);
11740                         break;
11741                     case 'prev':
11742                     case 'next':
11743                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11744                         switch(this.viewMode){
11745                                 case 0:
11746                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11747                                         break;
11748                                 case 1:
11749                                 case 2:
11750                                         this.viewDate = this.moveYear(this.viewDate, dir);
11751                                         break;
11752                         }
11753                         this.fill();
11754                         break;
11755                     case 'today':
11756                         var date = new Date();
11757                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11758                         this.fill()
11759                         this.setValue(this.formatDate(this.date));
11760                         this.hide();
11761                         break;
11762                 }
11763                 break;
11764             case 'span':
11765                 if (className.indexOf('disabled') === -1) {
11766                     this.viewDate.setUTCDate(1);
11767                     if (className.indexOf('month') !== -1) {
11768                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11769                     } else {
11770                         var year = parseInt(html, 10) || 0;
11771                         this.viewDate.setUTCFullYear(year);
11772                         
11773                     }
11774                     this.showMode(-1);
11775                     this.fill();
11776                 }
11777                 break;
11778                 
11779             case 'td':
11780                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11781                     var day = parseInt(html, 10) || 1;
11782                     var year = this.viewDate.getUTCFullYear(),
11783                         month = this.viewDate.getUTCMonth();
11784
11785                     if (className.indexOf('old') !== -1) {
11786                         if(month === 0 ){
11787                             month = 11;
11788                             year -= 1;
11789                         }else{
11790                             month -= 1;
11791                         }
11792                     } else if (className.indexOf('new') !== -1) {
11793                         if (month == 11) {
11794                             month = 0;
11795                             year += 1;
11796                         } else {
11797                             month += 1;
11798                         }
11799                     }
11800                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11801                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11802                     this.fill();
11803                     this.setValue(this.formatDate(this.date));
11804                     this.hide();
11805                 }
11806                 break;
11807         }
11808     },
11809     
11810     setStartDate: function(startDate){
11811         this.startDate = startDate || -Infinity;
11812         if (this.startDate !== -Infinity) {
11813             this.startDate = this.parseDate(this.startDate);
11814         }
11815         this.update();
11816         this.updateNavArrows();
11817     },
11818
11819     setEndDate: function(endDate){
11820         this.endDate = endDate || Infinity;
11821         if (this.endDate !== Infinity) {
11822             this.endDate = this.parseDate(this.endDate);
11823         }
11824         this.update();
11825         this.updateNavArrows();
11826     },
11827     
11828     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11829         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11830         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11831             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11832         }
11833         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11834             return parseInt(d, 10);
11835         });
11836         this.update();
11837         this.updateNavArrows();
11838     },
11839     
11840     updateNavArrows: function() {
11841         var d = new Date(this.viewDate),
11842         year = d.getUTCFullYear(),
11843         month = d.getUTCMonth();
11844         
11845         Roo.each(this.picker().select('.prev', true).elements, function(v){
11846             v.show();
11847             switch (this.viewMode) {
11848                 case 0:
11849
11850                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11851                         v.hide();
11852                     }
11853                     break;
11854                 case 1:
11855                 case 2:
11856                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11857                         v.hide();
11858                     }
11859                     break;
11860             }
11861         });
11862         
11863         Roo.each(this.picker().select('.next', true).elements, function(v){
11864             v.show();
11865             switch (this.viewMode) {
11866                 case 0:
11867
11868                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11869                         v.hide();
11870                     }
11871                     break;
11872                 case 1:
11873                 case 2:
11874                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11875                         v.hide();
11876                     }
11877                     break;
11878             }
11879         })
11880     },
11881     
11882     moveMonth: function(date, dir){
11883         if (!dir) return date;
11884         var new_date = new Date(date.valueOf()),
11885         day = new_date.getUTCDate(),
11886         month = new_date.getUTCMonth(),
11887         mag = Math.abs(dir),
11888         new_month, test;
11889         dir = dir > 0 ? 1 : -1;
11890         if (mag == 1){
11891             test = dir == -1
11892             // If going back one month, make sure month is not current month
11893             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11894             ? function(){
11895                 return new_date.getUTCMonth() == month;
11896             }
11897             // If going forward one month, make sure month is as expected
11898             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11899             : function(){
11900                 return new_date.getUTCMonth() != new_month;
11901             };
11902             new_month = month + dir;
11903             new_date.setUTCMonth(new_month);
11904             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11905             if (new_month < 0 || new_month > 11)
11906                 new_month = (new_month + 12) % 12;
11907         } else {
11908             // For magnitudes >1, move one month at a time...
11909             for (var i=0; i<mag; i++)
11910                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11911                 new_date = this.moveMonth(new_date, dir);
11912             // ...then reset the day, keeping it in the new month
11913             new_month = new_date.getUTCMonth();
11914             new_date.setUTCDate(day);
11915             test = function(){
11916                 return new_month != new_date.getUTCMonth();
11917             };
11918         }
11919         // Common date-resetting loop -- if date is beyond end of month, make it
11920         // end of month
11921         while (test()){
11922             new_date.setUTCDate(--day);
11923             new_date.setUTCMonth(new_month);
11924         }
11925         return new_date;
11926     },
11927
11928     moveYear: function(date, dir){
11929         return this.moveMonth(date, dir*12);
11930     },
11931
11932     dateWithinRange: function(date){
11933         return date >= this.startDate && date <= this.endDate;
11934     },
11935
11936     
11937     remove: function() {
11938         this.picker().remove();
11939     }
11940    
11941 });
11942
11943 Roo.apply(Roo.bootstrap.DateField,  {
11944     
11945     head : {
11946         tag: 'thead',
11947         cn: [
11948         {
11949             tag: 'tr',
11950             cn: [
11951             {
11952                 tag: 'th',
11953                 cls: 'prev',
11954                 html: '<i class="icon-arrow-left"/>'
11955             },
11956             {
11957                 tag: 'th',
11958                 cls: 'switch',
11959                 colspan: '5'
11960             },
11961             {
11962                 tag: 'th',
11963                 cls: 'next',
11964                 html: '<i class="icon-arrow-right"/>'
11965             }
11966
11967             ]
11968         }
11969         ]
11970     },
11971     
11972     content : {
11973         tag: 'tbody',
11974         cn: [
11975         {
11976             tag: 'tr',
11977             cn: [
11978             {
11979                 tag: 'td',
11980                 colspan: '7'
11981             }
11982             ]
11983         }
11984         ]
11985     },
11986     
11987     footer : {
11988         tag: 'tfoot',
11989         cn: [
11990         {
11991             tag: 'tr',
11992             cn: [
11993             {
11994                 tag: 'th',
11995                 colspan: '7',
11996                 cls: 'today'
11997             }
11998                     
11999             ]
12000         }
12001         ]
12002     },
12003     
12004     dates:{
12005         en: {
12006             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12007             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12008             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12009             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12010             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12011             today: "Today"
12012         }
12013     },
12014     
12015     modes: [
12016     {
12017         clsName: 'days',
12018         navFnc: 'Month',
12019         navStep: 1
12020     },
12021     {
12022         clsName: 'months',
12023         navFnc: 'FullYear',
12024         navStep: 1
12025     },
12026     {
12027         clsName: 'years',
12028         navFnc: 'FullYear',
12029         navStep: 10
12030     }]
12031 });
12032
12033 Roo.apply(Roo.bootstrap.DateField,  {
12034   
12035     template : {
12036         tag: 'div',
12037         cls: 'datepicker dropdown-menu',
12038         cn: [
12039         {
12040             tag: 'div',
12041             cls: 'datepicker-days',
12042             cn: [
12043             {
12044                 tag: 'table',
12045                 cls: 'table-condensed',
12046                 cn:[
12047                 Roo.bootstrap.DateField.head,
12048                 {
12049                     tag: 'tbody'
12050                 },
12051                 Roo.bootstrap.DateField.footer
12052                 ]
12053             }
12054             ]
12055         },
12056         {
12057             tag: 'div',
12058             cls: 'datepicker-months',
12059             cn: [
12060             {
12061                 tag: 'table',
12062                 cls: 'table-condensed',
12063                 cn:[
12064                 Roo.bootstrap.DateField.head,
12065                 Roo.bootstrap.DateField.content,
12066                 Roo.bootstrap.DateField.footer
12067                 ]
12068             }
12069             ]
12070         },
12071         {
12072             tag: 'div',
12073             cls: 'datepicker-years',
12074             cn: [
12075             {
12076                 tag: 'table',
12077                 cls: 'table-condensed',
12078                 cn:[
12079                 Roo.bootstrap.DateField.head,
12080                 Roo.bootstrap.DateField.content,
12081                 Roo.bootstrap.DateField.footer
12082                 ]
12083             }
12084             ]
12085         }
12086         ]
12087     }
12088 });
12089
12090  
12091
12092  /*
12093  * - LGPL
12094  *
12095  * TimeField
12096  * 
12097  */
12098
12099 /**
12100  * @class Roo.bootstrap.TimeField
12101  * @extends Roo.bootstrap.Input
12102  * Bootstrap DateField class
12103  * 
12104  * 
12105  * @constructor
12106  * Create a new TimeField
12107  * @param {Object} config The config object
12108  */
12109
12110 Roo.bootstrap.TimeField = function(config){
12111     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12112     this.addEvents({
12113             /**
12114              * @event show
12115              * Fires when this field show.
12116              * @param {Roo.bootstrap.DateField} this
12117              * @param {Mixed} date The date value
12118              */
12119             show : true,
12120             /**
12121              * @event show
12122              * Fires when this field hide.
12123              * @param {Roo.bootstrap.DateField} this
12124              * @param {Mixed} date The date value
12125              */
12126             hide : true,
12127             /**
12128              * @event select
12129              * Fires when select a date.
12130              * @param {Roo.bootstrap.DateField} this
12131              * @param {Mixed} date The date value
12132              */
12133             select : true
12134         });
12135 };
12136
12137 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12138     
12139     /**
12140      * @cfg {String} format
12141      * The default time format string which can be overriden for localization support.  The format must be
12142      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12143      */
12144     format : "H:i",
12145        
12146     onRender: function(ct, position)
12147     {
12148         
12149         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12150                 
12151         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12152         
12153         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12154         
12155         this.pop = this.picker().select('>.datepicker-time',true).first();
12156         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12157         
12158         this.picker().on('mousedown', this.onMousedown, this);
12159         this.picker().on('click', this.onClick, this);
12160         
12161         this.picker().addClass('datepicker-dropdown');
12162     
12163         this.fillTime();
12164         this.update();
12165             
12166         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12167         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12168         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12169         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12170         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12171         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12172
12173     },
12174     
12175     fireKey: function(e){
12176         if (!this.picker().isVisible()){
12177             if (e.keyCode == 27) // allow escape to hide and re-show picker
12178                 this.show();
12179             return;
12180         }
12181
12182         e.preventDefault();
12183         
12184         switch(e.keyCode){
12185             case 27: // escape
12186                 this.hide();
12187                 break;
12188             case 37: // left
12189             case 39: // right
12190                 this.onTogglePeriod();
12191                 break;
12192             case 38: // up
12193                 this.onIncrementMinutes();
12194                 break;
12195             case 40: // down
12196                 this.onDecrementMinutes();
12197                 break;
12198             case 13: // enter
12199             case 9: // tab
12200                 this.setTime();
12201                 break;
12202         }
12203     },
12204     
12205     onClick: function(e) {
12206         e.stopPropagation();
12207         e.preventDefault();
12208     },
12209     
12210     picker : function()
12211     {
12212         return this.el.select('.datepicker', true).first();
12213     },
12214     
12215     fillTime: function()
12216     {    
12217         var time = this.pop.select('tbody', true).first();
12218         
12219         time.dom.innerHTML = '';
12220         
12221         time.createChild({
12222             tag: 'tr',
12223             cn: [
12224                 {
12225                     tag: 'td',
12226                     cn: [
12227                         {
12228                             tag: 'a',
12229                             href: '#',
12230                             cls: 'btn',
12231                             cn: [
12232                                 {
12233                                     tag: 'span',
12234                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12235                                 }
12236                             ]
12237                         } 
12238                     ]
12239                 },
12240                 {
12241                     tag: 'td',
12242                     cls: 'separator'
12243                 },
12244                 {
12245                     tag: 'td',
12246                     cn: [
12247                         {
12248                             tag: 'a',
12249                             href: '#',
12250                             cls: 'btn',
12251                             cn: [
12252                                 {
12253                                     tag: 'span',
12254                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12255                                 }
12256                             ]
12257                         }
12258                     ]
12259                 },
12260                 {
12261                     tag: 'td',
12262                     cls: 'separator'
12263                 }
12264             ]
12265         });
12266         
12267         time.createChild({
12268             tag: 'tr',
12269             cn: [
12270                 {
12271                     tag: 'td',
12272                     cn: [
12273                         {
12274                             tag: 'span',
12275                             cls: 'timepicker-hour',
12276                             html: '00'
12277                         }  
12278                     ]
12279                 },
12280                 {
12281                     tag: 'td',
12282                     cls: 'separator',
12283                     html: ':'
12284                 },
12285                 {
12286                     tag: 'td',
12287                     cn: [
12288                         {
12289                             tag: 'span',
12290                             cls: 'timepicker-minute',
12291                             html: '00'
12292                         }  
12293                     ]
12294                 },
12295                 {
12296                     tag: 'td',
12297                     cls: 'separator'
12298                 },
12299                 {
12300                     tag: 'td',
12301                     cn: [
12302                         {
12303                             tag: 'button',
12304                             type: 'button',
12305                             cls: 'btn btn-primary period',
12306                             html: 'AM'
12307                             
12308                         }
12309                     ]
12310                 }
12311             ]
12312         });
12313         
12314         time.createChild({
12315             tag: 'tr',
12316             cn: [
12317                 {
12318                     tag: 'td',
12319                     cn: [
12320                         {
12321                             tag: 'a',
12322                             href: '#',
12323                             cls: 'btn',
12324                             cn: [
12325                                 {
12326                                     tag: 'span',
12327                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12328                                 }
12329                             ]
12330                         }
12331                     ]
12332                 },
12333                 {
12334                     tag: 'td',
12335                     cls: 'separator'
12336                 },
12337                 {
12338                     tag: 'td',
12339                     cn: [
12340                         {
12341                             tag: 'a',
12342                             href: '#',
12343                             cls: 'btn',
12344                             cn: [
12345                                 {
12346                                     tag: 'span',
12347                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12348                                 }
12349                             ]
12350                         }
12351                     ]
12352                 },
12353                 {
12354                     tag: 'td',
12355                     cls: 'separator'
12356                 }
12357             ]
12358         });
12359         
12360     },
12361     
12362     update: function()
12363     {
12364         
12365         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12366         
12367         this.fill();
12368     },
12369     
12370     fill: function() 
12371     {
12372         var hours = this.time.getHours();
12373         var minutes = this.time.getMinutes();
12374         var period = 'AM';
12375         
12376         if(hours > 11){
12377             period = 'PM';
12378         }
12379         
12380         if(hours == 0){
12381             hours = 12;
12382         }
12383         
12384         
12385         if(hours > 12){
12386             hours = hours - 12;
12387         }
12388         
12389         if(hours < 10){
12390             hours = '0' + hours;
12391         }
12392         
12393         if(minutes < 10){
12394             minutes = '0' + minutes;
12395         }
12396         
12397         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12398         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12399         this.pop.select('button', true).first().dom.innerHTML = period;
12400         
12401     },
12402     
12403     place: function()
12404     {   
12405         this.picker().removeClass(['bottom', 'top']);
12406         
12407         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12408             /*
12409              * place to the top of element!
12410              *
12411              */
12412             
12413             this.picker().addClass('top');
12414             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12415             
12416             return;
12417         }
12418         
12419         this.picker().addClass('bottom');
12420         
12421         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12422     },
12423   
12424     onFocus : function()
12425     {
12426         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12427         this.show();
12428     },
12429     
12430     onBlur : function()
12431     {
12432         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12433         this.hide();
12434     },
12435     
12436     show : function()
12437     {
12438         this.picker().show();
12439         this.pop.show();
12440         this.update();
12441         this.place();
12442         
12443         this.fireEvent('show', this, this.date);
12444     },
12445     
12446     hide : function()
12447     {
12448         this.picker().hide();
12449         this.pop.hide();
12450         
12451         this.fireEvent('hide', this, this.date);
12452     },
12453     
12454     setTime : function()
12455     {
12456         this.hide();
12457         this.setValue(this.time.format(this.format));
12458         
12459         this.fireEvent('select', this, this.date);
12460         
12461         
12462     },
12463     
12464     onMousedown: function(e){
12465         e.stopPropagation();
12466         e.preventDefault();
12467     },
12468     
12469     onIncrementHours: function()
12470     {
12471         Roo.log('onIncrementHours');
12472         this.time = this.time.add(Date.HOUR, 1);
12473         this.update();
12474         
12475     },
12476     
12477     onDecrementHours: function()
12478     {
12479         Roo.log('onDecrementHours');
12480         this.time = this.time.add(Date.HOUR, -1);
12481         this.update();
12482     },
12483     
12484     onIncrementMinutes: function()
12485     {
12486         Roo.log('onIncrementMinutes');
12487         this.time = this.time.add(Date.MINUTE, 1);
12488         this.update();
12489     },
12490     
12491     onDecrementMinutes: function()
12492     {
12493         Roo.log('onDecrementMinutes');
12494         this.time = this.time.add(Date.MINUTE, -1);
12495         this.update();
12496     },
12497     
12498     onTogglePeriod: function()
12499     {
12500         Roo.log('onTogglePeriod');
12501         this.time = this.time.add(Date.HOUR, 12);
12502         this.update();
12503     }
12504     
12505    
12506 });
12507
12508 Roo.apply(Roo.bootstrap.TimeField,  {
12509     
12510     content : {
12511         tag: 'tbody',
12512         cn: [
12513             {
12514                 tag: 'tr',
12515                 cn: [
12516                 {
12517                     tag: 'td',
12518                     colspan: '7'
12519                 }
12520                 ]
12521             }
12522         ]
12523     },
12524     
12525     footer : {
12526         tag: 'tfoot',
12527         cn: [
12528             {
12529                 tag: 'tr',
12530                 cn: [
12531                 {
12532                     tag: 'th',
12533                     colspan: '7',
12534                     cls: '',
12535                     cn: [
12536                         {
12537                             tag: 'button',
12538                             cls: 'btn btn-info ok',
12539                             html: 'OK'
12540                         }
12541                     ]
12542                 }
12543
12544                 ]
12545             }
12546         ]
12547     }
12548 });
12549
12550 Roo.apply(Roo.bootstrap.TimeField,  {
12551   
12552     template : {
12553         tag: 'div',
12554         cls: 'datepicker dropdown-menu',
12555         cn: [
12556             {
12557                 tag: 'div',
12558                 cls: 'datepicker-time',
12559                 cn: [
12560                 {
12561                     tag: 'table',
12562                     cls: 'table-condensed',
12563                     cn:[
12564                     Roo.bootstrap.TimeField.content,
12565                     Roo.bootstrap.TimeField.footer
12566                     ]
12567                 }
12568                 ]
12569             }
12570         ]
12571     }
12572 });
12573
12574  
12575
12576  /*
12577  * - LGPL
12578  *
12579  * CheckBox
12580  * 
12581  */
12582
12583 /**
12584  * @class Roo.bootstrap.CheckBox
12585  * @extends Roo.bootstrap.Input
12586  * Bootstrap CheckBox class
12587  * 
12588  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12589  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12590  * @cfg {String} boxLabel The text that appears beside the checkbox
12591  * @cfg {Boolean} checked initnal the element
12592  * 
12593  * @constructor
12594  * Create a new CheckBox
12595  * @param {Object} config The config object
12596  */
12597
12598 Roo.bootstrap.CheckBox = function(config){
12599     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12600    
12601         this.addEvents({
12602             /**
12603             * @event check
12604             * Fires when the element is checked or unchecked.
12605             * @param {Roo.bootstrap.CheckBox} this This input
12606             * @param {Boolean} checked The new checked value
12607             */
12608            check : true
12609         });
12610 };
12611
12612 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12613     
12614     inputType: 'checkbox',
12615     inputValue: 1,
12616     valueOff: 0,
12617     boxLabel: false,
12618     checked: false,
12619     
12620     getAutoCreate : function()
12621     {
12622         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12623         
12624         var id = Roo.id();
12625         
12626         var cfg = {};
12627         
12628         cfg.cls = 'form-group' //input-group
12629         
12630         var input =  {
12631             tag: 'input',
12632             id : id,
12633             type : this.inputType,
12634             value : (!this.checked) ? this.valueOff : this.inputValue,
12635             cls : 'form-box',
12636             placeholder : this.placeholder || ''
12637             
12638         };
12639         
12640         if (this.disabled) {
12641             input.disabled=true;
12642         }
12643         
12644         if(this.checked){
12645             input.checked = this.checked;
12646         }
12647         
12648         if (this.name) {
12649             input.name = this.name;
12650         }
12651         
12652         if (this.size) {
12653             input.cls += ' input-' + this.size;
12654         }
12655         
12656         var settings=this;
12657         ['xs','sm','md','lg'].map(function(size){
12658             if (settings[size]) {
12659                 cfg.cls += ' col-' + size + '-' + settings[size];
12660             }
12661         });
12662         
12663         var inputblock = input;
12664         
12665         if (this.before || this.after) {
12666             
12667             inputblock = {
12668                 cls : 'input-group',
12669                 cn :  [] 
12670             };
12671             if (this.before) {
12672                 inputblock.cn.push({
12673                     tag :'span',
12674                     cls : 'input-group-addon',
12675                     html : this.before
12676                 });
12677             }
12678             inputblock.cn.push(input);
12679             if (this.after) {
12680                 inputblock.cn.push({
12681                     tag :'span',
12682                     cls : 'input-group-addon',
12683                     html : this.after
12684                 });
12685             }
12686             
12687         };
12688         
12689         if (align ==='left' && this.fieldLabel.length) {
12690                 Roo.log("left and has label");
12691                 cfg.cn = [
12692                     
12693                     {
12694                         tag: 'label',
12695                         'for' :  id,
12696                         cls : 'control-label col-md-' + this.labelWidth,
12697                         html : this.fieldLabel
12698                         
12699                     },
12700                     {
12701                         cls : "col-md-" + (12 - this.labelWidth), 
12702                         cn: [
12703                             inputblock
12704                         ]
12705                     }
12706                     
12707                 ];
12708         } else if ( this.fieldLabel.length) {
12709                 Roo.log(" label");
12710                 cfg.cn = [
12711                    
12712                     {
12713                         tag: this.boxLabel ? 'span' : 'label',
12714                         'for': id,
12715                         cls: 'control-label box-input-label',
12716                         //cls : 'input-group-addon',
12717                         html : this.fieldLabel
12718                         
12719                     },
12720                     
12721                     inputblock
12722                     
12723                 ];
12724
12725         } else {
12726             
12727                    Roo.log(" no label && no align");
12728                 cfg.cn = [
12729                     
12730                         inputblock
12731                     
12732                 ];
12733                 
12734                 
12735         };
12736         
12737         if(this.boxLabel){
12738             cfg.cn.push({
12739                 tag: 'label',
12740                 'for': id,
12741                 cls: 'box-label',
12742                 html: this.boxLabel
12743             })
12744         }
12745         
12746         return cfg;
12747         
12748     },
12749     
12750     /**
12751      * return the real input element.
12752      */
12753     inputEl: function ()
12754     {
12755         return this.el.select('input.form-box',true).first();
12756     },
12757     
12758     initEvents : function()
12759     {
12760 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12761         
12762         this.inputEl().on('click', this.onClick,  this);
12763         
12764     },
12765     
12766     onClick : function()
12767     {   
12768         this.setChecked(!this.checked);
12769     },
12770     
12771     setChecked : function(state,suppressEvent)
12772     {
12773         this.checked = state;
12774         
12775         this.inputEl().dom.checked = state;
12776         
12777         if(suppressEvent !== true){
12778             this.fireEvent('check', this, state);
12779         }
12780         
12781         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12782         
12783     },
12784     
12785     setValue : function(v,suppressEvent)
12786     {
12787         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12788     }
12789     
12790 });
12791
12792  
12793 /*
12794  * - LGPL
12795  *
12796  * Radio
12797  * 
12798  */
12799
12800 /**
12801  * @class Roo.bootstrap.Radio
12802  * @extends Roo.bootstrap.CheckBox
12803  * Bootstrap Radio class
12804
12805  * @constructor
12806  * Create a new Radio
12807  * @param {Object} config The config object
12808  */
12809
12810 Roo.bootstrap.Radio = function(config){
12811     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12812    
12813 };
12814
12815 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12816     
12817     inputType: 'radio',
12818     inputValue: '',
12819     valueOff: '',
12820     
12821     getAutoCreate : function()
12822     {
12823         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12824         
12825         var id = Roo.id();
12826         
12827         var cfg = {};
12828         
12829         cfg.cls = 'form-group' //input-group
12830         
12831         var input =  {
12832             tag: 'input',
12833             id : id,
12834             type : this.inputType,
12835             value : (!this.checked) ? this.valueOff : this.inputValue,
12836             cls : 'form-box',
12837             placeholder : this.placeholder || ''
12838             
12839         };
12840         
12841         if (this.disabled) {
12842             input.disabled=true;
12843         }
12844         
12845         if(this.checked){
12846             input.checked = this.checked;
12847         }
12848         
12849         if (this.name) {
12850             input.name = this.name;
12851         }
12852         
12853         if (this.size) {
12854             input.cls += ' input-' + this.size;
12855         }
12856         
12857         var settings=this;
12858         ['xs','sm','md','lg'].map(function(size){
12859             if (settings[size]) {
12860                 cfg.cls += ' col-' + size + '-' + settings[size];
12861             }
12862         });
12863         
12864         var inputblock = input;
12865         
12866         if (this.before || this.after) {
12867             
12868             inputblock = {
12869                 cls : 'input-group',
12870                 cn :  [] 
12871             };
12872             if (this.before) {
12873                 inputblock.cn.push({
12874                     tag :'span',
12875                     cls : 'input-group-addon',
12876                     html : this.before
12877                 });
12878             }
12879             inputblock.cn.push(input);
12880             if (this.after) {
12881                 inputblock.cn.push({
12882                     tag :'span',
12883                     cls : 'input-group-addon',
12884                     html : this.after
12885                 });
12886             }
12887             
12888         };
12889         
12890         if (align ==='left' && this.fieldLabel.length) {
12891                 Roo.log("left and has label");
12892                 cfg.cn = [
12893                     
12894                     {
12895                         tag: 'label',
12896                         'for' :  id,
12897                         cls : 'control-label col-md-' + this.labelWidth,
12898                         html : this.fieldLabel
12899                         
12900                     },
12901                     {
12902                         cls : "col-md-" + (12 - this.labelWidth), 
12903                         cn: [
12904                             inputblock
12905                         ]
12906                     }
12907                     
12908                 ];
12909         } else if ( this.fieldLabel.length) {
12910                 Roo.log(" label");
12911                  cfg.cn = [
12912                    
12913                     {
12914                         tag: 'label',
12915                         'for': id,
12916                         cls: 'control-label box-input-label',
12917                         //cls : 'input-group-addon',
12918                         html : this.fieldLabel
12919                         
12920                     },
12921                     
12922                     inputblock
12923                     
12924                 ];
12925
12926         } else {
12927             
12928                    Roo.log(" no label && no align");
12929                 cfg.cn = [
12930                     
12931                         inputblock
12932                     
12933                 ];
12934                 
12935                 
12936         };
12937         
12938         if(this.boxLabel){
12939             cfg.cn.push({
12940                 tag: 'label',
12941                 'for': id,
12942                 cls: 'box-label',
12943                 html: this.boxLabel
12944             })
12945         }
12946         
12947         return cfg;
12948         
12949     },
12950    
12951     onClick : function()
12952     {   
12953         this.setChecked(true);
12954     },
12955     
12956     setChecked : function(state,suppressEvent)
12957     {
12958         if(state){
12959             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12960                 v.dom.checked = false;
12961             });
12962         }
12963         
12964         this.checked = state;
12965         this.inputEl().dom.checked = state;
12966         
12967         if(suppressEvent !== true){
12968             this.fireEvent('check', this, state);
12969         }
12970         
12971         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12972         
12973     },
12974     
12975     getGroupValue : function()
12976     {
12977         var value = ''
12978         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12979             if(v.dom.checked == true){
12980                 value = v.dom.value;
12981             }
12982         });
12983         
12984         return value;
12985     },
12986     
12987     /**
12988      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12989      * @return {Mixed} value The field value
12990      */
12991     getValue : function(){
12992         return this.getGroupValue();
12993     }
12994     
12995 });
12996
12997  
12998 //<script type="text/javascript">
12999
13000 /*
13001  * Based  Ext JS Library 1.1.1
13002  * Copyright(c) 2006-2007, Ext JS, LLC.
13003  * LGPL
13004  *
13005  */
13006  
13007 /**
13008  * @class Roo.HtmlEditorCore
13009  * @extends Roo.Component
13010  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13011  *
13012  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13013  */
13014
13015 Roo.HtmlEditorCore = function(config){
13016     
13017     
13018     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13019     this.addEvents({
13020         /**
13021          * @event initialize
13022          * Fires when the editor is fully initialized (including the iframe)
13023          * @param {Roo.HtmlEditorCore} this
13024          */
13025         initialize: true,
13026         /**
13027          * @event activate
13028          * Fires when the editor is first receives the focus. Any insertion must wait
13029          * until after this event.
13030          * @param {Roo.HtmlEditorCore} this
13031          */
13032         activate: true,
13033          /**
13034          * @event beforesync
13035          * Fires before the textarea is updated with content from the editor iframe. Return false
13036          * to cancel the sync.
13037          * @param {Roo.HtmlEditorCore} this
13038          * @param {String} html
13039          */
13040         beforesync: true,
13041          /**
13042          * @event beforepush
13043          * Fires before the iframe editor is updated with content from the textarea. Return false
13044          * to cancel the push.
13045          * @param {Roo.HtmlEditorCore} this
13046          * @param {String} html
13047          */
13048         beforepush: true,
13049          /**
13050          * @event sync
13051          * Fires when the textarea is updated with content from the editor iframe.
13052          * @param {Roo.HtmlEditorCore} this
13053          * @param {String} html
13054          */
13055         sync: true,
13056          /**
13057          * @event push
13058          * Fires when the iframe editor is updated with content from the textarea.
13059          * @param {Roo.HtmlEditorCore} this
13060          * @param {String} html
13061          */
13062         push: true,
13063         
13064         /**
13065          * @event editorevent
13066          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13067          * @param {Roo.HtmlEditorCore} this
13068          */
13069         editorevent: true
13070     });
13071      
13072 };
13073
13074
13075 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13076
13077
13078      /**
13079      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13080      */
13081     
13082     owner : false,
13083     
13084      /**
13085      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13086      *                        Roo.resizable.
13087      */
13088     resizable : false,
13089      /**
13090      * @cfg {Number} height (in pixels)
13091      */   
13092     height: 300,
13093    /**
13094      * @cfg {Number} width (in pixels)
13095      */   
13096     width: 500,
13097     
13098     /**
13099      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13100      * 
13101      */
13102     stylesheets: false,
13103     
13104     // id of frame..
13105     frameId: false,
13106     
13107     // private properties
13108     validationEvent : false,
13109     deferHeight: true,
13110     initialized : false,
13111     activated : false,
13112     sourceEditMode : false,
13113     onFocus : Roo.emptyFn,
13114     iframePad:3,
13115     hideMode:'offsets',
13116     
13117     clearUp: true,
13118     
13119      
13120     
13121
13122     /**
13123      * Protected method that will not generally be called directly. It
13124      * is called when the editor initializes the iframe with HTML contents. Override this method if you
13125      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13126      */
13127     getDocMarkup : function(){
13128         // body styles..
13129         var st = '';
13130         Roo.log(this.stylesheets);
13131         
13132         // inherit styels from page...?? 
13133         if (this.stylesheets === false) {
13134             
13135             Roo.get(document.head).select('style').each(function(node) {
13136                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13137             });
13138             
13139             Roo.get(document.head).select('link').each(function(node) { 
13140                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13141             });
13142             
13143         } else if (!this.stylesheets.length) {
13144                 // simple..
13145                 st = '<style type="text/css">' +
13146                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13147                    '</style>';
13148         } else {
13149             Roo.each(this.stylesheets, function(s) {
13150                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13151             });
13152             
13153         }
13154         
13155         st +=  '<style type="text/css">' +
13156             'IMG { cursor: pointer } ' +
13157         '</style>';
13158
13159         
13160         return '<html><head>' + st  +
13161             //<style type="text/css">' +
13162             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13163             //'</style>' +
13164             ' </head><body class="roo-htmleditor-body"></body></html>';
13165     },
13166
13167     // private
13168     onRender : function(ct, position)
13169     {
13170         var _t = this;
13171         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13172         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13173         
13174         
13175         this.el.dom.style.border = '0 none';
13176         this.el.dom.setAttribute('tabIndex', -1);
13177         this.el.addClass('x-hidden hide');
13178         
13179         
13180         
13181         if(Roo.isIE){ // fix IE 1px bogus margin
13182             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13183         }
13184        
13185         
13186         this.frameId = Roo.id();
13187         
13188          
13189         
13190         var iframe = this.owner.wrap.createChild({
13191             tag: 'iframe',
13192             cls: 'form-control', // bootstrap..
13193             id: this.frameId,
13194             name: this.frameId,
13195             frameBorder : 'no',
13196             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13197         }, this.el
13198         );
13199         
13200         
13201         this.iframe = iframe.dom;
13202
13203          this.assignDocWin();
13204         
13205         this.doc.designMode = 'on';
13206        
13207         this.doc.open();
13208         this.doc.write(this.getDocMarkup());
13209         this.doc.close();
13210
13211         
13212         var task = { // must defer to wait for browser to be ready
13213             run : function(){
13214                 //console.log("run task?" + this.doc.readyState);
13215                 this.assignDocWin();
13216                 if(this.doc.body || this.doc.readyState == 'complete'){
13217                     try {
13218                         this.doc.designMode="on";
13219                     } catch (e) {
13220                         return;
13221                     }
13222                     Roo.TaskMgr.stop(task);
13223                     this.initEditor.defer(10, this);
13224                 }
13225             },
13226             interval : 10,
13227             duration: 10000,
13228             scope: this
13229         };
13230         Roo.TaskMgr.start(task);
13231
13232         
13233          
13234     },
13235
13236     // private
13237     onResize : function(w, h)
13238     {
13239          Roo.log('resize: ' +w + ',' + h );
13240         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13241         if(!this.iframe){
13242             return;
13243         }
13244         if(typeof w == 'number'){
13245             
13246             this.iframe.style.width = w + 'px';
13247         }
13248         if(typeof h == 'number'){
13249             
13250             this.iframe.style.height = h + 'px';
13251             if(this.doc){
13252                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13253             }
13254         }
13255         
13256     },
13257
13258     /**
13259      * Toggles the editor between standard and source edit mode.
13260      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13261      */
13262     toggleSourceEdit : function(sourceEditMode){
13263         
13264         this.sourceEditMode = sourceEditMode === true;
13265         
13266         if(this.sourceEditMode){
13267  
13268             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13269             
13270         }else{
13271             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13272             //this.iframe.className = '';
13273             this.deferFocus();
13274         }
13275         //this.setSize(this.owner.wrap.getSize());
13276         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13277     },
13278
13279     
13280   
13281
13282     /**
13283      * Protected method that will not generally be called directly. If you need/want
13284      * custom HTML cleanup, this is the method you should override.
13285      * @param {String} html The HTML to be cleaned
13286      * return {String} The cleaned HTML
13287      */
13288     cleanHtml : function(html){
13289         html = String(html);
13290         if(html.length > 5){
13291             if(Roo.isSafari){ // strip safari nonsense
13292                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13293             }
13294         }
13295         if(html == '&nbsp;'){
13296             html = '';
13297         }
13298         return html;
13299     },
13300
13301     /**
13302      * HTML Editor -> Textarea
13303      * Protected method that will not generally be called directly. Syncs the contents
13304      * of the editor iframe with the textarea.
13305      */
13306     syncValue : function(){
13307         if(this.initialized){
13308             var bd = (this.doc.body || this.doc.documentElement);
13309             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13310             var html = bd.innerHTML;
13311             if(Roo.isSafari){
13312                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13313                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13314                 if(m && m[1]){
13315                     html = '<div style="'+m[0]+'">' + html + '</div>';
13316                 }
13317             }
13318             html = this.cleanHtml(html);
13319             // fix up the special chars.. normaly like back quotes in word...
13320             // however we do not want to do this with chinese..
13321             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13322                 var cc = b.charCodeAt();
13323                 if (
13324                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13325                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13326                     (cc >= 0xf900 && cc < 0xfb00 )
13327                 ) {
13328                         return b;
13329                 }
13330                 return "&#"+cc+";" 
13331             });
13332             if(this.owner.fireEvent('beforesync', this, html) !== false){
13333                 this.el.dom.value = html;
13334                 this.owner.fireEvent('sync', this, html);
13335             }
13336         }
13337     },
13338
13339     /**
13340      * Protected method that will not generally be called directly. Pushes the value of the textarea
13341      * into the iframe editor.
13342      */
13343     pushValue : function(){
13344         if(this.initialized){
13345             var v = this.el.dom.value.trim();
13346             
13347 //            if(v.length < 1){
13348 //                v = '&#160;';
13349 //            }
13350             
13351             if(this.owner.fireEvent('beforepush', this, v) !== false){
13352                 var d = (this.doc.body || this.doc.documentElement);
13353                 d.innerHTML = v;
13354                 this.cleanUpPaste();
13355                 this.el.dom.value = d.innerHTML;
13356                 this.owner.fireEvent('push', this, v);
13357             }
13358         }
13359     },
13360
13361     // private
13362     deferFocus : function(){
13363         this.focus.defer(10, this);
13364     },
13365
13366     // doc'ed in Field
13367     focus : function(){
13368         if(this.win && !this.sourceEditMode){
13369             this.win.focus();
13370         }else{
13371             this.el.focus();
13372         }
13373     },
13374     
13375     assignDocWin: function()
13376     {
13377         var iframe = this.iframe;
13378         
13379          if(Roo.isIE){
13380             this.doc = iframe.contentWindow.document;
13381             this.win = iframe.contentWindow;
13382         } else {
13383             if (!Roo.get(this.frameId)) {
13384                 return;
13385             }
13386             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13387             this.win = Roo.get(this.frameId).dom.contentWindow;
13388         }
13389     },
13390     
13391     // private
13392     initEditor : function(){
13393         //console.log("INIT EDITOR");
13394         this.assignDocWin();
13395         
13396         
13397         
13398         this.doc.designMode="on";
13399         this.doc.open();
13400         this.doc.write(this.getDocMarkup());
13401         this.doc.close();
13402         
13403         var dbody = (this.doc.body || this.doc.documentElement);
13404         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13405         // this copies styles from the containing element into thsi one..
13406         // not sure why we need all of this..
13407         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13408         ss['background-attachment'] = 'fixed'; // w3c
13409         dbody.bgProperties = 'fixed'; // ie
13410         Roo.DomHelper.applyStyles(dbody, ss);
13411         Roo.EventManager.on(this.doc, {
13412             //'mousedown': this.onEditorEvent,
13413             'mouseup': this.onEditorEvent,
13414             'dblclick': this.onEditorEvent,
13415             'click': this.onEditorEvent,
13416             'keyup': this.onEditorEvent,
13417             buffer:100,
13418             scope: this
13419         });
13420         if(Roo.isGecko){
13421             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13422         }
13423         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13424             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13425         }
13426         this.initialized = true;
13427
13428         this.owner.fireEvent('initialize', this);
13429         this.pushValue();
13430     },
13431
13432     // private
13433     onDestroy : function(){
13434         
13435         
13436         
13437         if(this.rendered){
13438             
13439             //for (var i =0; i < this.toolbars.length;i++) {
13440             //    // fixme - ask toolbars for heights?
13441             //    this.toolbars[i].onDestroy();
13442            // }
13443             
13444             //this.wrap.dom.innerHTML = '';
13445             //this.wrap.remove();
13446         }
13447     },
13448
13449     // private
13450     onFirstFocus : function(){
13451         
13452         this.assignDocWin();
13453         
13454         
13455         this.activated = true;
13456          
13457     
13458         if(Roo.isGecko){ // prevent silly gecko errors
13459             this.win.focus();
13460             var s = this.win.getSelection();
13461             if(!s.focusNode || s.focusNode.nodeType != 3){
13462                 var r = s.getRangeAt(0);
13463                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13464                 r.collapse(true);
13465                 this.deferFocus();
13466             }
13467             try{
13468                 this.execCmd('useCSS', true);
13469                 this.execCmd('styleWithCSS', false);
13470             }catch(e){}
13471         }
13472         this.owner.fireEvent('activate', this);
13473     },
13474
13475     // private
13476     adjustFont: function(btn){
13477         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13478         //if(Roo.isSafari){ // safari
13479         //    adjust *= 2;
13480        // }
13481         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13482         if(Roo.isSafari){ // safari
13483             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13484             v =  (v < 10) ? 10 : v;
13485             v =  (v > 48) ? 48 : v;
13486             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13487             
13488         }
13489         
13490         
13491         v = Math.max(1, v+adjust);
13492         
13493         this.execCmd('FontSize', v  );
13494     },
13495
13496     onEditorEvent : function(e){
13497         this.owner.fireEvent('editorevent', this, e);
13498       //  this.updateToolbar();
13499         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13500     },
13501
13502     insertTag : function(tg)
13503     {
13504         // could be a bit smarter... -> wrap the current selected tRoo..
13505         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13506             
13507             range = this.createRange(this.getSelection());
13508             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13509             wrappingNode.appendChild(range.extractContents());
13510             range.insertNode(wrappingNode);
13511
13512             return;
13513             
13514             
13515             
13516         }
13517         this.execCmd("formatblock",   tg);
13518         
13519     },
13520     
13521     insertText : function(txt)
13522     {
13523         
13524         
13525         var range = this.createRange();
13526         range.deleteContents();
13527                //alert(Sender.getAttribute('label'));
13528                
13529         range.insertNode(this.doc.createTextNode(txt));
13530     } ,
13531     
13532      
13533
13534     /**
13535      * Executes a Midas editor command on the editor document and performs necessary focus and
13536      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13537      * @param {String} cmd The Midas command
13538      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13539      */
13540     relayCmd : function(cmd, value){
13541         this.win.focus();
13542         this.execCmd(cmd, value);
13543         this.owner.fireEvent('editorevent', this);
13544         //this.updateToolbar();
13545         this.owner.deferFocus();
13546     },
13547
13548     /**
13549      * Executes a Midas editor command directly on the editor document.
13550      * For visual commands, you should use {@link #relayCmd} instead.
13551      * <b>This should only be called after the editor is initialized.</b>
13552      * @param {String} cmd The Midas command
13553      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13554      */
13555     execCmd : function(cmd, value){
13556         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13557         this.syncValue();
13558     },
13559  
13560  
13561    
13562     /**
13563      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13564      * to insert tRoo.
13565      * @param {String} text | dom node.. 
13566      */
13567     insertAtCursor : function(text)
13568     {
13569         
13570         
13571         
13572         if(!this.activated){
13573             return;
13574         }
13575         /*
13576         if(Roo.isIE){
13577             this.win.focus();
13578             var r = this.doc.selection.createRange();
13579             if(r){
13580                 r.collapse(true);
13581                 r.pasteHTML(text);
13582                 this.syncValue();
13583                 this.deferFocus();
13584             
13585             }
13586             return;
13587         }
13588         */
13589         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13590             this.win.focus();
13591             
13592             
13593             // from jquery ui (MIT licenced)
13594             var range, node;
13595             var win = this.win;
13596             
13597             if (win.getSelection && win.getSelection().getRangeAt) {
13598                 range = win.getSelection().getRangeAt(0);
13599                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13600                 range.insertNode(node);
13601             } else if (win.document.selection && win.document.selection.createRange) {
13602                 // no firefox support
13603                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13604                 win.document.selection.createRange().pasteHTML(txt);
13605             } else {
13606                 // no firefox support
13607                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13608                 this.execCmd('InsertHTML', txt);
13609             } 
13610             
13611             this.syncValue();
13612             
13613             this.deferFocus();
13614         }
13615     },
13616  // private
13617     mozKeyPress : function(e){
13618         if(e.ctrlKey){
13619             var c = e.getCharCode(), cmd;
13620           
13621             if(c > 0){
13622                 c = String.fromCharCode(c).toLowerCase();
13623                 switch(c){
13624                     case 'b':
13625                         cmd = 'bold';
13626                         break;
13627                     case 'i':
13628                         cmd = 'italic';
13629                         break;
13630                     
13631                     case 'u':
13632                         cmd = 'underline';
13633                         break;
13634                     
13635                     case 'v':
13636                         this.cleanUpPaste.defer(100, this);
13637                         return;
13638                         
13639                 }
13640                 if(cmd){
13641                     this.win.focus();
13642                     this.execCmd(cmd);
13643                     this.deferFocus();
13644                     e.preventDefault();
13645                 }
13646                 
13647             }
13648         }
13649     },
13650
13651     // private
13652     fixKeys : function(){ // load time branching for fastest keydown performance
13653         if(Roo.isIE){
13654             return function(e){
13655                 var k = e.getKey(), r;
13656                 if(k == e.TAB){
13657                     e.stopEvent();
13658                     r = this.doc.selection.createRange();
13659                     if(r){
13660                         r.collapse(true);
13661                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13662                         this.deferFocus();
13663                     }
13664                     return;
13665                 }
13666                 
13667                 if(k == e.ENTER){
13668                     r = this.doc.selection.createRange();
13669                     if(r){
13670                         var target = r.parentElement();
13671                         if(!target || target.tagName.toLowerCase() != 'li'){
13672                             e.stopEvent();
13673                             r.pasteHTML('<br />');
13674                             r.collapse(false);
13675                             r.select();
13676                         }
13677                     }
13678                 }
13679                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13680                     this.cleanUpPaste.defer(100, this);
13681                     return;
13682                 }
13683                 
13684                 
13685             };
13686         }else if(Roo.isOpera){
13687             return function(e){
13688                 var k = e.getKey();
13689                 if(k == e.TAB){
13690                     e.stopEvent();
13691                     this.win.focus();
13692                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13693                     this.deferFocus();
13694                 }
13695                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13696                     this.cleanUpPaste.defer(100, this);
13697                     return;
13698                 }
13699                 
13700             };
13701         }else if(Roo.isSafari){
13702             return function(e){
13703                 var k = e.getKey();
13704                 
13705                 if(k == e.TAB){
13706                     e.stopEvent();
13707                     this.execCmd('InsertText','\t');
13708                     this.deferFocus();
13709                     return;
13710                 }
13711                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13712                     this.cleanUpPaste.defer(100, this);
13713                     return;
13714                 }
13715                 
13716              };
13717         }
13718     }(),
13719     
13720     getAllAncestors: function()
13721     {
13722         var p = this.getSelectedNode();
13723         var a = [];
13724         if (!p) {
13725             a.push(p); // push blank onto stack..
13726             p = this.getParentElement();
13727         }
13728         
13729         
13730         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13731             a.push(p);
13732             p = p.parentNode;
13733         }
13734         a.push(this.doc.body);
13735         return a;
13736     },
13737     lastSel : false,
13738     lastSelNode : false,
13739     
13740     
13741     getSelection : function() 
13742     {
13743         this.assignDocWin();
13744         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13745     },
13746     
13747     getSelectedNode: function() 
13748     {
13749         // this may only work on Gecko!!!
13750         
13751         // should we cache this!!!!
13752         
13753         
13754         
13755          
13756         var range = this.createRange(this.getSelection()).cloneRange();
13757         
13758         if (Roo.isIE) {
13759             var parent = range.parentElement();
13760             while (true) {
13761                 var testRange = range.duplicate();
13762                 testRange.moveToElementText(parent);
13763                 if (testRange.inRange(range)) {
13764                     break;
13765                 }
13766                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13767                     break;
13768                 }
13769                 parent = parent.parentElement;
13770             }
13771             return parent;
13772         }
13773         
13774         // is ancestor a text element.
13775         var ac =  range.commonAncestorContainer;
13776         if (ac.nodeType == 3) {
13777             ac = ac.parentNode;
13778         }
13779         
13780         var ar = ac.childNodes;
13781          
13782         var nodes = [];
13783         var other_nodes = [];
13784         var has_other_nodes = false;
13785         for (var i=0;i<ar.length;i++) {
13786             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13787                 continue;
13788             }
13789             // fullly contained node.
13790             
13791             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13792                 nodes.push(ar[i]);
13793                 continue;
13794             }
13795             
13796             // probably selected..
13797             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13798                 other_nodes.push(ar[i]);
13799                 continue;
13800             }
13801             // outer..
13802             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13803                 continue;
13804             }
13805             
13806             
13807             has_other_nodes = true;
13808         }
13809         if (!nodes.length && other_nodes.length) {
13810             nodes= other_nodes;
13811         }
13812         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13813             return false;
13814         }
13815         
13816         return nodes[0];
13817     },
13818     createRange: function(sel)
13819     {
13820         // this has strange effects when using with 
13821         // top toolbar - not sure if it's a great idea.
13822         //this.editor.contentWindow.focus();
13823         if (typeof sel != "undefined") {
13824             try {
13825                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13826             } catch(e) {
13827                 return this.doc.createRange();
13828             }
13829         } else {
13830             return this.doc.createRange();
13831         }
13832     },
13833     getParentElement: function()
13834     {
13835         
13836         this.assignDocWin();
13837         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13838         
13839         var range = this.createRange(sel);
13840          
13841         try {
13842             var p = range.commonAncestorContainer;
13843             while (p.nodeType == 3) { // text node
13844                 p = p.parentNode;
13845             }
13846             return p;
13847         } catch (e) {
13848             return null;
13849         }
13850     
13851     },
13852     /***
13853      *
13854      * Range intersection.. the hard stuff...
13855      *  '-1' = before
13856      *  '0' = hits..
13857      *  '1' = after.
13858      *         [ -- selected range --- ]
13859      *   [fail]                        [fail]
13860      *
13861      *    basically..
13862      *      if end is before start or  hits it. fail.
13863      *      if start is after end or hits it fail.
13864      *
13865      *   if either hits (but other is outside. - then it's not 
13866      *   
13867      *    
13868      **/
13869     
13870     
13871     // @see http://www.thismuchiknow.co.uk/?p=64.
13872     rangeIntersectsNode : function(range, node)
13873     {
13874         var nodeRange = node.ownerDocument.createRange();
13875         try {
13876             nodeRange.selectNode(node);
13877         } catch (e) {
13878             nodeRange.selectNodeContents(node);
13879         }
13880     
13881         var rangeStartRange = range.cloneRange();
13882         rangeStartRange.collapse(true);
13883     
13884         var rangeEndRange = range.cloneRange();
13885         rangeEndRange.collapse(false);
13886     
13887         var nodeStartRange = nodeRange.cloneRange();
13888         nodeStartRange.collapse(true);
13889     
13890         var nodeEndRange = nodeRange.cloneRange();
13891         nodeEndRange.collapse(false);
13892     
13893         return rangeStartRange.compareBoundaryPoints(
13894                  Range.START_TO_START, nodeEndRange) == -1 &&
13895                rangeEndRange.compareBoundaryPoints(
13896                  Range.START_TO_START, nodeStartRange) == 1;
13897         
13898          
13899     },
13900     rangeCompareNode : function(range, node)
13901     {
13902         var nodeRange = node.ownerDocument.createRange();
13903         try {
13904             nodeRange.selectNode(node);
13905         } catch (e) {
13906             nodeRange.selectNodeContents(node);
13907         }
13908         
13909         
13910         range.collapse(true);
13911     
13912         nodeRange.collapse(true);
13913      
13914         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13915         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13916          
13917         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13918         
13919         var nodeIsBefore   =  ss == 1;
13920         var nodeIsAfter    = ee == -1;
13921         
13922         if (nodeIsBefore && nodeIsAfter)
13923             return 0; // outer
13924         if (!nodeIsBefore && nodeIsAfter)
13925             return 1; //right trailed.
13926         
13927         if (nodeIsBefore && !nodeIsAfter)
13928             return 2;  // left trailed.
13929         // fully contined.
13930         return 3;
13931     },
13932
13933     // private? - in a new class?
13934     cleanUpPaste :  function()
13935     {
13936         // cleans up the whole document..
13937         Roo.log('cleanuppaste');
13938         
13939         this.cleanUpChildren(this.doc.body);
13940         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13941         if (clean != this.doc.body.innerHTML) {
13942             this.doc.body.innerHTML = clean;
13943         }
13944         
13945     },
13946     
13947     cleanWordChars : function(input) {// change the chars to hex code
13948         var he = Roo.HtmlEditorCore;
13949         
13950         var output = input;
13951         Roo.each(he.swapCodes, function(sw) { 
13952             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13953             
13954             output = output.replace(swapper, sw[1]);
13955         });
13956         
13957         return output;
13958     },
13959     
13960     
13961     cleanUpChildren : function (n)
13962     {
13963         if (!n.childNodes.length) {
13964             return;
13965         }
13966         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13967            this.cleanUpChild(n.childNodes[i]);
13968         }
13969     },
13970     
13971     
13972         
13973     
13974     cleanUpChild : function (node)
13975     {
13976         var ed = this;
13977         //console.log(node);
13978         if (node.nodeName == "#text") {
13979             // clean up silly Windows -- stuff?
13980             return; 
13981         }
13982         if (node.nodeName == "#comment") {
13983             node.parentNode.removeChild(node);
13984             // clean up silly Windows -- stuff?
13985             return; 
13986         }
13987         
13988         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13989             // remove node.
13990             node.parentNode.removeChild(node);
13991             return;
13992             
13993         }
13994         
13995         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13996         
13997         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13998         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13999         
14000         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14001         //    remove_keep_children = true;
14002         //}
14003         
14004         if (remove_keep_children) {
14005             this.cleanUpChildren(node);
14006             // inserts everything just before this node...
14007             while (node.childNodes.length) {
14008                 var cn = node.childNodes[0];
14009                 node.removeChild(cn);
14010                 node.parentNode.insertBefore(cn, node);
14011             }
14012             node.parentNode.removeChild(node);
14013             return;
14014         }
14015         
14016         if (!node.attributes || !node.attributes.length) {
14017             this.cleanUpChildren(node);
14018             return;
14019         }
14020         
14021         function cleanAttr(n,v)
14022         {
14023             
14024             if (v.match(/^\./) || v.match(/^\//)) {
14025                 return;
14026             }
14027             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14028                 return;
14029             }
14030             if (v.match(/^#/)) {
14031                 return;
14032             }
14033 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14034             node.removeAttribute(n);
14035             
14036         }
14037         
14038         function cleanStyle(n,v)
14039         {
14040             if (v.match(/expression/)) { //XSS?? should we even bother..
14041                 node.removeAttribute(n);
14042                 return;
14043             }
14044             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14045             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14046             
14047             
14048             var parts = v.split(/;/);
14049             var clean = [];
14050             
14051             Roo.each(parts, function(p) {
14052                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14053                 if (!p.length) {
14054                     return true;
14055                 }
14056                 var l = p.split(':').shift().replace(/\s+/g,'');
14057                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14058                 
14059                 if ( cblack.indexOf(l) > -1) {
14060 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14061                     //node.removeAttribute(n);
14062                     return true;
14063                 }
14064                 //Roo.log()
14065                 // only allow 'c whitelisted system attributes'
14066                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14067 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14068                     //node.removeAttribute(n);
14069                     return true;
14070                 }
14071                 
14072                 
14073                  
14074                 
14075                 clean.push(p);
14076                 return true;
14077             });
14078             if (clean.length) { 
14079                 node.setAttribute(n, clean.join(';'));
14080             } else {
14081                 node.removeAttribute(n);
14082             }
14083             
14084         }
14085         
14086         
14087         for (var i = node.attributes.length-1; i > -1 ; i--) {
14088             var a = node.attributes[i];
14089             //console.log(a);
14090             
14091             if (a.name.toLowerCase().substr(0,2)=='on')  {
14092                 node.removeAttribute(a.name);
14093                 continue;
14094             }
14095             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14096                 node.removeAttribute(a.name);
14097                 continue;
14098             }
14099             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14100                 cleanAttr(a.name,a.value); // fixme..
14101                 continue;
14102             }
14103             if (a.name == 'style') {
14104                 cleanStyle(a.name,a.value);
14105                 continue;
14106             }
14107             /// clean up MS crap..
14108             // tecnically this should be a list of valid class'es..
14109             
14110             
14111             if (a.name == 'class') {
14112                 if (a.value.match(/^Mso/)) {
14113                     node.className = '';
14114                 }
14115                 
14116                 if (a.value.match(/body/)) {
14117                     node.className = '';
14118                 }
14119                 continue;
14120             }
14121             
14122             // style cleanup!?
14123             // class cleanup?
14124             
14125         }
14126         
14127         
14128         this.cleanUpChildren(node);
14129         
14130         
14131     }
14132     
14133     
14134     // hide stuff that is not compatible
14135     /**
14136      * @event blur
14137      * @hide
14138      */
14139     /**
14140      * @event change
14141      * @hide
14142      */
14143     /**
14144      * @event focus
14145      * @hide
14146      */
14147     /**
14148      * @event specialkey
14149      * @hide
14150      */
14151     /**
14152      * @cfg {String} fieldClass @hide
14153      */
14154     /**
14155      * @cfg {String} focusClass @hide
14156      */
14157     /**
14158      * @cfg {String} autoCreate @hide
14159      */
14160     /**
14161      * @cfg {String} inputType @hide
14162      */
14163     /**
14164      * @cfg {String} invalidClass @hide
14165      */
14166     /**
14167      * @cfg {String} invalidText @hide
14168      */
14169     /**
14170      * @cfg {String} msgFx @hide
14171      */
14172     /**
14173      * @cfg {String} validateOnBlur @hide
14174      */
14175 });
14176
14177 Roo.HtmlEditorCore.white = [
14178         'area', 'br', 'img', 'input', 'hr', 'wbr',
14179         
14180        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14181        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14182        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14183        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14184        'table',   'ul',         'xmp', 
14185        
14186        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14187       'thead',   'tr', 
14188      
14189       'dir', 'menu', 'ol', 'ul', 'dl',
14190        
14191       'embed',  'object'
14192 ];
14193
14194
14195 Roo.HtmlEditorCore.black = [
14196     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14197         'applet', // 
14198         'base',   'basefont', 'bgsound', 'blink',  'body', 
14199         'frame',  'frameset', 'head',    'html',   'ilayer', 
14200         'iframe', 'layer',  'link',     'meta',    'object',   
14201         'script', 'style' ,'title',  'xml' // clean later..
14202 ];
14203 Roo.HtmlEditorCore.clean = [
14204     'script', 'style', 'title', 'xml'
14205 ];
14206 Roo.HtmlEditorCore.remove = [
14207     'font'
14208 ];
14209 // attributes..
14210
14211 Roo.HtmlEditorCore.ablack = [
14212     'on'
14213 ];
14214     
14215 Roo.HtmlEditorCore.aclean = [ 
14216     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14217 ];
14218
14219 // protocols..
14220 Roo.HtmlEditorCore.pwhite= [
14221         'http',  'https',  'mailto'
14222 ];
14223
14224 // white listed style attributes.
14225 Roo.HtmlEditorCore.cwhite= [
14226       //  'text-align', /// default is to allow most things..
14227       
14228          
14229 //        'font-size'//??
14230 ];
14231
14232 // black listed style attributes.
14233 Roo.HtmlEditorCore.cblack= [
14234       //  'font-size' -- this can be set by the project 
14235 ];
14236
14237
14238 Roo.HtmlEditorCore.swapCodes   =[ 
14239     [    8211, "--" ], 
14240     [    8212, "--" ], 
14241     [    8216,  "'" ],  
14242     [    8217, "'" ],  
14243     [    8220, '"' ],  
14244     [    8221, '"' ],  
14245     [    8226, "*" ],  
14246     [    8230, "..." ]
14247 ]; 
14248
14249     /*
14250  * - LGPL
14251  *
14252  * HtmlEditor
14253  * 
14254  */
14255
14256 /**
14257  * @class Roo.bootstrap.HtmlEditor
14258  * @extends Roo.bootstrap.TextArea
14259  * Bootstrap HtmlEditor class
14260
14261  * @constructor
14262  * Create a new HtmlEditor
14263  * @param {Object} config The config object
14264  */
14265
14266 Roo.bootstrap.HtmlEditor = function(config){
14267     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14268     if (!this.toolbars) {
14269         this.toolbars = [];
14270     }
14271     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14272     this.addEvents({
14273             /**
14274              * @event initialize
14275              * Fires when the editor is fully initialized (including the iframe)
14276              * @param {HtmlEditor} this
14277              */
14278             initialize: true,
14279             /**
14280              * @event activate
14281              * Fires when the editor is first receives the focus. Any insertion must wait
14282              * until after this event.
14283              * @param {HtmlEditor} this
14284              */
14285             activate: true,
14286              /**
14287              * @event beforesync
14288              * Fires before the textarea is updated with content from the editor iframe. Return false
14289              * to cancel the sync.
14290              * @param {HtmlEditor} this
14291              * @param {String} html
14292              */
14293             beforesync: true,
14294              /**
14295              * @event beforepush
14296              * Fires before the iframe editor is updated with content from the textarea. Return false
14297              * to cancel the push.
14298              * @param {HtmlEditor} this
14299              * @param {String} html
14300              */
14301             beforepush: true,
14302              /**
14303              * @event sync
14304              * Fires when the textarea is updated with content from the editor iframe.
14305              * @param {HtmlEditor} this
14306              * @param {String} html
14307              */
14308             sync: true,
14309              /**
14310              * @event push
14311              * Fires when the iframe editor is updated with content from the textarea.
14312              * @param {HtmlEditor} this
14313              * @param {String} html
14314              */
14315             push: true,
14316              /**
14317              * @event editmodechange
14318              * Fires when the editor switches edit modes
14319              * @param {HtmlEditor} this
14320              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14321              */
14322             editmodechange: true,
14323             /**
14324              * @event editorevent
14325              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14326              * @param {HtmlEditor} this
14327              */
14328             editorevent: true,
14329             /**
14330              * @event firstfocus
14331              * Fires when on first focus - needed by toolbars..
14332              * @param {HtmlEditor} this
14333              */
14334             firstfocus: true,
14335             /**
14336              * @event autosave
14337              * Auto save the htmlEditor value as a file into Events
14338              * @param {HtmlEditor} this
14339              */
14340             autosave: true,
14341             /**
14342              * @event savedpreview
14343              * preview the saved version of htmlEditor
14344              * @param {HtmlEditor} this
14345              */
14346             savedpreview: true
14347         });
14348 };
14349
14350
14351 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14352     
14353     
14354       /**
14355      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14356      */
14357     toolbars : false,
14358    
14359      /**
14360      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14361      *                        Roo.resizable.
14362      */
14363     resizable : false,
14364      /**
14365      * @cfg {Number} height (in pixels)
14366      */   
14367     height: 300,
14368    /**
14369      * @cfg {Number} width (in pixels)
14370      */   
14371     width: false,
14372     
14373     /**
14374      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14375      * 
14376      */
14377     stylesheets: false,
14378     
14379     // id of frame..
14380     frameId: false,
14381     
14382     // private properties
14383     validationEvent : false,
14384     deferHeight: true,
14385     initialized : false,
14386     activated : false,
14387     
14388     onFocus : Roo.emptyFn,
14389     iframePad:3,
14390     hideMode:'offsets',
14391     
14392     
14393     tbContainer : false,
14394     
14395     toolbarContainer :function() {
14396         return this.wrap.select('.x-html-editor-tb',true).first();
14397     },
14398
14399     /**
14400      * Protected method that will not generally be called directly. It
14401      * is called when the editor creates its toolbar. Override this method if you need to
14402      * add custom toolbar buttons.
14403      * @param {HtmlEditor} editor
14404      */
14405     createToolbar : function(){
14406         
14407         Roo.log("create toolbars");
14408         
14409         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14410         this.toolbars[0].render(this.toolbarContainer());
14411         
14412         return;
14413         
14414 //        if (!editor.toolbars || !editor.toolbars.length) {
14415 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14416 //        }
14417 //        
14418 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14419 //            editor.toolbars[i] = Roo.factory(
14420 //                    typeof(editor.toolbars[i]) == 'string' ?
14421 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14422 //                Roo.bootstrap.HtmlEditor);
14423 //            editor.toolbars[i].init(editor);
14424 //        }
14425     },
14426
14427      
14428     // private
14429     onRender : function(ct, position)
14430     {
14431        // Roo.log("Call onRender: " + this.xtype);
14432         var _t = this;
14433         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14434       
14435         this.wrap = this.inputEl().wrap({
14436             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14437         });
14438         
14439         this.editorcore.onRender(ct, position);
14440          
14441         if (this.resizable) {
14442             this.resizeEl = new Roo.Resizable(this.wrap, {
14443                 pinned : true,
14444                 wrap: true,
14445                 dynamic : true,
14446                 minHeight : this.height,
14447                 height: this.height,
14448                 handles : this.resizable,
14449                 width: this.width,
14450                 listeners : {
14451                     resize : function(r, w, h) {
14452                         _t.onResize(w,h); // -something
14453                     }
14454                 }
14455             });
14456             
14457         }
14458         this.createToolbar(this);
14459        
14460         
14461         if(!this.width && this.resizable){
14462             this.setSize(this.wrap.getSize());
14463         }
14464         if (this.resizeEl) {
14465             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14466             // should trigger onReize..
14467         }
14468         
14469     },
14470
14471     // private
14472     onResize : function(w, h)
14473     {
14474         Roo.log('resize: ' +w + ',' + h );
14475         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14476         var ew = false;
14477         var eh = false;
14478         
14479         if(this.inputEl() ){
14480             if(typeof w == 'number'){
14481                 var aw = w - this.wrap.getFrameWidth('lr');
14482                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14483                 ew = aw;
14484             }
14485             if(typeof h == 'number'){
14486                  var tbh = -11;  // fixme it needs to tool bar size!
14487                 for (var i =0; i < this.toolbars.length;i++) {
14488                     // fixme - ask toolbars for heights?
14489                     tbh += this.toolbars[i].el.getHeight();
14490                     //if (this.toolbars[i].footer) {
14491                     //    tbh += this.toolbars[i].footer.el.getHeight();
14492                     //}
14493                 }
14494               
14495                 
14496                 
14497                 
14498                 
14499                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14500                 ah -= 5; // knock a few pixes off for look..
14501                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14502                 var eh = ah;
14503             }
14504         }
14505         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14506         this.editorcore.onResize(ew,eh);
14507         
14508     },
14509
14510     /**
14511      * Toggles the editor between standard and source edit mode.
14512      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14513      */
14514     toggleSourceEdit : function(sourceEditMode)
14515     {
14516         this.editorcore.toggleSourceEdit(sourceEditMode);
14517         
14518         if(this.editorcore.sourceEditMode){
14519             Roo.log('editor - showing textarea');
14520             
14521 //            Roo.log('in');
14522 //            Roo.log(this.syncValue());
14523             this.syncValue();
14524             this.inputEl().removeClass('hide');
14525             this.inputEl().dom.removeAttribute('tabIndex');
14526             this.inputEl().focus();
14527         }else{
14528             Roo.log('editor - hiding textarea');
14529 //            Roo.log('out')
14530 //            Roo.log(this.pushValue()); 
14531             this.pushValue();
14532             
14533             this.inputEl().addClass('hide');
14534             this.inputEl().dom.setAttribute('tabIndex', -1);
14535             //this.deferFocus();
14536         }
14537          
14538         if(this.resizable){
14539             this.setSize(this.wrap.getSize());
14540         }
14541         
14542         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14543     },
14544  
14545     // private (for BoxComponent)
14546     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14547
14548     // private (for BoxComponent)
14549     getResizeEl : function(){
14550         return this.wrap;
14551     },
14552
14553     // private (for BoxComponent)
14554     getPositionEl : function(){
14555         return this.wrap;
14556     },
14557
14558     // private
14559     initEvents : function(){
14560         this.originalValue = this.getValue();
14561     },
14562
14563 //    /**
14564 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14565 //     * @method
14566 //     */
14567 //    markInvalid : Roo.emptyFn,
14568 //    /**
14569 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14570 //     * @method
14571 //     */
14572 //    clearInvalid : Roo.emptyFn,
14573
14574     setValue : function(v){
14575         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14576         this.editorcore.pushValue();
14577     },
14578
14579      
14580     // private
14581     deferFocus : function(){
14582         this.focus.defer(10, this);
14583     },
14584
14585     // doc'ed in Field
14586     focus : function(){
14587         this.editorcore.focus();
14588         
14589     },
14590       
14591
14592     // private
14593     onDestroy : function(){
14594         
14595         
14596         
14597         if(this.rendered){
14598             
14599             for (var i =0; i < this.toolbars.length;i++) {
14600                 // fixme - ask toolbars for heights?
14601                 this.toolbars[i].onDestroy();
14602             }
14603             
14604             this.wrap.dom.innerHTML = '';
14605             this.wrap.remove();
14606         }
14607     },
14608
14609     // private
14610     onFirstFocus : function(){
14611         //Roo.log("onFirstFocus");
14612         this.editorcore.onFirstFocus();
14613          for (var i =0; i < this.toolbars.length;i++) {
14614             this.toolbars[i].onFirstFocus();
14615         }
14616         
14617     },
14618     
14619     // private
14620     syncValue : function()
14621     {   
14622         this.editorcore.syncValue();
14623     },
14624     
14625     pushValue : function()
14626     {   
14627         this.editorcore.pushValue();
14628     }
14629      
14630     
14631     // hide stuff that is not compatible
14632     /**
14633      * @event blur
14634      * @hide
14635      */
14636     /**
14637      * @event change
14638      * @hide
14639      */
14640     /**
14641      * @event focus
14642      * @hide
14643      */
14644     /**
14645      * @event specialkey
14646      * @hide
14647      */
14648     /**
14649      * @cfg {String} fieldClass @hide
14650      */
14651     /**
14652      * @cfg {String} focusClass @hide
14653      */
14654     /**
14655      * @cfg {String} autoCreate @hide
14656      */
14657     /**
14658      * @cfg {String} inputType @hide
14659      */
14660     /**
14661      * @cfg {String} invalidClass @hide
14662      */
14663     /**
14664      * @cfg {String} invalidText @hide
14665      */
14666     /**
14667      * @cfg {String} msgFx @hide
14668      */
14669     /**
14670      * @cfg {String} validateOnBlur @hide
14671      */
14672 });
14673  
14674     
14675    
14676    
14677    
14678       
14679
14680 /**
14681  * @class Roo.bootstrap.HtmlEditorToolbar1
14682  * Basic Toolbar
14683  * 
14684  * Usage:
14685  *
14686  new Roo.bootstrap.HtmlEditor({
14687     ....
14688     toolbars : [
14689         new Roo.bootstrap.HtmlEditorToolbar1({
14690             disable : { fonts: 1 , format: 1, ..., ... , ...],
14691             btns : [ .... ]
14692         })
14693     }
14694      
14695  * 
14696  * @cfg {Object} disable List of elements to disable..
14697  * @cfg {Array} btns List of additional buttons.
14698  * 
14699  * 
14700  * NEEDS Extra CSS? 
14701  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14702  */
14703  
14704 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14705 {
14706     
14707     Roo.apply(this, config);
14708     
14709     // default disabled, based on 'good practice'..
14710     this.disable = this.disable || {};
14711     Roo.applyIf(this.disable, {
14712         fontSize : true,
14713         colors : true,
14714         specialElements : true
14715     });
14716     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14717     
14718     this.editor = config.editor;
14719     this.editorcore = config.editor.editorcore;
14720     
14721     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14722     
14723     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14724     // dont call parent... till later.
14725 }
14726 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14727     
14728     
14729     bar : true,
14730     
14731     editor : false,
14732     editorcore : false,
14733     
14734     
14735     formats : [
14736         "p" ,  
14737         "h1","h2","h3","h4","h5","h6", 
14738         "pre", "code", 
14739         "abbr", "acronym", "address", "cite", "samp", "var",
14740         'div','span'
14741     ],
14742     
14743     onRender : function(ct, position)
14744     {
14745        // Roo.log("Call onRender: " + this.xtype);
14746         
14747        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14748        Roo.log(this.el);
14749        this.el.dom.style.marginBottom = '0';
14750        var _this = this;
14751        var editorcore = this.editorcore;
14752        var editor= this.editor;
14753        
14754        var children = [];
14755        var btn = function(id,cmd , toggle, handler){
14756        
14757             var  event = toggle ? 'toggle' : 'click';
14758        
14759             var a = {
14760                 size : 'sm',
14761                 xtype: 'Button',
14762                 xns: Roo.bootstrap,
14763                 glyphicon : id,
14764                 cmd : id || cmd,
14765                 enableToggle:toggle !== false,
14766                 //html : 'submit'
14767                 pressed : toggle ? false : null,
14768                 listeners : {}
14769             }
14770             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14771                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14772             }
14773             children.push(a);
14774             return a;
14775        }
14776         
14777         var style = {
14778                 xtype: 'Button',
14779                 size : 'sm',
14780                 xns: Roo.bootstrap,
14781                 glyphicon : 'font',
14782                 //html : 'submit'
14783                 menu : {
14784                     xtype: 'Menu',
14785                     xns: Roo.bootstrap,
14786                     items:  []
14787                 }
14788         };
14789         Roo.each(this.formats, function(f) {
14790             style.menu.items.push({
14791                 xtype :'MenuItem',
14792                 xns: Roo.bootstrap,
14793                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14794                 tagname : f,
14795                 listeners : {
14796                     click : function()
14797                     {
14798                         editorcore.insertTag(this.tagname);
14799                         editor.focus();
14800                     }
14801                 }
14802                 
14803             });
14804         });
14805          children.push(style);   
14806             
14807             
14808         btn('bold',false,true);
14809         btn('italic',false,true);
14810         btn('align-left', 'justifyleft',true);
14811         btn('align-center', 'justifycenter',true);
14812         btn('align-right' , 'justifyright',true);
14813         btn('link', false, false, function(btn) {
14814             //Roo.log("create link?");
14815             var url = prompt(this.createLinkText, this.defaultLinkValue);
14816             if(url && url != 'http:/'+'/'){
14817                 this.editorcore.relayCmd('createlink', url);
14818             }
14819         }),
14820         btn('list','insertunorderedlist',true);
14821         btn('pencil', false,true, function(btn){
14822                 Roo.log(this);
14823                 
14824                 this.toggleSourceEdit(btn.pressed);
14825         });
14826         /*
14827         var cog = {
14828                 xtype: 'Button',
14829                 size : 'sm',
14830                 xns: Roo.bootstrap,
14831                 glyphicon : 'cog',
14832                 //html : 'submit'
14833                 menu : {
14834                     xtype: 'Menu',
14835                     xns: Roo.bootstrap,
14836                     items:  []
14837                 }
14838         };
14839         
14840         cog.menu.items.push({
14841             xtype :'MenuItem',
14842             xns: Roo.bootstrap,
14843             html : Clean styles,
14844             tagname : f,
14845             listeners : {
14846                 click : function()
14847                 {
14848                     editorcore.insertTag(this.tagname);
14849                     editor.focus();
14850                 }
14851             }
14852             
14853         });
14854        */
14855         
14856          
14857        this.xtype = 'Navbar';
14858         
14859         for(var i=0;i< children.length;i++) {
14860             
14861             this.buttons.add(this.addxtypeChild(children[i]));
14862             
14863         }
14864         
14865         editor.on('editorevent', this.updateToolbar, this);
14866     },
14867     onBtnClick : function(id)
14868     {
14869        this.editorcore.relayCmd(id);
14870        this.editorcore.focus();
14871     },
14872     
14873     /**
14874      * Protected method that will not generally be called directly. It triggers
14875      * a toolbar update by reading the markup state of the current selection in the editor.
14876      */
14877     updateToolbar: function(){
14878
14879         if(!this.editorcore.activated){
14880             this.editor.onFirstFocus(); // is this neeed?
14881             return;
14882         }
14883
14884         var btns = this.buttons; 
14885         var doc = this.editorcore.doc;
14886         btns.get('bold').setActive(doc.queryCommandState('bold'));
14887         btns.get('italic').setActive(doc.queryCommandState('italic'));
14888         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14889         
14890         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14891         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14892         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14893         
14894         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14895         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14896          /*
14897         
14898         var ans = this.editorcore.getAllAncestors();
14899         if (this.formatCombo) {
14900             
14901             
14902             var store = this.formatCombo.store;
14903             this.formatCombo.setValue("");
14904             for (var i =0; i < ans.length;i++) {
14905                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14906                     // select it..
14907                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14908                     break;
14909                 }
14910             }
14911         }
14912         
14913         
14914         
14915         // hides menus... - so this cant be on a menu...
14916         Roo.bootstrap.MenuMgr.hideAll();
14917         */
14918         Roo.bootstrap.MenuMgr.hideAll();
14919         //this.editorsyncValue();
14920     },
14921     onFirstFocus: function() {
14922         this.buttons.each(function(item){
14923            item.enable();
14924         });
14925     },
14926     toggleSourceEdit : function(sourceEditMode){
14927         
14928           
14929         if(sourceEditMode){
14930             Roo.log("disabling buttons");
14931            this.buttons.each( function(item){
14932                 if(item.cmd != 'pencil'){
14933                     item.disable();
14934                 }
14935             });
14936           
14937         }else{
14938             Roo.log("enabling buttons");
14939             if(this.editorcore.initialized){
14940                 this.buttons.each( function(item){
14941                     item.enable();
14942                 });
14943             }
14944             
14945         }
14946         Roo.log("calling toggole on editor");
14947         // tell the editor that it's been pressed..
14948         this.editor.toggleSourceEdit(sourceEditMode);
14949        
14950     }
14951 });
14952
14953
14954
14955
14956
14957 /**
14958  * @class Roo.bootstrap.Table.AbstractSelectionModel
14959  * @extends Roo.util.Observable
14960  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14961  * implemented by descendant classes.  This class should not be directly instantiated.
14962  * @constructor
14963  */
14964 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14965     this.locked = false;
14966     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14967 };
14968
14969
14970 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14971     /** @ignore Called by the grid automatically. Do not call directly. */
14972     init : function(grid){
14973         this.grid = grid;
14974         this.initEvents();
14975     },
14976
14977     /**
14978      * Locks the selections.
14979      */
14980     lock : function(){
14981         this.locked = true;
14982     },
14983
14984     /**
14985      * Unlocks the selections.
14986      */
14987     unlock : function(){
14988         this.locked = false;
14989     },
14990
14991     /**
14992      * Returns true if the selections are locked.
14993      * @return {Boolean}
14994      */
14995     isLocked : function(){
14996         return this.locked;
14997     }
14998 });
14999 /**
15000  * @class Roo.bootstrap.Table.ColumnModel
15001  * @extends Roo.util.Observable
15002  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15003  * the columns in the table.
15004  
15005  * @constructor
15006  * @param {Object} config An Array of column config objects. See this class's
15007  * config objects for details.
15008 */
15009 Roo.bootstrap.Table.ColumnModel = function(config){
15010         /**
15011      * The config passed into the constructor
15012      */
15013     this.config = config;
15014     this.lookup = {};
15015
15016     // if no id, create one
15017     // if the column does not have a dataIndex mapping,
15018     // map it to the order it is in the config
15019     for(var i = 0, len = config.length; i < len; i++){
15020         var c = config[i];
15021         if(typeof c.dataIndex == "undefined"){
15022             c.dataIndex = i;
15023         }
15024         if(typeof c.renderer == "string"){
15025             c.renderer = Roo.util.Format[c.renderer];
15026         }
15027         if(typeof c.id == "undefined"){
15028             c.id = Roo.id();
15029         }
15030 //        if(c.editor && c.editor.xtype){
15031 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15032 //        }
15033 //        if(c.editor && c.editor.isFormField){
15034 //            c.editor = new Roo.grid.GridEditor(c.editor);
15035 //        }
15036
15037         this.lookup[c.id] = c;
15038     }
15039
15040     /**
15041      * The width of columns which have no width specified (defaults to 100)
15042      * @type Number
15043      */
15044     this.defaultWidth = 100;
15045
15046     /**
15047      * Default sortable of columns which have no sortable specified (defaults to false)
15048      * @type Boolean
15049      */
15050     this.defaultSortable = false;
15051
15052     this.addEvents({
15053         /**
15054              * @event widthchange
15055              * Fires when the width of a column changes.
15056              * @param {ColumnModel} this
15057              * @param {Number} columnIndex The column index
15058              * @param {Number} newWidth The new width
15059              */
15060             "widthchange": true,
15061         /**
15062              * @event headerchange
15063              * Fires when the text of a header changes.
15064              * @param {ColumnModel} this
15065              * @param {Number} columnIndex The column index
15066              * @param {Number} newText The new header text
15067              */
15068             "headerchange": true,
15069         /**
15070              * @event hiddenchange
15071              * Fires when a column is hidden or "unhidden".
15072              * @param {ColumnModel} this
15073              * @param {Number} columnIndex The column index
15074              * @param {Boolean} hidden true if hidden, false otherwise
15075              */
15076             "hiddenchange": true,
15077             /**
15078          * @event columnmoved
15079          * Fires when a column is moved.
15080          * @param {ColumnModel} this
15081          * @param {Number} oldIndex
15082          * @param {Number} newIndex
15083          */
15084         "columnmoved" : true,
15085         /**
15086          * @event columlockchange
15087          * Fires when a column's locked state is changed
15088          * @param {ColumnModel} this
15089          * @param {Number} colIndex
15090          * @param {Boolean} locked true if locked
15091          */
15092         "columnlockchange" : true
15093     });
15094     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15095 };
15096 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15097     /**
15098      * @cfg {String} header The header text to display in the Grid view.
15099      */
15100     /**
15101      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15102      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15103      * specified, the column's index is used as an index into the Record's data Array.
15104      */
15105     /**
15106      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15107      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15108      */
15109     /**
15110      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15111      * Defaults to the value of the {@link #defaultSortable} property.
15112      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15113      */
15114     /**
15115      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
15116      */
15117     /**
15118      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
15119      */
15120     /**
15121      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15122      */
15123     /**
15124      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15125      */
15126     /**
15127      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15128      * given the cell's data value. See {@link #setRenderer}. If not specified, the
15129      * default renderer uses the raw data value.
15130      */
15131     /**
15132      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
15133      */
15134
15135     /**
15136      * Returns the id of the column at the specified index.
15137      * @param {Number} index The column index
15138      * @return {String} the id
15139      */
15140     getColumnId : function(index){
15141         return this.config[index].id;
15142     },
15143
15144     /**
15145      * Returns the column for a specified id.
15146      * @param {String} id The column id
15147      * @return {Object} the column
15148      */
15149     getColumnById : function(id){
15150         return this.lookup[id];
15151     },
15152
15153     
15154     /**
15155      * Returns the column for a specified dataIndex.
15156      * @param {String} dataIndex The column dataIndex
15157      * @return {Object|Boolean} the column or false if not found
15158      */
15159     getColumnByDataIndex: function(dataIndex){
15160         var index = this.findColumnIndex(dataIndex);
15161         return index > -1 ? this.config[index] : false;
15162     },
15163     
15164     /**
15165      * Returns the index for a specified column id.
15166      * @param {String} id The column id
15167      * @return {Number} the index, or -1 if not found
15168      */
15169     getIndexById : function(id){
15170         for(var i = 0, len = this.config.length; i < len; i++){
15171             if(this.config[i].id == id){
15172                 return i;
15173             }
15174         }
15175         return -1;
15176     },
15177     
15178     /**
15179      * Returns the index for a specified column dataIndex.
15180      * @param {String} dataIndex The column dataIndex
15181      * @return {Number} the index, or -1 if not found
15182      */
15183     
15184     findColumnIndex : function(dataIndex){
15185         for(var i = 0, len = this.config.length; i < len; i++){
15186             if(this.config[i].dataIndex == dataIndex){
15187                 return i;
15188             }
15189         }
15190         return -1;
15191     },
15192     
15193     
15194     moveColumn : function(oldIndex, newIndex){
15195         var c = this.config[oldIndex];
15196         this.config.splice(oldIndex, 1);
15197         this.config.splice(newIndex, 0, c);
15198         this.dataMap = null;
15199         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15200     },
15201
15202     isLocked : function(colIndex){
15203         return this.config[colIndex].locked === true;
15204     },
15205
15206     setLocked : function(colIndex, value, suppressEvent){
15207         if(this.isLocked(colIndex) == value){
15208             return;
15209         }
15210         this.config[colIndex].locked = value;
15211         if(!suppressEvent){
15212             this.fireEvent("columnlockchange", this, colIndex, value);
15213         }
15214     },
15215
15216     getTotalLockedWidth : function(){
15217         var totalWidth = 0;
15218         for(var i = 0; i < this.config.length; i++){
15219             if(this.isLocked(i) && !this.isHidden(i)){
15220                 this.totalWidth += this.getColumnWidth(i);
15221             }
15222         }
15223         return totalWidth;
15224     },
15225
15226     getLockedCount : function(){
15227         for(var i = 0, len = this.config.length; i < len; i++){
15228             if(!this.isLocked(i)){
15229                 return i;
15230             }
15231         }
15232     },
15233
15234     /**
15235      * Returns the number of columns.
15236      * @return {Number}
15237      */
15238     getColumnCount : function(visibleOnly){
15239         if(visibleOnly === true){
15240             var c = 0;
15241             for(var i = 0, len = this.config.length; i < len; i++){
15242                 if(!this.isHidden(i)){
15243                     c++;
15244                 }
15245             }
15246             return c;
15247         }
15248         return this.config.length;
15249     },
15250
15251     /**
15252      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15253      * @param {Function} fn
15254      * @param {Object} scope (optional)
15255      * @return {Array} result
15256      */
15257     getColumnsBy : function(fn, scope){
15258         var r = [];
15259         for(var i = 0, len = this.config.length; i < len; i++){
15260             var c = this.config[i];
15261             if(fn.call(scope||this, c, i) === true){
15262                 r[r.length] = c;
15263             }
15264         }
15265         return r;
15266     },
15267
15268     /**
15269      * Returns true if the specified column is sortable.
15270      * @param {Number} col The column index
15271      * @return {Boolean}
15272      */
15273     isSortable : function(col){
15274         if(typeof this.config[col].sortable == "undefined"){
15275             return this.defaultSortable;
15276         }
15277         return this.config[col].sortable;
15278     },
15279
15280     /**
15281      * Returns the rendering (formatting) function defined for the column.
15282      * @param {Number} col The column index.
15283      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15284      */
15285     getRenderer : function(col){
15286         if(!this.config[col].renderer){
15287             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15288         }
15289         return this.config[col].renderer;
15290     },
15291
15292     /**
15293      * Sets the rendering (formatting) function for a column.
15294      * @param {Number} col The column index
15295      * @param {Function} fn The function to use to process the cell's raw data
15296      * to return HTML markup for the grid view. The render function is called with
15297      * the following parameters:<ul>
15298      * <li>Data value.</li>
15299      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15300      * <li>css A CSS style string to apply to the table cell.</li>
15301      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15302      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15303      * <li>Row index</li>
15304      * <li>Column index</li>
15305      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15306      */
15307     setRenderer : function(col, fn){
15308         this.config[col].renderer = fn;
15309     },
15310
15311     /**
15312      * Returns the width for the specified column.
15313      * @param {Number} col The column index
15314      * @return {Number}
15315      */
15316     getColumnWidth : function(col){
15317         return this.config[col].width * 1 || this.defaultWidth;
15318     },
15319
15320     /**
15321      * Sets the width for a column.
15322      * @param {Number} col The column index
15323      * @param {Number} width The new width
15324      */
15325     setColumnWidth : function(col, width, suppressEvent){
15326         this.config[col].width = width;
15327         this.totalWidth = null;
15328         if(!suppressEvent){
15329              this.fireEvent("widthchange", this, col, width);
15330         }
15331     },
15332
15333     /**
15334      * Returns the total width of all columns.
15335      * @param {Boolean} includeHidden True to include hidden column widths
15336      * @return {Number}
15337      */
15338     getTotalWidth : function(includeHidden){
15339         if(!this.totalWidth){
15340             this.totalWidth = 0;
15341             for(var i = 0, len = this.config.length; i < len; i++){
15342                 if(includeHidden || !this.isHidden(i)){
15343                     this.totalWidth += this.getColumnWidth(i);
15344                 }
15345             }
15346         }
15347         return this.totalWidth;
15348     },
15349
15350     /**
15351      * Returns the header for the specified column.
15352      * @param {Number} col The column index
15353      * @return {String}
15354      */
15355     getColumnHeader : function(col){
15356         return this.config[col].header;
15357     },
15358
15359     /**
15360      * Sets the header for a column.
15361      * @param {Number} col The column index
15362      * @param {String} header The new header
15363      */
15364     setColumnHeader : function(col, header){
15365         this.config[col].header = header;
15366         this.fireEvent("headerchange", this, col, header);
15367     },
15368
15369     /**
15370      * Returns the tooltip for the specified column.
15371      * @param {Number} col The column index
15372      * @return {String}
15373      */
15374     getColumnTooltip : function(col){
15375             return this.config[col].tooltip;
15376     },
15377     /**
15378      * Sets the tooltip for a column.
15379      * @param {Number} col The column index
15380      * @param {String} tooltip The new tooltip
15381      */
15382     setColumnTooltip : function(col, tooltip){
15383             this.config[col].tooltip = tooltip;
15384     },
15385
15386     /**
15387      * Returns the dataIndex for the specified column.
15388      * @param {Number} col The column index
15389      * @return {Number}
15390      */
15391     getDataIndex : function(col){
15392         return this.config[col].dataIndex;
15393     },
15394
15395     /**
15396      * Sets the dataIndex for a column.
15397      * @param {Number} col The column index
15398      * @param {Number} dataIndex The new dataIndex
15399      */
15400     setDataIndex : function(col, dataIndex){
15401         this.config[col].dataIndex = dataIndex;
15402     },
15403
15404     
15405     
15406     /**
15407      * Returns true if the cell is editable.
15408      * @param {Number} colIndex The column index
15409      * @param {Number} rowIndex The row index
15410      * @return {Boolean}
15411      */
15412     isCellEditable : function(colIndex, rowIndex){
15413         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15414     },
15415
15416     /**
15417      * Returns the editor defined for the cell/column.
15418      * return false or null to disable editing.
15419      * @param {Number} colIndex The column index
15420      * @param {Number} rowIndex The row index
15421      * @return {Object}
15422      */
15423     getCellEditor : function(colIndex, rowIndex){
15424         return this.config[colIndex].editor;
15425     },
15426
15427     /**
15428      * Sets if a column is editable.
15429      * @param {Number} col The column index
15430      * @param {Boolean} editable True if the column is editable
15431      */
15432     setEditable : function(col, editable){
15433         this.config[col].editable = editable;
15434     },
15435
15436
15437     /**
15438      * Returns true if the column is hidden.
15439      * @param {Number} colIndex The column index
15440      * @return {Boolean}
15441      */
15442     isHidden : function(colIndex){
15443         return this.config[colIndex].hidden;
15444     },
15445
15446
15447     /**
15448      * Returns true if the column width cannot be changed
15449      */
15450     isFixed : function(colIndex){
15451         return this.config[colIndex].fixed;
15452     },
15453
15454     /**
15455      * Returns true if the column can be resized
15456      * @return {Boolean}
15457      */
15458     isResizable : function(colIndex){
15459         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15460     },
15461     /**
15462      * Sets if a column is hidden.
15463      * @param {Number} colIndex The column index
15464      * @param {Boolean} hidden True if the column is hidden
15465      */
15466     setHidden : function(colIndex, hidden){
15467         this.config[colIndex].hidden = hidden;
15468         this.totalWidth = null;
15469         this.fireEvent("hiddenchange", this, colIndex, hidden);
15470     },
15471
15472     /**
15473      * Sets the editor for a column.
15474      * @param {Number} col The column index
15475      * @param {Object} editor The editor object
15476      */
15477     setEditor : function(col, editor){
15478         this.config[col].editor = editor;
15479     }
15480 });
15481
15482 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15483         if(typeof value == "string" && value.length < 1){
15484             return "&#160;";
15485         }
15486         return value;
15487 };
15488
15489 // Alias for backwards compatibility
15490 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15491
15492 /**
15493  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15494  * @class Roo.bootstrap.Table.RowSelectionModel
15495  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15496  * It supports multiple selections and keyboard selection/navigation. 
15497  * @constructor
15498  * @param {Object} config
15499  */
15500
15501 Roo.bootstrap.Table.RowSelectionModel = function(config){
15502     Roo.apply(this, config);
15503     this.selections = new Roo.util.MixedCollection(false, function(o){
15504         return o.id;
15505     });
15506
15507     this.last = false;
15508     this.lastActive = false;
15509
15510     this.addEvents({
15511         /**
15512              * @event selectionchange
15513              * Fires when the selection changes
15514              * @param {SelectionModel} this
15515              */
15516             "selectionchange" : true,
15517         /**
15518              * @event afterselectionchange
15519              * Fires after the selection changes (eg. by key press or clicking)
15520              * @param {SelectionModel} this
15521              */
15522             "afterselectionchange" : true,
15523         /**
15524              * @event beforerowselect
15525              * Fires when a row is selected being selected, return false to cancel.
15526              * @param {SelectionModel} this
15527              * @param {Number} rowIndex The selected index
15528              * @param {Boolean} keepExisting False if other selections will be cleared
15529              */
15530             "beforerowselect" : true,
15531         /**
15532              * @event rowselect
15533              * Fires when a row is selected.
15534              * @param {SelectionModel} this
15535              * @param {Number} rowIndex The selected index
15536              * @param {Roo.data.Record} r The record
15537              */
15538             "rowselect" : true,
15539         /**
15540              * @event rowdeselect
15541              * Fires when a row is deselected.
15542              * @param {SelectionModel} this
15543              * @param {Number} rowIndex The selected index
15544              */
15545         "rowdeselect" : true
15546     });
15547     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15548     this.locked = false;
15549 };
15550
15551 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15552     /**
15553      * @cfg {Boolean} singleSelect
15554      * True to allow selection of only one row at a time (defaults to false)
15555      */
15556     singleSelect : false,
15557
15558     // private
15559     initEvents : function(){
15560
15561         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15562             this.grid.on("mousedown", this.handleMouseDown, this);
15563         }else{ // allow click to work like normal
15564             this.grid.on("rowclick", this.handleDragableRowClick, this);
15565         }
15566
15567         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15568             "up" : function(e){
15569                 if(!e.shiftKey){
15570                     this.selectPrevious(e.shiftKey);
15571                 }else if(this.last !== false && this.lastActive !== false){
15572                     var last = this.last;
15573                     this.selectRange(this.last,  this.lastActive-1);
15574                     this.grid.getView().focusRow(this.lastActive);
15575                     if(last !== false){
15576                         this.last = last;
15577                     }
15578                 }else{
15579                     this.selectFirstRow();
15580                 }
15581                 this.fireEvent("afterselectionchange", this);
15582             },
15583             "down" : function(e){
15584                 if(!e.shiftKey){
15585                     this.selectNext(e.shiftKey);
15586                 }else if(this.last !== false && this.lastActive !== false){
15587                     var last = this.last;
15588                     this.selectRange(this.last,  this.lastActive+1);
15589                     this.grid.getView().focusRow(this.lastActive);
15590                     if(last !== false){
15591                         this.last = last;
15592                     }
15593                 }else{
15594                     this.selectFirstRow();
15595                 }
15596                 this.fireEvent("afterselectionchange", this);
15597             },
15598             scope: this
15599         });
15600
15601         var view = this.grid.view;
15602         view.on("refresh", this.onRefresh, this);
15603         view.on("rowupdated", this.onRowUpdated, this);
15604         view.on("rowremoved", this.onRemove, this);
15605     },
15606
15607     // private
15608     onRefresh : function(){
15609         var ds = this.grid.dataSource, i, v = this.grid.view;
15610         var s = this.selections;
15611         s.each(function(r){
15612             if((i = ds.indexOfId(r.id)) != -1){
15613                 v.onRowSelect(i);
15614             }else{
15615                 s.remove(r);
15616             }
15617         });
15618     },
15619
15620     // private
15621     onRemove : function(v, index, r){
15622         this.selections.remove(r);
15623     },
15624
15625     // private
15626     onRowUpdated : function(v, index, r){
15627         if(this.isSelected(r)){
15628             v.onRowSelect(index);
15629         }
15630     },
15631
15632     /**
15633      * Select records.
15634      * @param {Array} records The records to select
15635      * @param {Boolean} keepExisting (optional) True to keep existing selections
15636      */
15637     selectRecords : function(records, keepExisting){
15638         if(!keepExisting){
15639             this.clearSelections();
15640         }
15641         var ds = this.grid.dataSource;
15642         for(var i = 0, len = records.length; i < len; i++){
15643             this.selectRow(ds.indexOf(records[i]), true);
15644         }
15645     },
15646
15647     /**
15648      * Gets the number of selected rows.
15649      * @return {Number}
15650      */
15651     getCount : function(){
15652         return this.selections.length;
15653     },
15654
15655     /**
15656      * Selects the first row in the grid.
15657      */
15658     selectFirstRow : function(){
15659         this.selectRow(0);
15660     },
15661
15662     /**
15663      * Select the last row.
15664      * @param {Boolean} keepExisting (optional) True to keep existing selections
15665      */
15666     selectLastRow : function(keepExisting){
15667         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15668     },
15669
15670     /**
15671      * Selects the row immediately following the last selected row.
15672      * @param {Boolean} keepExisting (optional) True to keep existing selections
15673      */
15674     selectNext : function(keepExisting){
15675         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15676             this.selectRow(this.last+1, keepExisting);
15677             this.grid.getView().focusRow(this.last);
15678         }
15679     },
15680
15681     /**
15682      * Selects the row that precedes the last selected row.
15683      * @param {Boolean} keepExisting (optional) True to keep existing selections
15684      */
15685     selectPrevious : function(keepExisting){
15686         if(this.last){
15687             this.selectRow(this.last-1, keepExisting);
15688             this.grid.getView().focusRow(this.last);
15689         }
15690     },
15691
15692     /**
15693      * Returns the selected records
15694      * @return {Array} Array of selected records
15695      */
15696     getSelections : function(){
15697         return [].concat(this.selections.items);
15698     },
15699
15700     /**
15701      * Returns the first selected record.
15702      * @return {Record}
15703      */
15704     getSelected : function(){
15705         return this.selections.itemAt(0);
15706     },
15707
15708
15709     /**
15710      * Clears all selections.
15711      */
15712     clearSelections : function(fast){
15713         if(this.locked) return;
15714         if(fast !== true){
15715             var ds = this.grid.dataSource;
15716             var s = this.selections;
15717             s.each(function(r){
15718                 this.deselectRow(ds.indexOfId(r.id));
15719             }, this);
15720             s.clear();
15721         }else{
15722             this.selections.clear();
15723         }
15724         this.last = false;
15725     },
15726
15727
15728     /**
15729      * Selects all rows.
15730      */
15731     selectAll : function(){
15732         if(this.locked) return;
15733         this.selections.clear();
15734         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15735             this.selectRow(i, true);
15736         }
15737     },
15738
15739     /**
15740      * Returns True if there is a selection.
15741      * @return {Boolean}
15742      */
15743     hasSelection : function(){
15744         return this.selections.length > 0;
15745     },
15746
15747     /**
15748      * Returns True if the specified row is selected.
15749      * @param {Number/Record} record The record or index of the record to check
15750      * @return {Boolean}
15751      */
15752     isSelected : function(index){
15753         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15754         return (r && this.selections.key(r.id) ? true : false);
15755     },
15756
15757     /**
15758      * Returns True if the specified record id is selected.
15759      * @param {String} id The id of record to check
15760      * @return {Boolean}
15761      */
15762     isIdSelected : function(id){
15763         return (this.selections.key(id) ? true : false);
15764     },
15765
15766     // private
15767     handleMouseDown : function(e, t){
15768         var view = this.grid.getView(), rowIndex;
15769         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15770             return;
15771         };
15772         if(e.shiftKey && this.last !== false){
15773             var last = this.last;
15774             this.selectRange(last, rowIndex, e.ctrlKey);
15775             this.last = last; // reset the last
15776             view.focusRow(rowIndex);
15777         }else{
15778             var isSelected = this.isSelected(rowIndex);
15779             if(e.button !== 0 && isSelected){
15780                 view.focusRow(rowIndex);
15781             }else if(e.ctrlKey && isSelected){
15782                 this.deselectRow(rowIndex);
15783             }else if(!isSelected){
15784                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15785                 view.focusRow(rowIndex);
15786             }
15787         }
15788         this.fireEvent("afterselectionchange", this);
15789     },
15790     // private
15791     handleDragableRowClick :  function(grid, rowIndex, e) 
15792     {
15793         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15794             this.selectRow(rowIndex, false);
15795             grid.view.focusRow(rowIndex);
15796              this.fireEvent("afterselectionchange", this);
15797         }
15798     },
15799     
15800     /**
15801      * Selects multiple rows.
15802      * @param {Array} rows Array of the indexes of the row to select
15803      * @param {Boolean} keepExisting (optional) True to keep existing selections
15804      */
15805     selectRows : function(rows, keepExisting){
15806         if(!keepExisting){
15807             this.clearSelections();
15808         }
15809         for(var i = 0, len = rows.length; i < len; i++){
15810             this.selectRow(rows[i], true);
15811         }
15812     },
15813
15814     /**
15815      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15816      * @param {Number} startRow The index of the first row in the range
15817      * @param {Number} endRow The index of the last row in the range
15818      * @param {Boolean} keepExisting (optional) True to retain existing selections
15819      */
15820     selectRange : function(startRow, endRow, keepExisting){
15821         if(this.locked) return;
15822         if(!keepExisting){
15823             this.clearSelections();
15824         }
15825         if(startRow <= endRow){
15826             for(var i = startRow; i <= endRow; i++){
15827                 this.selectRow(i, true);
15828             }
15829         }else{
15830             for(var i = startRow; i >= endRow; i--){
15831                 this.selectRow(i, true);
15832             }
15833         }
15834     },
15835
15836     /**
15837      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15838      * @param {Number} startRow The index of the first row in the range
15839      * @param {Number} endRow The index of the last row in the range
15840      */
15841     deselectRange : function(startRow, endRow, preventViewNotify){
15842         if(this.locked) return;
15843         for(var i = startRow; i <= endRow; i++){
15844             this.deselectRow(i, preventViewNotify);
15845         }
15846     },
15847
15848     /**
15849      * Selects a row.
15850      * @param {Number} row The index of the row to select
15851      * @param {Boolean} keepExisting (optional) True to keep existing selections
15852      */
15853     selectRow : function(index, keepExisting, preventViewNotify){
15854         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15855         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15856             if(!keepExisting || this.singleSelect){
15857                 this.clearSelections();
15858             }
15859             var r = this.grid.dataSource.getAt(index);
15860             this.selections.add(r);
15861             this.last = this.lastActive = index;
15862             if(!preventViewNotify){
15863                 this.grid.getView().onRowSelect(index);
15864             }
15865             this.fireEvent("rowselect", this, index, r);
15866             this.fireEvent("selectionchange", this);
15867         }
15868     },
15869
15870     /**
15871      * Deselects a row.
15872      * @param {Number} row The index of the row to deselect
15873      */
15874     deselectRow : function(index, preventViewNotify){
15875         if(this.locked) return;
15876         if(this.last == index){
15877             this.last = false;
15878         }
15879         if(this.lastActive == index){
15880             this.lastActive = false;
15881         }
15882         var r = this.grid.dataSource.getAt(index);
15883         this.selections.remove(r);
15884         if(!preventViewNotify){
15885             this.grid.getView().onRowDeselect(index);
15886         }
15887         this.fireEvent("rowdeselect", this, index);
15888         this.fireEvent("selectionchange", this);
15889     },
15890
15891     // private
15892     restoreLast : function(){
15893         if(this._last){
15894             this.last = this._last;
15895         }
15896     },
15897
15898     // private
15899     acceptsNav : function(row, col, cm){
15900         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15901     },
15902
15903     // private
15904     onEditorKey : function(field, e){
15905         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15906         if(k == e.TAB){
15907             e.stopEvent();
15908             ed.completeEdit();
15909             if(e.shiftKey){
15910                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15911             }else{
15912                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15913             }
15914         }else if(k == e.ENTER && !e.ctrlKey){
15915             e.stopEvent();
15916             ed.completeEdit();
15917             if(e.shiftKey){
15918                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15919             }else{
15920                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15921             }
15922         }else if(k == e.ESC){
15923             ed.cancelEdit();
15924         }
15925         if(newCell){
15926             g.startEditing(newCell[0], newCell[1]);
15927         }
15928     }
15929 });/*
15930  * - LGPL
15931  *
15932  * element
15933  * 
15934  */
15935
15936 /**
15937  * @class Roo.bootstrap.MessageBar
15938  * @extends Roo.bootstrap.Component
15939  * Bootstrap MessageBar class
15940  * @cfg {String} html contents of the MessageBar
15941  * @cfg {String} weight (info | success | warning | danger) default info
15942  * @cfg {String} beforeClass insert the bar before the given class
15943  * @cfg {Boolean} closable (true | false) default false
15944  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15945  * 
15946  * @constructor
15947  * Create a new Element
15948  * @param {Object} config The config object
15949  */
15950
15951 Roo.bootstrap.MessageBar = function(config){
15952     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15953 };
15954
15955 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15956     
15957     html: '',
15958     weight: 'info',
15959     closable: false,
15960     fixed: false,
15961     beforeClass: 'bootstrap-sticky-wrap',
15962     
15963     getAutoCreate : function(){
15964         
15965         var cfg = {
15966             tag: 'div',
15967             cls: 'alert alert-dismissable alert-' + this.weight,
15968             cn: [
15969                 {
15970                     tag: 'span',
15971                     cls: 'message',
15972                     html: this.html || ''
15973                 }
15974             ]
15975         }
15976         
15977         if(this.fixed){
15978             cfg.cls += ' alert-messages-fixed';
15979         }
15980         
15981         if(this.closable){
15982             cfg.cn.push({
15983                 tag: 'button',
15984                 cls: 'close',
15985                 html: 'x'
15986             });
15987         }
15988         
15989         return cfg;
15990     },
15991     
15992     onRender : function(ct, position)
15993     {
15994         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15995         
15996         if(!this.el){
15997             var cfg = Roo.apply({},  this.getAutoCreate());
15998             cfg.id = Roo.id();
15999             
16000             if (this.cls) {
16001                 cfg.cls += ' ' + this.cls;
16002             }
16003             if (this.style) {
16004                 cfg.style = this.style;
16005             }
16006             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16007             
16008             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16009         }
16010         
16011         this.el.select('>button.close').on('click', this.hide, this);
16012         
16013     },
16014     
16015     show : function()
16016     {
16017         if (!this.rendered) {
16018             this.render();
16019         }
16020         
16021         this.el.show();
16022         
16023         this.fireEvent('show', this);
16024         
16025     },
16026     
16027     hide : function()
16028     {
16029         if (!this.rendered) {
16030             this.render();
16031         }
16032         
16033         this.el.hide();
16034         
16035         this.fireEvent('hide', this);
16036     },
16037     
16038     update : function()
16039     {
16040 //        var e = this.el.dom.firstChild;
16041 //        
16042 //        if(this.closable){
16043 //            e = e.nextSibling;
16044 //        }
16045 //        
16046 //        e.data = this.html || '';
16047
16048         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16049     }
16050    
16051 });
16052
16053  
16054
16055