Roo/bootstrap/Modal.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 = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
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        this.el.enableDisplayMode();
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     setText : function(str)
722     {
723         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
724     },
725     hide: function() {
726        
727      
728         this.el.hide();   
729     },
730     show: function() {
731        
732         this.el.show();   
733     }
734     
735     
736 });
737
738  /*
739  * - LGPL
740  *
741  * column
742  * 
743  */
744
745 /**
746  * @class Roo.bootstrap.Column
747  * @extends Roo.bootstrap.Component
748  * Bootstrap Column class
749  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
750  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
751  * @cfg {Number} md colspan out of 12 for computer-sized screens
752  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
753  * @cfg {String} html content of column.
754  * 
755  * @constructor
756  * Create a new Column
757  * @param {Object} config The config object
758  */
759
760 Roo.bootstrap.Column = function(config){
761     Roo.bootstrap.Column.superclass.constructor.call(this, config);
762 };
763
764 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
765     
766     xs: null,
767     sm: null,
768     md: null,
769     lg: null,
770     html: '',
771     offset: 0,
772     
773     getAutoCreate : function(){
774         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
775         
776         cfg = {
777             tag: 'div',
778             cls: 'column'
779         };
780         
781         var settings=this;
782         ['xs','sm','md','lg'].map(function(size){
783             if (settings[size]) {
784                 cfg.cls += ' col-' + size + '-' + settings[size];
785             }
786         });
787         if (this.html.length) {
788             cfg.html = this.html;
789         }
790         
791         return cfg;
792     }
793    
794 });
795
796  
797
798  /*
799  * - LGPL
800  *
801  * page container.
802  * 
803  */
804
805
806 /**
807  * @class Roo.bootstrap.Container
808  * @extends Roo.bootstrap.Component
809  * Bootstrap Container class
810  * @cfg {Boolean} jumbotron is it a jumbotron element
811  * @cfg {String} html content of element
812  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
813  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
814  * @cfg {String} header content of header (for panel)
815  * @cfg {String} footer content of footer (for panel)
816  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
817  *     
818  * @constructor
819  * Create a new Container
820  * @param {Object} config The config object
821  */
822
823 Roo.bootstrap.Container = function(config){
824     Roo.bootstrap.Container.superclass.constructor.call(this, config);
825 };
826
827 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
828     
829     jumbotron : false,
830     well: '',
831     panel : '',
832     header: '',
833     footer : '',
834     sticky: '',
835   
836      
837     getChildContainer : function() {
838         
839         if(!this.el){
840             return false;
841         }
842         
843         if (this.panel.length) {
844             return this.el.select('.panel-body',true).first();
845         }
846         
847         return this.el;
848     },
849     
850     
851     getAutoCreate : function(){
852         
853         var cfg = {
854             html : '',
855             cls : ''
856         };
857         if (this.jumbotron) {
858             cfg.cls = 'jumbotron';
859         }
860         if (this.cls) {
861             cfg.cls = this.cls + '';
862         }
863         
864         if (this.sticky.length) {
865             
866             var bd = Roo.get(document.body);
867             if (!bd.hasClass('bootstrap-sticky')) {
868                 bd.addClass('bootstrap-sticky');
869                 Roo.select('html',true).setStyle('height', '100%');
870             }
871              
872             cfg.cls += 'bootstrap-sticky-' + this.sticky;
873         }
874         
875         
876         if (this.well.length) {
877             switch (this.well) {
878                 case 'lg':
879                 case 'sm':
880                     cfg.cls +=' well well-' +this.well;
881                     break;
882                 default:
883                     cfg.cls +=' well';
884                     break;
885             }
886         }
887         
888         var body = cfg;
889         
890         if (this.panel.length) {
891             cfg.cls += ' panel panel-' + this.panel;
892             cfg.cn = [];
893             if (this.header.length) {
894                 cfg.cn.push({
895                     
896                     cls : 'panel-heading',
897                     cn : [{
898                         tag: 'h3',
899                         cls : 'panel-title',
900                         html : this.header
901                     }]
902                     
903                 });
904             }
905             body = false;
906             cfg.cn.push({
907                 cls : 'panel-body',
908                 html : this.html
909             });
910             
911             
912             if (this.footer.length) {
913                 cfg.cn.push({
914                     cls : 'panel-footer',
915                     html : this.footer
916                     
917                 });
918             }
919             
920         }
921         if (body) {
922             body.html = this.html || cfg.html;
923         }
924         if (!cfg.cls.length) {
925             cfg.cls =  'container';
926         }
927         
928         return cfg;
929     }
930    
931 });
932
933  /*
934  * - LGPL
935  *
936  * image
937  * 
938  */
939
940
941 /**
942  * @class Roo.bootstrap.Img
943  * @extends Roo.bootstrap.Component
944  * Bootstrap Img class
945  * @cfg {Boolean} imgResponsive false | true
946  * @cfg {String} border rounded | circle | thumbnail
947  * @cfg {String} src image source
948  * @cfg {String} alt image alternative text
949  * @cfg {String} href a tag href
950  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
951  * 
952  * @constructor
953  * Create a new Input
954  * @param {Object} config The config object
955  */
956
957 Roo.bootstrap.Img = function(config){
958     Roo.bootstrap.Img.superclass.constructor.call(this, config);
959     
960     this.addEvents({
961         // img events
962         /**
963          * @event click
964          * The img click event for the img.
965          * @param {Roo.EventObject} e
966          */
967         "click" : true
968     });
969 };
970
971 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
972     
973     imgResponsive: true,
974     border: '',
975     src: '',
976     href: false,
977     target: false,
978
979     getAutoCreate : function(){
980         
981         var cfg = {
982             tag: 'img',
983             cls: (this.imgResponsive) ? 'img-responsive' : '',
984             html : null
985         }
986         
987         cfg.html = this.html || cfg.html;
988         
989         cfg.src = this.src || cfg.src;
990         
991         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
992             cfg.cls += ' img-' + this.border;
993         }
994         
995         if(this.alt){
996             cfg.alt = this.alt;
997         }
998         
999         if(this.href){
1000             var a = {
1001                 tag: 'a',
1002                 href: this.href,
1003                 cn: [
1004                     cfg
1005                 ]
1006             }
1007             
1008             if(this.target){
1009                 a.target = this.target;
1010             }
1011             
1012         }
1013         
1014         
1015         return (this.href) ? a : cfg;
1016     },
1017     
1018     initEvents: function() {
1019         
1020         if(!this.href){
1021             this.el.on('click', this.onClick, this);
1022         }
1023     },
1024     
1025     onClick : function(e)
1026     {
1027         Roo.log('img onclick');
1028         this.fireEvent('click', this, e);
1029     }
1030    
1031 });
1032
1033  /*
1034  * - LGPL
1035  *
1036  * header
1037  * 
1038  */
1039
1040 /**
1041  * @class Roo.bootstrap.Header
1042  * @extends Roo.bootstrap.Component
1043  * Bootstrap Header class
1044  * @cfg {String} html content of header
1045  * @cfg {Number} level (1|2|3|4|5|6) default 1
1046  * 
1047  * @constructor
1048  * Create a new Header
1049  * @param {Object} config The config object
1050  */
1051
1052
1053 Roo.bootstrap.Header  = function(config){
1054     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1058     
1059     //href : false,
1060     html : false,
1061     level : 1,
1062     
1063     
1064     
1065     getAutoCreate : function(){
1066         
1067         var cfg = {
1068             tag: 'h' + (1 *this.level),
1069             html: this.html || 'fill in html'
1070         } ;
1071         
1072         return cfg;
1073     }
1074    
1075 });
1076
1077  
1078
1079  /*
1080  * Based on:
1081  * Ext JS Library 1.1.1
1082  * Copyright(c) 2006-2007, Ext JS, LLC.
1083  *
1084  * Originally Released Under LGPL - original licence link has changed is not relivant.
1085  *
1086  * Fork - LGPL
1087  * <script type="text/javascript">
1088  */
1089  
1090 /**
1091  * @class Roo.bootstrap.MenuMgr
1092  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1093  * @singleton
1094  */
1095 Roo.bootstrap.MenuMgr = function(){
1096    var menus, active, groups = {}, attached = false, lastShow = new Date();
1097
1098    // private - called when first menu is created
1099    function init(){
1100        menus = {};
1101        active = new Roo.util.MixedCollection();
1102        Roo.get(document).addKeyListener(27, function(){
1103            if(active.length > 0){
1104                hideAll();
1105            }
1106        });
1107    }
1108
1109    // private
1110    function hideAll(){
1111        if(active && active.length > 0){
1112            var c = active.clone();
1113            c.each(function(m){
1114                m.hide();
1115            });
1116        }
1117    }
1118
1119    // private
1120    function onHide(m){
1121        active.remove(m);
1122        if(active.length < 1){
1123            Roo.get(document).un("mouseup", onMouseDown);
1124             
1125            attached = false;
1126        }
1127    }
1128
1129    // private
1130    function onShow(m){
1131        var last = active.last();
1132        lastShow = new Date();
1133        active.add(m);
1134        if(!attached){
1135           Roo.get(document).on("mouseup", onMouseDown);
1136            
1137            attached = true;
1138        }
1139        if(m.parentMenu){
1140           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1141           m.parentMenu.activeChild = m;
1142        }else if(last && last.isVisible()){
1143           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1144        }
1145    }
1146
1147    // private
1148    function onBeforeHide(m){
1149        if(m.activeChild){
1150            m.activeChild.hide();
1151        }
1152        if(m.autoHideTimer){
1153            clearTimeout(m.autoHideTimer);
1154            delete m.autoHideTimer;
1155        }
1156    }
1157
1158    // private
1159    function onBeforeShow(m){
1160        var pm = m.parentMenu;
1161        if(!pm && !m.allowOtherMenus){
1162            hideAll();
1163        }else if(pm && pm.activeChild && active != m){
1164            pm.activeChild.hide();
1165        }
1166    }
1167
1168    // private
1169    function onMouseDown(e){
1170         Roo.log("on MouseDown");
1171         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1172            hideAll();
1173         }
1174         
1175         
1176    }
1177
1178    // private
1179    function onBeforeCheck(mi, state){
1180        if(state){
1181            var g = groups[mi.group];
1182            for(var i = 0, l = g.length; i < l; i++){
1183                if(g[i] != mi){
1184                    g[i].setChecked(false);
1185                }
1186            }
1187        }
1188    }
1189
1190    return {
1191
1192        /**
1193         * Hides all menus that are currently visible
1194         */
1195        hideAll : function(){
1196             hideAll();  
1197        },
1198
1199        // private
1200        register : function(menu){
1201            if(!menus){
1202                init();
1203            }
1204            menus[menu.id] = menu;
1205            menu.on("beforehide", onBeforeHide);
1206            menu.on("hide", onHide);
1207            menu.on("beforeshow", onBeforeShow);
1208            menu.on("show", onShow);
1209            var g = menu.group;
1210            if(g && menu.events["checkchange"]){
1211                if(!groups[g]){
1212                    groups[g] = [];
1213                }
1214                groups[g].push(menu);
1215                menu.on("checkchange", onCheck);
1216            }
1217        },
1218
1219         /**
1220          * Returns a {@link Roo.menu.Menu} object
1221          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1222          * be used to generate and return a new Menu instance.
1223          */
1224        get : function(menu){
1225            if(typeof menu == "string"){ // menu id
1226                return menus[menu];
1227            }else if(menu.events){  // menu instance
1228                return menu;
1229            }
1230            /*else if(typeof menu.length == 'number'){ // array of menu items?
1231                return new Roo.bootstrap.Menu({items:menu});
1232            }else{ // otherwise, must be a config
1233                return new Roo.bootstrap.Menu(menu);
1234            }
1235            */
1236            return false;
1237        },
1238
1239        // private
1240        unregister : function(menu){
1241            delete menus[menu.id];
1242            menu.un("beforehide", onBeforeHide);
1243            menu.un("hide", onHide);
1244            menu.un("beforeshow", onBeforeShow);
1245            menu.un("show", onShow);
1246            var g = menu.group;
1247            if(g && menu.events["checkchange"]){
1248                groups[g].remove(menu);
1249                menu.un("checkchange", onCheck);
1250            }
1251        },
1252
1253        // private
1254        registerCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                if(!groups[g]){
1258                    groups[g] = [];
1259                }
1260                groups[g].push(menuItem);
1261                menuItem.on("beforecheckchange", onBeforeCheck);
1262            }
1263        },
1264
1265        // private
1266        unregisterCheckable : function(menuItem){
1267            var g = menuItem.group;
1268            if(g){
1269                groups[g].remove(menuItem);
1270                menuItem.un("beforecheckchange", onBeforeCheck);
1271            }
1272        }
1273    };
1274 }();/*
1275  * - LGPL
1276  *
1277  * menu
1278  * 
1279  */
1280
1281 /**
1282  * @class Roo.bootstrap.Menu
1283  * @extends Roo.bootstrap.Component
1284  * Bootstrap Menu class - container for MenuItems
1285  * @cfg {String} type type of menu
1286  * 
1287  * @constructor
1288  * Create a new Menu
1289  * @param {Object} config The config object
1290  */
1291
1292
1293 Roo.bootstrap.Menu = function(config){
1294     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1295     if (this.registerMenu) {
1296         Roo.bootstrap.MenuMgr.register(this);
1297     }
1298     this.addEvents({
1299         /**
1300          * @event beforeshow
1301          * Fires before this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         beforeshow : true,
1305         /**
1306          * @event beforehide
1307          * Fires before this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         beforehide : true,
1311         /**
1312          * @event show
1313          * Fires after this menu is displayed
1314          * @param {Roo.menu.Menu} this
1315          */
1316         show : true,
1317         /**
1318          * @event hide
1319          * Fires after this menu is hidden
1320          * @param {Roo.menu.Menu} this
1321          */
1322         hide : true,
1323         /**
1324          * @event click
1325          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1326          * @param {Roo.menu.Menu} this
1327          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1328          * @param {Roo.EventObject} e
1329          */
1330         click : true,
1331         /**
1332          * @event mouseover
1333          * Fires when the mouse is hovering over this menu
1334          * @param {Roo.menu.Menu} this
1335          * @param {Roo.EventObject} e
1336          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337          */
1338         mouseover : true,
1339         /**
1340          * @event mouseout
1341          * Fires when the mouse exits this menu
1342          * @param {Roo.menu.Menu} this
1343          * @param {Roo.EventObject} e
1344          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1345          */
1346         mouseout : true,
1347         /**
1348          * @event itemclick
1349          * Fires when a menu item contained in this menu is clicked
1350          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1351          * @param {Roo.EventObject} e
1352          */
1353         itemclick: true
1354     });
1355     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1356 };
1357
1358 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1359     
1360    /// html : false,
1361     //align : '',
1362     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1363     type: false,
1364     /**
1365      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1366      */
1367     registerMenu : true,
1368     
1369     menuItems :false, // stores the menu items..
1370     
1371     hidden:true,
1372     
1373     parentMenu : false,
1374     
1375     getChildContainer : function() {
1376         return this.el;  
1377     },
1378     
1379     getAutoCreate : function(){
1380          
1381         //if (['right'].indexOf(this.align)!==-1) {
1382         //    cfg.cn[1].cls += ' pull-right'
1383         //}
1384         var cfg = {
1385             tag : 'ul',
1386             cls : 'dropdown-menu' ,
1387             style : 'z-index:1000'
1388             
1389         }
1390         
1391         if (this.type === 'submenu') {
1392             cfg.cls = 'submenu active'
1393         }
1394         
1395         return cfg;
1396     },
1397     initEvents : function() {
1398         
1399        // Roo.log("ADD event");
1400        // Roo.log(this.triggerEl.dom);
1401         this.triggerEl.on('click', this.onTriggerPress, this);
1402         this.triggerEl.addClass('dropdown-toggle');
1403         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1404
1405         this.el.on("mouseover", this.onMouseOver, this);
1406         this.el.on("mouseout", this.onMouseOut, this);
1407         
1408         
1409     },
1410     findTargetItem : function(e){
1411         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1412         if(!t){
1413             return false;
1414         }
1415         //Roo.log(t);         Roo.log(t.id);
1416         if(t && t.id){
1417             //Roo.log(this.menuitems);
1418             return this.menuitems.get(t.id);
1419             
1420             //return this.items.get(t.menuItemId);
1421         }
1422         
1423         return false;
1424     },
1425     onClick : function(e){
1426         Roo.log("menu.onClick");
1427         var t = this.findTargetItem(e);
1428         if(!t){
1429             return;
1430         }
1431         Roo.log(e);
1432         /*
1433         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1434             if(t == this.activeItem && t.shouldDeactivate(e)){
1435                 this.activeItem.deactivate();
1436                 delete this.activeItem;
1437                 return;
1438             }
1439             if(t.canActivate){
1440                 this.setActiveItem(t, true);
1441             }
1442             return;
1443             
1444             
1445         }
1446         */
1447         Roo.log('pass click event');
1448         
1449         t.onClick(e);
1450         
1451         this.fireEvent("click", this, t, e);
1452         
1453         this.hide();
1454     },
1455      onMouseOver : function(e){
1456         var t  = this.findTargetItem(e);
1457         //Roo.log(t);
1458         //if(t){
1459         //    if(t.canActivate && !t.disabled){
1460         //        this.setActiveItem(t, true);
1461         //    }
1462         //}
1463         
1464         this.fireEvent("mouseover", this, e, t);
1465     },
1466     isVisible : function(){
1467         return !this.hidden;
1468     },
1469      onMouseOut : function(e){
1470         var t  = this.findTargetItem(e);
1471         
1472         //if(t ){
1473         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1474         //        this.activeItem.deactivate();
1475         //        delete this.activeItem;
1476         //    }
1477         //}
1478         this.fireEvent("mouseout", this, e, t);
1479     },
1480     
1481     
1482     /**
1483      * Displays this menu relative to another element
1484      * @param {String/HTMLElement/Roo.Element} element The element to align to
1485      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1486      * the element (defaults to this.defaultAlign)
1487      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1488      */
1489     show : function(el, pos, parentMenu){
1490         this.parentMenu = parentMenu;
1491         if(!this.el){
1492             this.render();
1493         }
1494         this.fireEvent("beforeshow", this);
1495         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1496     },
1497      /**
1498      * Displays this menu at a specific xy position
1499      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1500      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1501      */
1502     showAt : function(xy, parentMenu, /* private: */_e){
1503         this.parentMenu = parentMenu;
1504         if(!this.el){
1505             this.render();
1506         }
1507         if(_e !== false){
1508             this.fireEvent("beforeshow", this);
1509             
1510             //xy = this.el.adjustForConstraints(xy);
1511         }
1512         //this.el.setXY(xy);
1513         //this.el.show();
1514         this.hideMenuItems();
1515         this.hidden = false;
1516         this.triggerEl.addClass('open');
1517         this.focus();
1518         this.fireEvent("show", this);
1519     },
1520     
1521     focus : function(){
1522         return;
1523         if(!this.hidden){
1524             this.doFocus.defer(50, this);
1525         }
1526     },
1527
1528     doFocus : function(){
1529         if(!this.hidden){
1530             this.focusEl.focus();
1531         }
1532     },
1533
1534     /**
1535      * Hides this menu and optionally all parent menus
1536      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1537      */
1538     hide : function(deep){
1539         
1540         this.hideMenuItems();
1541         if(this.el && this.isVisible()){
1542             this.fireEvent("beforehide", this);
1543             if(this.activeItem){
1544                 this.activeItem.deactivate();
1545                 this.activeItem = null;
1546             }
1547             this.triggerEl.removeClass('open');;
1548             this.hidden = true;
1549             this.fireEvent("hide", this);
1550         }
1551         if(deep === true && this.parentMenu){
1552             this.parentMenu.hide(true);
1553         }
1554     },
1555     
1556     onTriggerPress  : function(e)
1557     {
1558         
1559         Roo.log('trigger press');
1560         //Roo.log(e.getTarget());
1561        // Roo.log(this.triggerEl.dom);
1562         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1563             return;
1564         }
1565         if (this.isVisible()) {
1566             Roo.log('hide');
1567             this.hide();
1568         } else {
1569             this.show(this.triggerEl, false, false);
1570         }
1571         
1572         
1573     },
1574     
1575          
1576        
1577     
1578     hideMenuItems : function()
1579     {
1580         //$(backdrop).remove()
1581         Roo.select('.open',true).each(function(aa) {
1582             
1583             aa.removeClass('open');
1584           //var parent = getParent($(this))
1585           //var relatedTarget = { relatedTarget: this }
1586           
1587            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1588           //if (e.isDefaultPrevented()) return
1589            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1590         })
1591     },
1592     addxtypeChild : function (tree, cntr) {
1593         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1594           
1595         this.menuitems.add(comp);
1596         return comp;
1597
1598     },
1599     getEl : function()
1600     {
1601         Roo.log(this.el);
1602         return this.el;
1603     }
1604 });
1605
1606  
1607  /*
1608  * - LGPL
1609  *
1610  * menu item
1611  * 
1612  */
1613
1614
1615 /**
1616  * @class Roo.bootstrap.MenuItem
1617  * @extends Roo.bootstrap.Component
1618  * Bootstrap MenuItem class
1619  * @cfg {String} html the menu label
1620  * @cfg {String} href the link
1621  * @cfg {Boolean} preventDefault (true | false) default true
1622  * 
1623  * 
1624  * @constructor
1625  * Create a new MenuItem
1626  * @param {Object} config The config object
1627  */
1628
1629
1630 Roo.bootstrap.MenuItem = function(config){
1631     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1632     this.addEvents({
1633         // raw events
1634         /**
1635          * @event click
1636          * The raw click event for the entire grid.
1637          * @param {Roo.EventObject} e
1638          */
1639         "click" : true
1640     });
1641 };
1642
1643 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1644     
1645     href : false,
1646     html : false,
1647     preventDefault: true,
1648     
1649     getAutoCreate : function(){
1650         var cfg= {
1651             tag: 'li',
1652         cls: 'dropdown-menu-item',
1653             cn: [
1654             {
1655                 tag : 'a',
1656                 href : '#',
1657                 html : 'Link'
1658             }
1659             ]
1660     };
1661         
1662         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1663         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1664         return cfg;
1665     },
1666     
1667     initEvents: function() {
1668         
1669         //this.el.select('a').on('click', this.onClick, this);
1670         
1671     },
1672     onClick : function(e)
1673     {
1674         Roo.log('item on click ');
1675         //if(this.preventDefault){
1676         //    e.preventDefault();
1677         //}
1678         //this.parent().hideMenuItems();
1679         
1680         this.fireEvent('click', this, e);
1681     },
1682     getEl : function()
1683     {
1684         return this.el;
1685     }
1686 });
1687
1688  
1689
1690  /*
1691  * - LGPL
1692  *
1693  * menu separator
1694  * 
1695  */
1696
1697
1698 /**
1699  * @class Roo.bootstrap.MenuSeparator
1700  * @extends Roo.bootstrap.Component
1701  * Bootstrap MenuSeparator class
1702  * 
1703  * @constructor
1704  * Create a new MenuItem
1705  * @param {Object} config The config object
1706  */
1707
1708
1709 Roo.bootstrap.MenuSeparator = function(config){
1710     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1711 };
1712
1713 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1714     
1715     getAutoCreate : function(){
1716         var cfg = {
1717             cls: 'divider',
1718             tag : 'li'
1719         };
1720         
1721         return cfg;
1722     }
1723    
1724 });
1725
1726  
1727
1728  
1729 /*
1730 <div class="modal fade">
1731   <div class="modal-dialog">
1732     <div class="modal-content">
1733       <div class="modal-header">
1734         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1735         <h4 class="modal-title">Modal title</h4>
1736       </div>
1737       <div class="modal-body">
1738         <p>One fine body&hellip;</p>
1739       </div>
1740       <div class="modal-footer">
1741         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1742         <button type="button" class="btn btn-primary">Save changes</button>
1743       </div>
1744     </div><!-- /.modal-content -->
1745   </div><!-- /.modal-dialog -->
1746 </div><!-- /.modal -->
1747 */
1748 /*
1749  * - LGPL
1750  *
1751  * page contgainer.
1752  * 
1753  */
1754
1755 /**
1756  * @class Roo.bootstrap.Modal
1757  * @extends Roo.bootstrap.Component
1758  * Bootstrap Modal class
1759  * @cfg {String} title Title of dialog
1760  * @cfg {Boolean} specificTitle (true|false) default false
1761  * @cfg {Array} buttons Array of buttons or standard button set..
1762  * 
1763  * @constructor
1764  * Create a new Modal Dialog
1765  * @param {Object} config The config object
1766  */
1767
1768 Roo.bootstrap.Modal = function(config){
1769     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1770     this.addEvents({
1771         // raw events
1772         /**
1773          * @event btnclick
1774          * The raw btnclick event for the button
1775          * @param {Roo.EventObject} e
1776          */
1777         "btnclick" : true
1778     });
1779     this.buttons = this.buttons || [];
1780 };
1781
1782 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1783     
1784     title : 'test dialog',
1785    
1786     buttons : false,
1787     
1788     // set on load...
1789     body:  false,
1790     
1791     specificTitle: false,
1792     
1793     onRender : function(ct, position)
1794     {
1795         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1796      
1797         if(!this.el){
1798             var cfg = Roo.apply({},  this.getAutoCreate());
1799             cfg.id = Roo.id();
1800             //if(!cfg.name){
1801             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1802             //}
1803             //if (!cfg.name.length) {
1804             //    delete cfg.name;
1805            // }
1806             if (this.cls) {
1807                 cfg.cls += ' ' + this.cls;
1808             }
1809             if (this.style) {
1810                 cfg.style = this.style;
1811             }
1812             this.el = Roo.get(document.body).createChild(cfg, position);
1813         }
1814         //var type = this.el.dom.type;
1815         
1816         if(this.tabIndex !== undefined){
1817             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1818         }
1819         
1820         
1821         
1822         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1823         this.maskEl.enableDisplayMode("block");
1824         this.maskEl.hide();
1825         //this.el.addClass("x-dlg-modal");
1826     
1827         if (this.buttons.length) {
1828             Roo.each(this.buttons, function(bb) {
1829                 b = Roo.apply({}, bb);
1830                 b.xns = b.xns || Roo.bootstrap;
1831                 b.xtype = b.xtype || 'Button';
1832                 if (typeof(b.listeners) == 'undefined') {
1833                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1834                 }
1835                 
1836                 var btn = Roo.factory(b);
1837                 
1838                 btn.onRender(this.el.select('.modal-footer').first());
1839                 
1840             },this);
1841         }
1842         // render the children.
1843         var nitems = [];
1844         
1845         if(typeof(this.items) != 'undefined'){
1846             var items = this.items;
1847             delete this.items;
1848
1849             for(var i =0;i < items.length;i++) {
1850                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1851             }
1852         }
1853         
1854         this.items = nitems;
1855         
1856         this.body = this.el.select('.modal-body',true).first();
1857         this.close = this.el.select('.modal-header .close', true).first();
1858         this.footer = this.el.select('.modal-footer',true).first();
1859         this.initEvents();
1860         //this.el.addClass([this.fieldClass, this.cls]);
1861         
1862     },
1863     getAutoCreate : function(){
1864         
1865         
1866         var bdy = {
1867                 cls : 'modal-body',
1868                 html : this.html || ''
1869         };
1870         
1871         var title = {
1872             tag: 'h4',
1873             cls : 'modal-title',
1874             html : this.title
1875         };
1876         
1877         if(this.specificTitle){
1878             title = this.title;
1879         };
1880         
1881         return modal = {
1882             cls: "modal fade",
1883             style : 'display: none',
1884             cn : [
1885                 {
1886                     cls: "modal-dialog",
1887                     cn : [
1888                         {
1889                             cls : "modal-content",
1890                             cn : [
1891                                 {
1892                                     cls : 'modal-header',
1893                                     cn : [
1894                                         {
1895                                             tag: 'button',
1896                                             cls : 'close',
1897                                             html : '&times'
1898                                         },
1899                                         title
1900                                     ]
1901                                 },
1902                                 bdy,
1903                                 {
1904                                     cls : 'modal-footer' 
1905                                 }
1906                                 
1907                                 
1908                             ]
1909                             
1910                         }
1911                     ]
1912                         
1913                 }
1914             ]
1915             
1916             
1917         };
1918           
1919     },
1920     getChildContainer : function() {
1921          
1922          return this.el.select('.modal-body',true).first();
1923         
1924     },
1925     getButtonContainer : function() {
1926          return this.el.select('.modal-footer',true).first();
1927         
1928     },
1929     initEvents : function()
1930     {
1931         this.el.select('.modal-header .close').on('click', this.hide, this);
1932 //        
1933 //        this.addxtype(this);
1934     },
1935     show : function() {
1936         
1937         if (!this.rendered) {
1938             this.render();
1939         }
1940        
1941         this.el.addClass('on');
1942         this.el.removeClass('fade');
1943         this.el.setStyle('display', 'block');
1944         Roo.get(document.body).addClass("x-body-masked");
1945         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1946         this.maskEl.show();
1947         this.el.setStyle('zIndex', '10001');
1948         this.fireEvent('show', this);
1949         
1950         
1951     },
1952     hide : function()
1953     {
1954         Roo.log('Modal hide?!');
1955         this.maskEl.hide();
1956         Roo.get(document.body).removeClass("x-body-masked");
1957         this.el.removeClass('on');
1958         this.el.addClass('fade');
1959         this.el.setStyle('display', 'none');
1960         this.fireEvent('hide', this);
1961     },
1962     
1963     addButton : function(str, cb)
1964     {
1965          
1966         
1967         var b = Roo.apply({}, { html : str } );
1968         b.xns = b.xns || Roo.bootstrap;
1969         b.xtype = b.xtype || 'Button';
1970         if (typeof(b.listeners) == 'undefined') {
1971             b.listeners = { click : cb.createDelegate(this)  };
1972         }
1973         
1974         var btn = Roo.factory(b);
1975            
1976         btn.onRender(this.el.select('.modal-footer').first());
1977         
1978         return btn;   
1979        
1980     },
1981     
1982     setDefaultButton : function(btn)
1983     {
1984         //this.el.select('.modal-footer').()
1985     },
1986     resizeTo: function(w,h)
1987     {
1988         // skip..
1989     },
1990     setContentSize  : function(w, h)
1991     {
1992         
1993     },
1994     onButtonClick: function(btn,e)
1995     {
1996         //Roo.log([a,b,c]);
1997         this.fireEvent('btnclick', btn.name, e);
1998     },
1999     setTitle: function(str) {
2000         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2001         
2002     }
2003 });
2004
2005
2006 Roo.apply(Roo.bootstrap.Modal,  {
2007     /**
2008          * Button config that displays a single OK button
2009          * @type Object
2010          */
2011         OK :  [{
2012             name : 'ok',
2013             weight : 'primary',
2014             html : 'OK'
2015         }], 
2016         /**
2017          * Button config that displays Yes and No buttons
2018          * @type Object
2019          */
2020         YESNO : [
2021             {
2022                 name  : 'no',
2023                 html : 'No'
2024             },
2025             {
2026                 name  :'yes',
2027                 weight : 'primary',
2028                 html : 'Yes'
2029             }
2030         ],
2031         
2032         /**
2033          * Button config that displays OK and Cancel buttons
2034          * @type Object
2035          */
2036         OKCANCEL : [
2037             {
2038                name : 'cancel',
2039                 html : 'Cancel'
2040             },
2041             {
2042                 name : 'ok',
2043                 weight : 'primary',
2044                 html : 'OK'
2045             }
2046         ],
2047         /**
2048          * Button config that displays Yes, No and Cancel buttons
2049          * @type Object
2050          */
2051         YESNOCANCEL : [
2052             {
2053                 name : 'yes',
2054                 weight : 'primary',
2055                 html : 'Yes'
2056             },
2057             {
2058                 name : 'no',
2059                 html : 'No'
2060             },
2061             {
2062                 name : 'cancel',
2063                 html : 'Cancel'
2064             }
2065         ]
2066 });
2067  /*
2068  * - LGPL
2069  *
2070  * messagebox - can be used as a replace
2071  * 
2072  */
2073 /**
2074  * @class Roo.MessageBox
2075  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2076  * Example usage:
2077  *<pre><code>
2078 // Basic alert:
2079 Roo.Msg.alert('Status', 'Changes saved successfully.');
2080
2081 // Prompt for user data:
2082 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2083     if (btn == 'ok'){
2084         // process text value...
2085     }
2086 });
2087
2088 // Show a dialog using config options:
2089 Roo.Msg.show({
2090    title:'Save Changes?',
2091    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2092    buttons: Roo.Msg.YESNOCANCEL,
2093    fn: processResult,
2094    animEl: 'elId'
2095 });
2096 </code></pre>
2097  * @singleton
2098  */
2099 Roo.bootstrap.MessageBox = function(){
2100     var dlg, opt, mask, waitTimer;
2101     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2102     var buttons, activeTextEl, bwidth;
2103
2104     
2105     // private
2106     var handleButton = function(button){
2107         dlg.hide();
2108         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2109     };
2110
2111     // private
2112     var handleHide = function(){
2113         if(opt && opt.cls){
2114             dlg.el.removeClass(opt.cls);
2115         }
2116         //if(waitTimer){
2117         //    Roo.TaskMgr.stop(waitTimer);
2118         //    waitTimer = null;
2119         //}
2120     };
2121
2122     // private
2123     var updateButtons = function(b){
2124         var width = 0;
2125         if(!b){
2126             buttons["ok"].hide();
2127             buttons["cancel"].hide();
2128             buttons["yes"].hide();
2129             buttons["no"].hide();
2130             //dlg.footer.dom.style.display = 'none';
2131             return width;
2132         }
2133         dlg.footer.dom.style.display = '';
2134         for(var k in buttons){
2135             if(typeof buttons[k] != "function"){
2136                 if(b[k]){
2137                     buttons[k].show();
2138                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2139                     width += buttons[k].el.getWidth()+15;
2140                 }else{
2141                     buttons[k].hide();
2142                 }
2143             }
2144         }
2145         return width;
2146     };
2147
2148     // private
2149     var handleEsc = function(d, k, e){
2150         if(opt && opt.closable !== false){
2151             dlg.hide();
2152         }
2153         if(e){
2154             e.stopEvent();
2155         }
2156     };
2157
2158     return {
2159         /**
2160          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2161          * @return {Roo.BasicDialog} The BasicDialog element
2162          */
2163         getDialog : function(){
2164            if(!dlg){
2165                 dlg = new Roo.bootstrap.Modal( {
2166                     //draggable: true,
2167                     //resizable:false,
2168                     //constraintoviewport:false,
2169                     //fixedcenter:true,
2170                     //collapsible : false,
2171                     //shim:true,
2172                     //modal: true,
2173                   //  width:400,
2174                   //  height:100,
2175                     //buttonAlign:"center",
2176                     closeClick : function(){
2177                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2178                             handleButton("no");
2179                         }else{
2180                             handleButton("cancel");
2181                         }
2182                     }
2183                 });
2184                 dlg.render();
2185                 dlg.on("hide", handleHide);
2186                 mask = dlg.mask;
2187                 //dlg.addKeyListener(27, handleEsc);
2188                 buttons = {};
2189                 this.buttons = buttons;
2190                 var bt = this.buttonText;
2191                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2192                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2193                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2194                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2195                 Roo.log(buttons)
2196                 bodyEl = dlg.body.createChild({
2197
2198                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2199                         '<textarea class="roo-mb-textarea"></textarea>' +
2200                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2201                 });
2202                 msgEl = bodyEl.dom.firstChild;
2203                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2204                 textboxEl.enableDisplayMode();
2205                 textboxEl.addKeyListener([10,13], function(){
2206                     if(dlg.isVisible() && opt && opt.buttons){
2207                         if(opt.buttons.ok){
2208                             handleButton("ok");
2209                         }else if(opt.buttons.yes){
2210                             handleButton("yes");
2211                         }
2212                     }
2213                 });
2214                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2215                 textareaEl.enableDisplayMode();
2216                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2217                 progressEl.enableDisplayMode();
2218                 var pf = progressEl.dom.firstChild;
2219                 if (pf) {
2220                     pp = Roo.get(pf.firstChild);
2221                     pp.setHeight(pf.offsetHeight);
2222                 }
2223                 
2224             }
2225             return dlg;
2226         },
2227
2228         /**
2229          * Updates the message box body text
2230          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2231          * the XHTML-compliant non-breaking space character '&amp;#160;')
2232          * @return {Roo.MessageBox} This message box
2233          */
2234         updateText : function(text){
2235             if(!dlg.isVisible() && !opt.width){
2236                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2237             }
2238             msgEl.innerHTML = text || '&#160;';
2239       
2240             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2241             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2242             var w = Math.max(
2243                     Math.min(opt.width || cw , this.maxWidth), 
2244                     Math.max(opt.minWidth || this.minWidth, bwidth)
2245             );
2246             if(opt.prompt){
2247                 activeTextEl.setWidth(w);
2248             }
2249             if(dlg.isVisible()){
2250                 dlg.fixedcenter = false;
2251             }
2252             // to big, make it scroll. = But as usual stupid IE does not support
2253             // !important..
2254             
2255             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2256                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2257                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2258             } else {
2259                 bodyEl.dom.style.height = '';
2260                 bodyEl.dom.style.overflowY = '';
2261             }
2262             if (cw > w) {
2263                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2264             } else {
2265                 bodyEl.dom.style.overflowX = '';
2266             }
2267             
2268             dlg.setContentSize(w, bodyEl.getHeight());
2269             if(dlg.isVisible()){
2270                 dlg.fixedcenter = true;
2271             }
2272             return this;
2273         },
2274
2275         /**
2276          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2277          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2278          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2279          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2280          * @return {Roo.MessageBox} This message box
2281          */
2282         updateProgress : function(value, text){
2283             if(text){
2284                 this.updateText(text);
2285             }
2286             if (pp) { // weird bug on my firefox - for some reason this is not defined
2287                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2288             }
2289             return this;
2290         },        
2291
2292         /**
2293          * Returns true if the message box is currently displayed
2294          * @return {Boolean} True if the message box is visible, else false
2295          */
2296         isVisible : function(){
2297             return dlg && dlg.isVisible();  
2298         },
2299
2300         /**
2301          * Hides the message box if it is displayed
2302          */
2303         hide : function(){
2304             if(this.isVisible()){
2305                 dlg.hide();
2306             }  
2307         },
2308
2309         /**
2310          * Displays a new message box, or reinitializes an existing message box, based on the config options
2311          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2312          * The following config object properties are supported:
2313          * <pre>
2314 Property    Type             Description
2315 ----------  ---------------  ------------------------------------------------------------------------------------
2316 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2317                                    closes (defaults to undefined)
2318 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2319                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2320 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2321                                    progress and wait dialogs will ignore this property and always hide the
2322                                    close button as they can only be closed programmatically.
2323 cls               String           A custom CSS class to apply to the message box element
2324 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2325                                    displayed (defaults to 75)
2326 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2327                                    function will be btn (the name of the button that was clicked, if applicable,
2328                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2329                                    Progress and wait dialogs will ignore this option since they do not respond to
2330                                    user actions and can only be closed programmatically, so any required function
2331                                    should be called by the same code after it closes the dialog.
2332 icon              String           A CSS class that provides a background image to be used as an icon for
2333                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2334 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2335 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2336 modal             Boolean          False to allow user interaction with the page while the message box is
2337                                    displayed (defaults to true)
2338 msg               String           A string that will replace the existing message box body text (defaults
2339                                    to the XHTML-compliant non-breaking space character '&#160;')
2340 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2341 progress          Boolean          True to display a progress bar (defaults to false)
2342 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2343 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2344 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2345 title             String           The title text
2346 value             String           The string value to set into the active textbox element if displayed
2347 wait              Boolean          True to display a progress bar (defaults to false)
2348 width             Number           The width of the dialog in pixels
2349 </pre>
2350          *
2351          * Example usage:
2352          * <pre><code>
2353 Roo.Msg.show({
2354    title: 'Address',
2355    msg: 'Please enter your address:',
2356    width: 300,
2357    buttons: Roo.MessageBox.OKCANCEL,
2358    multiline: true,
2359    fn: saveAddress,
2360    animEl: 'addAddressBtn'
2361 });
2362 </code></pre>
2363          * @param {Object} config Configuration options
2364          * @return {Roo.MessageBox} This message box
2365          */
2366         show : function(options)
2367         {
2368             
2369             // this causes nightmares if you show one dialog after another
2370             // especially on callbacks..
2371              
2372             if(this.isVisible()){
2373                 
2374                 this.hide();
2375                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2376                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2377                 Roo.log("New Dialog Message:" +  options.msg )
2378                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2379                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2380                 
2381             }
2382             var d = this.getDialog();
2383             opt = options;
2384             d.setTitle(opt.title || "&#160;");
2385             d.close.setDisplayed(opt.closable !== false);
2386             activeTextEl = textboxEl;
2387             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2388             if(opt.prompt){
2389                 if(opt.multiline){
2390                     textboxEl.hide();
2391                     textareaEl.show();
2392                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2393                         opt.multiline : this.defaultTextHeight);
2394                     activeTextEl = textareaEl;
2395                 }else{
2396                     textboxEl.show();
2397                     textareaEl.hide();
2398                 }
2399             }else{
2400                 textboxEl.hide();
2401                 textareaEl.hide();
2402             }
2403             progressEl.setDisplayed(opt.progress === true);
2404             this.updateProgress(0);
2405             activeTextEl.dom.value = opt.value || "";
2406             if(opt.prompt){
2407                 dlg.setDefaultButton(activeTextEl);
2408             }else{
2409                 var bs = opt.buttons;
2410                 var db = null;
2411                 if(bs && bs.ok){
2412                     db = buttons["ok"];
2413                 }else if(bs && bs.yes){
2414                     db = buttons["yes"];
2415                 }
2416                 dlg.setDefaultButton(db);
2417             }
2418             bwidth = updateButtons(opt.buttons);
2419             this.updateText(opt.msg);
2420             if(opt.cls){
2421                 d.el.addClass(opt.cls);
2422             }
2423             d.proxyDrag = opt.proxyDrag === true;
2424             d.modal = opt.modal !== false;
2425             d.mask = opt.modal !== false ? mask : false;
2426             if(!d.isVisible()){
2427                 // force it to the end of the z-index stack so it gets a cursor in FF
2428                 document.body.appendChild(dlg.el.dom);
2429                 d.animateTarget = null;
2430                 d.show(options.animEl);
2431             }
2432             return this;
2433         },
2434
2435         /**
2436          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2437          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2438          * and closing the message box when the process is complete.
2439          * @param {String} title The title bar text
2440          * @param {String} msg The message box body text
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         progress : function(title, msg){
2444             this.show({
2445                 title : title,
2446                 msg : msg,
2447                 buttons: false,
2448                 progress:true,
2449                 closable:false,
2450                 minWidth: this.minProgressWidth,
2451                 modal : true
2452             });
2453             return this;
2454         },
2455
2456         /**
2457          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2458          * If a callback function is passed it will be called after the user clicks the button, and the
2459          * id of the button that was clicked will be passed as the only parameter to the callback
2460          * (could also be the top-right close button).
2461          * @param {String} title The title bar text
2462          * @param {String} msg The message box body text
2463          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2464          * @param {Object} scope (optional) The scope of the callback function
2465          * @return {Roo.MessageBox} This message box
2466          */
2467         alert : function(title, msg, fn, scope){
2468             this.show({
2469                 title : title,
2470                 msg : msg,
2471                 buttons: this.OK,
2472                 fn: fn,
2473                 scope : scope,
2474                 modal : true
2475             });
2476             return this;
2477         },
2478
2479         /**
2480          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2481          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2482          * You are responsible for closing the message box when the process is complete.
2483          * @param {String} msg The message box body text
2484          * @param {String} title (optional) The title bar text
2485          * @return {Roo.MessageBox} This message box
2486          */
2487         wait : function(msg, title){
2488             this.show({
2489                 title : title,
2490                 msg : msg,
2491                 buttons: false,
2492                 closable:false,
2493                 progress:true,
2494                 modal:true,
2495                 width:300,
2496                 wait:true
2497             });
2498             waitTimer = Roo.TaskMgr.start({
2499                 run: function(i){
2500                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2501                 },
2502                 interval: 1000
2503             });
2504             return this;
2505         },
2506
2507         /**
2508          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2509          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2510          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2511          * @param {String} title The title bar text
2512          * @param {String} msg The message box body text
2513          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2514          * @param {Object} scope (optional) The scope of the callback function
2515          * @return {Roo.MessageBox} This message box
2516          */
2517         confirm : function(title, msg, fn, scope){
2518             this.show({
2519                 title : title,
2520                 msg : msg,
2521                 buttons: this.YESNO,
2522                 fn: fn,
2523                 scope : scope,
2524                 modal : true
2525             });
2526             return this;
2527         },
2528
2529         /**
2530          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2531          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2532          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2533          * (could also be the top-right close button) and the text that was entered will be passed as the two
2534          * parameters to the callback.
2535          * @param {String} title The title bar text
2536          * @param {String} msg The message box body text
2537          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2538          * @param {Object} scope (optional) The scope of the callback function
2539          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2540          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2541          * @return {Roo.MessageBox} This message box
2542          */
2543         prompt : function(title, msg, fn, scope, multiline){
2544             this.show({
2545                 title : title,
2546                 msg : msg,
2547                 buttons: this.OKCANCEL,
2548                 fn: fn,
2549                 minWidth:250,
2550                 scope : scope,
2551                 prompt:true,
2552                 multiline: multiline,
2553                 modal : true
2554             });
2555             return this;
2556         },
2557
2558         /**
2559          * Button config that displays a single OK button
2560          * @type Object
2561          */
2562         OK : {ok:true},
2563         /**
2564          * Button config that displays Yes and No buttons
2565          * @type Object
2566          */
2567         YESNO : {yes:true, no:true},
2568         /**
2569          * Button config that displays OK and Cancel buttons
2570          * @type Object
2571          */
2572         OKCANCEL : {ok:true, cancel:true},
2573         /**
2574          * Button config that displays Yes, No and Cancel buttons
2575          * @type Object
2576          */
2577         YESNOCANCEL : {yes:true, no:true, cancel:true},
2578
2579         /**
2580          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2581          * @type Number
2582          */
2583         defaultTextHeight : 75,
2584         /**
2585          * The maximum width in pixels of the message box (defaults to 600)
2586          * @type Number
2587          */
2588         maxWidth : 600,
2589         /**
2590          * The minimum width in pixels of the message box (defaults to 100)
2591          * @type Number
2592          */
2593         minWidth : 100,
2594         /**
2595          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2596          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2597          * @type Number
2598          */
2599         minProgressWidth : 250,
2600         /**
2601          * An object containing the default button text strings that can be overriden for localized language support.
2602          * Supported properties are: ok, cancel, yes and no.
2603          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2604          * @type Object
2605          */
2606         buttonText : {
2607             ok : "OK",
2608             cancel : "Cancel",
2609             yes : "Yes",
2610             no : "No"
2611         }
2612     };
2613 }();
2614
2615 /**
2616  * Shorthand for {@link Roo.MessageBox}
2617  */
2618 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2619 Roo.Msg = Roo.Msg || Roo.MessageBox;
2620 /*
2621  * - LGPL
2622  *
2623  * navbar
2624  * 
2625  */
2626
2627 /**
2628  * @class Roo.bootstrap.Navbar
2629  * @extends Roo.bootstrap.Component
2630  * Bootstrap Navbar class
2631  * @cfg {Boolean} sidebar has side bar
2632  * @cfg {Boolean} bar is a bar?
2633  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2634  * @cfg {String} brand what is brand
2635  * @cfg {Boolean} inverse is inverted color
2636  * @cfg {String} type (nav | pills | tabs)
2637  * @cfg {Boolean} arrangement stacked | justified
2638  * @cfg {String} align (left | right) alignment
2639  * @cfg {String} brand_href href of the brand
2640  * @cfg {Boolean} main (true|false) main nav bar? default false
2641  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2642  *
2643  * 
2644  * @constructor
2645  * Create a new Navbar
2646  * @param {Object} config The config object
2647  */
2648
2649
2650 Roo.bootstrap.Navbar = function(config){
2651     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2652    
2653     
2654 };
2655
2656 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2657     
2658     sidebar: false,
2659     
2660     bar: false,
2661     brand: '',
2662     inverse: false,
2663     position: '',
2664     align : false,
2665     type: 'nav',
2666     arrangement: '',
2667     brand_href: false,
2668     main : false,
2669     loadMask : false,
2670     
2671     
2672     // private
2673     navItems : false,
2674     
2675     getAutoCreate : function(){
2676         var cfg = {
2677             cls : 'navbar'
2678         };
2679         
2680         if (this.sidebar === true) {
2681             cfg = {
2682                 tag: 'div',
2683                 cls: 'sidebar-nav'
2684             };
2685             return cfg;
2686         }
2687         
2688         if (this.bar === true) {
2689             cfg = {
2690                 tag: 'nav',
2691                 cls: 'navbar',
2692                 role: 'navigation',
2693                 cn: [
2694                     {
2695                         tag: 'div',
2696                         cls: 'navbar-header',
2697                         cn: [
2698                             {
2699                             tag: 'button',
2700                             type: 'button',
2701                             cls: 'navbar-toggle',
2702                             'data-toggle': 'collapse',
2703                             cn: [
2704                                 {
2705                                     tag: 'span',
2706                                     cls: 'sr-only',
2707                                     html: 'Toggle navigation'
2708                                 },
2709                                 {
2710                                     tag: 'span',
2711                                     cls: 'icon-bar'
2712                                 },
2713                                 {
2714                                     tag: 'span',
2715                                     cls: 'icon-bar'
2716                                 },
2717                                 {
2718                                     tag: 'span',
2719                                     cls: 'icon-bar'
2720                                 }
2721                             ]
2722                             }
2723                         ]
2724                     },
2725                     {
2726                     tag: 'div',
2727                     cls: 'collapse navbar-collapse'
2728                     }
2729                 ]
2730             };
2731             
2732             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2733             
2734             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2735                 cfg.cls += ' navbar-' + this.position;
2736                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2737             }
2738             
2739             if (this.brand !== '') {
2740                 cfg.cn[0].cn.push({
2741                     tag: 'a',
2742                     href: this.brand_href ? this.brand_href : '#',
2743                     cls: 'navbar-brand',
2744                     cn: [
2745                     this.brand
2746                     ]
2747                 });
2748             }
2749             
2750             if(this.main){
2751                 cfg.cls += ' main-nav';
2752             }
2753             
2754             
2755             return cfg;
2756         
2757         } else if (this.bar === false) {
2758             
2759         } else {
2760             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2761         }
2762         
2763         cfg.cn = [
2764             {
2765                 cls: 'nav',
2766                 tag : 'ul'
2767             }
2768         ];
2769         
2770         if (['tabs','pills'].indexOf(this.type)!==-1) {
2771             cfg.cn[0].cls += ' nav-' + this.type
2772         } else {
2773             if (this.type!=='nav') {
2774             Roo.log('nav type must be nav/tabs/pills')
2775             }
2776             cfg.cn[0].cls += ' navbar-nav'
2777         }
2778         
2779         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2780             cfg.cn[0].cls += ' nav-' + this.arrangement;
2781         }
2782         
2783         if (this.align === 'right') {
2784             cfg.cn[0].cls += ' navbar-right';
2785         }
2786         if (this.inverse) {
2787             cfg.cls += ' navbar-inverse';
2788             
2789         }
2790         
2791         
2792         return cfg;
2793     },
2794     
2795     initEvents :function ()
2796     {
2797         //Roo.log(this.el.select('.navbar-toggle',true));
2798         this.el.select('.navbar-toggle',true).on('click', function() {
2799            // Roo.log('click');
2800             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2801         }, this);
2802         
2803         var mark = {
2804             tag: "div",
2805             cls:"x-dlg-mask"
2806         }
2807         
2808         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2809         
2810         var size = this.el.getSize();
2811         this.maskEl.setSize(size.width, size.height);
2812         this.maskEl.enableDisplayMode("block");
2813         this.maskEl.hide();
2814         
2815         if(this.loadMask){
2816             this.maskEl.show();
2817         }
2818     },
2819     
2820     
2821     getChildContainer : function()
2822     {
2823         if (this.bar === true) {
2824             return this.el.select('.collapse',true).first();
2825         }
2826         
2827         return this.el;
2828     },
2829     
2830     mask : function()
2831     {
2832         this.maskEl.show();
2833     },
2834     
2835     unmask : function()
2836     {
2837         this.maskEl.hide();
2838     }
2839     
2840     
2841     
2842 });
2843
2844
2845
2846  
2847
2848  /*
2849  * - LGPL
2850  *
2851  * nav group
2852  * 
2853  */
2854
2855 /**
2856  * @class Roo.bootstrap.NavGroup
2857  * @extends Roo.bootstrap.Component
2858  * Bootstrap NavGroup class
2859  * @cfg {String} align left | right
2860  * @cfg {Boolean} inverse false | true
2861  * @cfg {String} type (nav|pills|tab) default nav
2862  * @cfg {String} navId - reference Id for navbar.
2863
2864  * 
2865  * @constructor
2866  * Create a new nav group
2867  * @param {Object} config The config object
2868  */
2869
2870 Roo.bootstrap.NavGroup = function(config){
2871     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2872     this.navItems = [];
2873     Roo.bootstrap.NavGroup.register(this);
2874      this.addEvents({
2875         /**
2876              * @event changed
2877              * Fires when the active item changes
2878              * @param {Roo.bootstrap.NavGroup} this
2879              * @param {Roo.bootstrap.Navbar.Item} item The item selected
2880              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
2881          */
2882         'changed': true
2883      });
2884     
2885 };
2886
2887 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2888     
2889     align: '',
2890     inverse: false,
2891     form: false,
2892     type: 'nav',
2893     navId : '',
2894     // private
2895     
2896     navItems : false,
2897     
2898     getAutoCreate : function()
2899     {
2900         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2901         
2902         cfg = {
2903             tag : 'ul',
2904             cls: 'nav' 
2905         }
2906         
2907         if (['tabs','pills'].indexOf(this.type)!==-1) {
2908             cfg.cls += ' nav-' + this.type
2909         } else {
2910             if (this.type!=='nav') {
2911                 Roo.log('nav type must be nav/tabs/pills')
2912             }
2913             cfg.cls += ' navbar-nav'
2914         }
2915         
2916         if (this.parent().sidebar === true) {
2917             cfg = {
2918                 tag: 'ul',
2919                 cls: 'dashboard-menu'
2920             }
2921             
2922             return cfg;
2923         }
2924         
2925         if (this.form === true) {
2926             cfg = {
2927                 tag: 'form',
2928                 cls: 'navbar-form'
2929             }
2930             
2931             if (this.align === 'right') {
2932                 cfg.cls += ' navbar-right';
2933             } else {
2934                 cfg.cls += ' navbar-left';
2935             }
2936         }
2937         
2938         if (this.align === 'right') {
2939             cfg.cls += ' navbar-right';
2940         }
2941         
2942         if (this.inverse) {
2943             cfg.cls += ' navbar-inverse';
2944             
2945         }
2946         
2947         
2948         return cfg;
2949     },
2950     
2951     setActiveItem : function(item)
2952     {
2953         var prev = false;
2954         Roo.each(this.navItems, function(v){
2955             if (v == item) {
2956                 return ;
2957             }
2958             if (v.isActive()) {
2959                 v.setActive(false, true);
2960                 prev = v;
2961                 
2962             }
2963             
2964         });
2965
2966         item.setActive(true, true);
2967         this.fireEvent('changed', this, item, prev);
2968         
2969         
2970     },
2971     
2972     
2973     register : function(item)
2974     {
2975         this.navItems.push( item);
2976         item.navId = this.navId;
2977     
2978     },
2979     getNavItem: function(tabId)
2980     {
2981         var ret = false;
2982         Roo.each(this.navItems, function(e) {
2983             if (e.tabId == tabId) {
2984                ret =  e;
2985                return false;
2986             }
2987             return true;
2988             
2989         });
2990         return ret;
2991     }
2992 });
2993
2994  
2995 Roo.apply(Roo.bootstrap.NavGroup, {
2996     
2997     groups: {},
2998     
2999     register : function(navgrp)
3000     {
3001         this.groups[navgrp.navId] = navgrp;
3002         
3003     },
3004     get: function(navId) {
3005         return this.groups[navId];
3006     }
3007     
3008     
3009     
3010 });
3011
3012  /*
3013  * - LGPL
3014  *
3015  * row
3016  * 
3017  */
3018
3019 /**
3020  * @class Roo.bootstrap.Navbar.Item
3021  * @extends Roo.bootstrap.Component
3022  * Bootstrap Navbar.Button class
3023  * @cfg {String} href  link to
3024  * @cfg {String} html content of button
3025  * @cfg {String} badge text inside badge
3026  * @cfg {String} glyphicon name of glyphicon
3027  * @cfg {String} icon name of font awesome icon
3028  * @cfg {Boolean} active Is item active
3029  * @cfg {Boolean} preventDefault (true | false) default false
3030  * @cfg {String} tabId the tab that this item activates.
3031   
3032  * @constructor
3033  * Create a new Navbar Button
3034  * @param {Object} config The config object
3035  */
3036 Roo.bootstrap.Navbar.Item = function(config){
3037     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3038     this.addEvents({
3039         // raw events
3040         /**
3041          * @event click
3042          * The raw click event for the entire grid.
3043          * @param {Roo.EventObject} e
3044          */
3045         "click" : true,
3046          /**
3047             * @event changed
3048             * Fires when the active item active state changes
3049             * @param {Roo.bootstrap.Navbar.Item} this
3050             * @param {boolean} state the new state
3051              
3052          */
3053         'changed': true
3054     });
3055    
3056 };
3057
3058 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3059     
3060     href: false,
3061     html: '',
3062     badge: '',
3063     icon: false,
3064     glyphicon: false,
3065     active: false,
3066     preventDefault : false,
3067     tabId : false,
3068     
3069     getAutoCreate : function(){
3070         
3071         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3072         
3073         if (this.parent().parent().sidebar === true) {
3074             cfg = {
3075                 tag: 'li',
3076                 cls: '',
3077                 cn: [
3078                     {
3079                         tag: 'p',
3080                         cls: ''
3081                     }
3082                 ]
3083             }
3084             
3085             if (this.html) {
3086                 cfg.cn[0].html = this.html;
3087             }
3088             
3089             if (this.active) {
3090                 this.cls += ' active';
3091             }
3092             
3093             if (this.menu) {
3094                 cfg.cn[0].cls += ' dropdown-toggle';
3095                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3096             }
3097             
3098             if (this.href) {
3099                 cfg.cn[0].tag = 'a',
3100                 cfg.cn[0].href = this.href;
3101             }
3102             
3103             if (this.glyphicon) {
3104                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3105             }
3106             
3107             if (this.icon) {
3108                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3109             }
3110             
3111             return cfg;
3112         }
3113         
3114         cfg = {
3115             tag: 'li',
3116             cls: 'nav-item'
3117         }
3118         
3119         if (this.active) {
3120             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3121         }
3122             
3123         cfg.cn = [
3124             {
3125                 tag: 'p',
3126                 html: 'Text'
3127             }
3128         ];
3129         
3130         if (this.glyphicon) {
3131             if(cfg.html){cfg.html = ' ' + this.html};
3132             cfg.cn=[
3133                 {
3134                     tag: 'span',
3135                     cls: 'glyphicon glyphicon-' + this.glyphicon
3136                 }
3137             ];
3138         }
3139         
3140         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3141         
3142         if (this.menu) {
3143             cfg.cn[0].tag='a';
3144             cfg.cn[0].href='#';
3145             cfg.cn[0].html += " <span class='caret'></span>";
3146         //}else if (!this.href) {
3147         //    cfg.cn[0].tag='p';
3148         //    cfg.cn[0].cls='navbar-text';
3149         } else {
3150             cfg.cn[0].tag='a';
3151             cfg.cn[0].href=this.href||'#';
3152             cfg.cn[0].html=this.html;
3153         }
3154         
3155         if (this.badge !== '') {
3156             
3157             cfg.cn[0].cn=[
3158                 cfg.cn[0].html + ' ',
3159                 {
3160                     tag: 'span',
3161                     cls: 'badge',
3162                     html: this.badge
3163                 }
3164             ];
3165             cfg.cn[0].html=''
3166         }
3167          
3168         if (this.icon) {
3169             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3170         }
3171         
3172         return cfg;
3173     },
3174     initEvents: function() {
3175        // Roo.log('init events?');
3176        // Roo.log(this.el.dom);
3177         this.el.select('a',true).on('click', this.onClick, this);
3178         // at this point parent should be available..
3179         this.parent().register(this);
3180     },
3181     
3182     onClick : function(e)
3183     {
3184         if(this.preventDefault){
3185             e.preventDefault();
3186         }
3187         
3188         if(this.fireEvent('click', this, e) === false){
3189             return;
3190         };
3191         
3192         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3193              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3194                 this.parent().setActiveItem(this);
3195             }
3196             
3197             
3198             
3199         } 
3200     },
3201     
3202     isActive: function () {
3203         return this.active
3204     },
3205     setActive : function(state, fire)
3206     {
3207         this.active = state;
3208         if (!state ) {
3209             this.el.removeClass('active');
3210         } else if (!this.el.hasClass('active')) {
3211             this.el.addClass('active');
3212         }
3213         if (fire) {
3214             this.fireEvent('changed', this, state);
3215         }
3216         
3217         
3218     }
3219      // this should not be here...
3220  
3221 });
3222  
3223
3224  /*
3225  * - LGPL
3226  *
3227  * row
3228  * 
3229  */
3230
3231 /**
3232  * @class Roo.bootstrap.Row
3233  * @extends Roo.bootstrap.Component
3234  * Bootstrap Row class (contains columns...)
3235  * 
3236  * @constructor
3237  * Create a new Row
3238  * @param {Object} config The config object
3239  */
3240
3241 Roo.bootstrap.Row = function(config){
3242     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3243 };
3244
3245 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3246     
3247     getAutoCreate : function(){
3248        return {
3249             cls: 'row clearfix'
3250        };
3251     }
3252     
3253     
3254 });
3255
3256  
3257
3258  /*
3259  * - LGPL
3260  *
3261  * element
3262  * 
3263  */
3264
3265 /**
3266  * @class Roo.bootstrap.Element
3267  * @extends Roo.bootstrap.Component
3268  * Bootstrap Element class
3269  * @cfg {String} html contents of the element
3270  * @cfg {String} tag tag of the element
3271  * @cfg {String} cls class of the element
3272  * 
3273  * @constructor
3274  * Create a new Element
3275  * @param {Object} config The config object
3276  */
3277
3278 Roo.bootstrap.Element = function(config){
3279     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3280 };
3281
3282 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3283     
3284     tag: 'div',
3285     cls: '',
3286     html: '',
3287      
3288     
3289     getAutoCreate : function(){
3290         
3291         var cfg = {
3292             tag: this.tag,
3293             cls: this.cls,
3294             html: this.html
3295         }
3296         
3297         
3298         
3299         return cfg;
3300     }
3301    
3302 });
3303
3304  
3305
3306  /*
3307  * - LGPL
3308  *
3309  * pagination
3310  * 
3311  */
3312
3313 /**
3314  * @class Roo.bootstrap.Pagination
3315  * @extends Roo.bootstrap.Component
3316  * Bootstrap Pagination class
3317  * @cfg {String} size xs | sm | md | lg
3318  * @cfg {Boolean} inverse false | true
3319  * 
3320  * @constructor
3321  * Create a new Pagination
3322  * @param {Object} config The config object
3323  */
3324
3325 Roo.bootstrap.Pagination = function(config){
3326     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3327 };
3328
3329 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3330     
3331     cls: false,
3332     size: false,
3333     inverse: false,
3334     
3335     getAutoCreate : function(){
3336         var cfg = {
3337             tag: 'ul',
3338                 cls: 'pagination'
3339         };
3340         if (this.inverse) {
3341             cfg.cls += ' inverse';
3342         }
3343         if (this.html) {
3344             cfg.html=this.html;
3345         }
3346         if (this.cls) {
3347             cfg.cls += " " + this.cls;
3348         }
3349         return cfg;
3350     }
3351    
3352 });
3353
3354  
3355
3356  /*
3357  * - LGPL
3358  *
3359  * Pagination item
3360  * 
3361  */
3362
3363
3364 /**
3365  * @class Roo.bootstrap.PaginationItem
3366  * @extends Roo.bootstrap.Component
3367  * Bootstrap PaginationItem class
3368  * @cfg {String} html text
3369  * @cfg {String} href the link
3370  * @cfg {Boolean} preventDefault (true | false) default true
3371  * @cfg {Boolean} active (true | false) default false
3372  * 
3373  * 
3374  * @constructor
3375  * Create a new PaginationItem
3376  * @param {Object} config The config object
3377  */
3378
3379
3380 Roo.bootstrap.PaginationItem = function(config){
3381     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3382     this.addEvents({
3383         // raw events
3384         /**
3385          * @event click
3386          * The raw click event for the entire grid.
3387          * @param {Roo.EventObject} e
3388          */
3389         "click" : true
3390     });
3391 };
3392
3393 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3394     
3395     href : false,
3396     html : false,
3397     preventDefault: true,
3398     active : false,
3399     cls : false,
3400     
3401     getAutoCreate : function(){
3402         var cfg= {
3403             tag: 'li',
3404             cn: [
3405                 {
3406                     tag : 'a',
3407                     href : this.href ? this.href : '#',
3408                     html : this.html ? this.html : ''
3409                 }
3410             ]
3411         };
3412         
3413         if(this.cls){
3414             cfg.cls = this.cls;
3415         }
3416         
3417         if(this.active){
3418             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3419         }
3420         
3421         return cfg;
3422     },
3423     
3424     initEvents: function() {
3425         
3426         this.el.on('click', this.onClick, this);
3427         
3428     },
3429     onClick : function(e)
3430     {
3431         Roo.log('PaginationItem on click ');
3432         if(this.preventDefault){
3433             e.preventDefault();
3434         }
3435         
3436         this.fireEvent('click', this, e);
3437     }
3438    
3439 });
3440
3441  
3442
3443  /*
3444  * - LGPL
3445  *
3446  * slider
3447  * 
3448  */
3449
3450
3451 /**
3452  * @class Roo.bootstrap.Slider
3453  * @extends Roo.bootstrap.Component
3454  * Bootstrap Slider class
3455  *    
3456  * @constructor
3457  * Create a new Slider
3458  * @param {Object} config The config object
3459  */
3460
3461 Roo.bootstrap.Slider = function(config){
3462     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3463 };
3464
3465 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3466     
3467     getAutoCreate : function(){
3468         
3469         var cfg = {
3470             tag: 'div',
3471             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3472             cn: [
3473                 {
3474                     tag: 'a',
3475                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3476                 }
3477             ]
3478         }
3479         
3480         return cfg;
3481     }
3482    
3483 });
3484
3485  /*
3486  * - LGPL
3487  *
3488  * table
3489  * 
3490  */
3491
3492 /**
3493  * @class Roo.bootstrap.Table
3494  * @extends Roo.bootstrap.Component
3495  * Bootstrap Table class
3496  * @cfg {String} cls table class
3497  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3498  * @cfg {String} bgcolor Specifies the background color for a table
3499  * @cfg {Number} border Specifies whether the table cells should have borders or not
3500  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3501  * @cfg {Number} cellspacing Specifies the space between cells
3502  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3503  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3504  * @cfg {String} sortable Specifies that the table should be sortable
3505  * @cfg {String} summary Specifies a summary of the content of a table
3506  * @cfg {Number} width Specifies the width of a table
3507  * 
3508  * @cfg {boolean} striped Should the rows be alternative striped
3509  * @cfg {boolean} bordered Add borders to the table
3510  * @cfg {boolean} hover Add hover highlighting
3511  * @cfg {boolean} condensed Format condensed
3512  * @cfg {boolean} responsive Format condensed
3513  *
3514  
3515  
3516  * 
3517  * @constructor
3518  * Create a new Table
3519  * @param {Object} config The config object
3520  */
3521
3522 Roo.bootstrap.Table = function(config){
3523     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3524     
3525     if (this.sm) {
3526         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3527         this.sm = this.selModel;
3528         this.sm.xmodule = this.xmodule || false;
3529     }
3530     if (this.cm && typeof(this.cm.config) == 'undefined') {
3531         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3532         this.cm = this.colModel;
3533         this.cm.xmodule = this.xmodule || false;
3534     }
3535     if (this.store) {
3536         this.store= Roo.factory(this.store, Roo.data);
3537         this.ds = this.store;
3538         this.ds.xmodule = this.xmodule || false;
3539          
3540     }
3541 };
3542
3543 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3544     
3545     cls: false,
3546     align: false,
3547     bgcolor: false,
3548     border: false,
3549     cellpadding: false,
3550     cellspacing: false,
3551     frame: false,
3552     rules: false,
3553     sortable: false,
3554     summary: false,
3555     width: false,
3556     striped : false,
3557     bordered: false,
3558     hover:  false,
3559     condensed : false,
3560     responsive : false,
3561     sm : false,
3562     cm : false,
3563     store : false,
3564     
3565     getAutoCreate : function(){
3566         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3567         
3568         cfg = {
3569             tag: 'table',
3570             cls : 'table',
3571             cn : []
3572         }
3573             
3574         if (this.striped) {
3575             cfg.cls += ' table-striped';
3576         }
3577         if (this.hover) {
3578             cfg.cls += ' table-hover';
3579         }
3580         if (this.bordered) {
3581             cfg.cls += ' table-bordered';
3582         }
3583         if (this.condensed) {
3584             cfg.cls += ' table-condensed';
3585         }
3586         if (this.responsive) {
3587             cfg.cls += ' table-responsive';
3588         }
3589         
3590           
3591         
3592         
3593         if (this.cls) {
3594             cfg.cls+=  ' ' +this.cls;
3595         }
3596         
3597         // this lot should be simplifed...
3598         
3599         if (this.align) {
3600             cfg.align=this.align;
3601         }
3602         if (this.bgcolor) {
3603             cfg.bgcolor=this.bgcolor;
3604         }
3605         if (this.border) {
3606             cfg.border=this.border;
3607         }
3608         if (this.cellpadding) {
3609             cfg.cellpadding=this.cellpadding;
3610         }
3611         if (this.cellspacing) {
3612             cfg.cellspacing=this.cellspacing;
3613         }
3614         if (this.frame) {
3615             cfg.frame=this.frame;
3616         }
3617         if (this.rules) {
3618             cfg.rules=this.rules;
3619         }
3620         if (this.sortable) {
3621             cfg.sortable=this.sortable;
3622         }
3623         if (this.summary) {
3624             cfg.summary=this.summary;
3625         }
3626         if (this.width) {
3627             cfg.width=this.width;
3628         }
3629         
3630         if(this.store || this.cm){
3631             cfg.cn.push(this.renderHeader());
3632             cfg.cn.push(this.renderBody());
3633             cfg.cn.push(this.renderFooter());
3634             
3635             cfg.cls+=  ' TableGrid';
3636         }
3637         
3638         return cfg;
3639     },
3640 //    
3641 //    initTableGrid : function()
3642 //    {
3643 //        var cfg = {};
3644 //        
3645 //        var header = {
3646 //            tag: 'thead',
3647 //            cn : []
3648 //        };
3649 //        
3650 //        var cm = this.cm;
3651 //        
3652 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3653 //            header.cn.push({
3654 //                tag: 'th',
3655 //                html: cm.getColumnHeader(i)
3656 //            })
3657 //        }
3658 //        
3659 //        cfg.push(header);
3660 //        
3661 //        return cfg;
3662 //        
3663 //        
3664 //    },
3665     
3666     initEvents : function()
3667     {   
3668         if(!this.store || !this.cm){
3669             return;
3670         }
3671         
3672         Roo.log('initEvents with ds!!!!');
3673         
3674         var _this = this;
3675         
3676         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3677             e.on('click', _this.sort, _this);
3678         });
3679 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3680 //        this.maskEl.enableDisplayMode("block");
3681 //        this.maskEl.show();
3682         
3683         this.store.on('load', this.onLoad, this);
3684         this.store.on('beforeload', this.onBeforeLoad, this);
3685         
3686         this.store.load();
3687         
3688         
3689         
3690     },
3691     
3692     sort : function(e,el)
3693     {
3694         var col = Roo.get(el)
3695         
3696         if(!col.hasClass('sortable')){
3697             return;
3698         }
3699         
3700         var sort = col.attr('sort');
3701         var dir = 'ASC';
3702         
3703         if(col.hasClass('glyphicon-arrow-up')){
3704             dir = 'DESC';
3705         }
3706         
3707         this.store.sortInfo = {field : sort, direction : dir};
3708         
3709         this.store.load();
3710     },
3711     
3712     renderHeader : function()
3713     {
3714         var header = {
3715             tag: 'thead',
3716             cn : []
3717         };
3718         
3719         var cm = this.cm;
3720         
3721         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3722             
3723             var config = cm.config[i];
3724             
3725             var c = {
3726                 tag: 'th',
3727                 html: cm.getColumnHeader(i)
3728             };
3729             
3730             if(typeof(config.dataIndex) != 'undefined'){
3731                 c.sort = config.dataIndex;
3732             }
3733             
3734             if(typeof(config.sortable) != 'undefined' && config.sortable){
3735                 c.cls = 'sortable';
3736             }
3737             
3738             if(typeof(config.width) != 'undefined'){
3739                 c.style = 'width:' + config.width + 'px';
3740             }
3741             
3742             header.cn.push(c)
3743         }
3744         
3745         return header;
3746     },
3747     
3748     renderBody : function()
3749     {
3750         var body = {
3751             tag: 'tbody',
3752             cn : []
3753         };
3754         
3755         return body;
3756     },
3757     
3758     renderFooter : function()
3759     {
3760         var footer = {
3761             tag: 'tfoot',
3762             cn : []
3763         };
3764         
3765         return footer;
3766     },
3767     
3768     onLoad : function()
3769     {
3770         Roo.log('ds onload');
3771         
3772         var _this = this;
3773         var cm = this.cm;
3774         
3775         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3776             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3777             
3778             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3779                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3780             }
3781             
3782             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3783                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3784             }
3785         });
3786         
3787         var tbody = this.el.select('tbody', true).first();
3788         
3789         var renders = [];
3790         
3791         if(this.store.getCount() > 0){
3792             this.store.data.each(function(d){
3793                 var row = {
3794                     tag : 'tr',
3795                     cn : []
3796                 };
3797                 
3798                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3799                     var renderer = cm.getRenderer(i);
3800                     var config = cm.config[i];
3801                     var value = '';
3802                     var id = Roo.id();
3803                     
3804                     if(typeof(renderer) !== 'undefined'){
3805                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3806                     }
3807                     
3808                     if(typeof(value) === 'object'){
3809                         renders.push({
3810                             id : id,
3811                             cfg : value 
3812                         })
3813                     }
3814                     
3815                     var td = {
3816                         tag: 'td',
3817                         id: id,
3818                         html: (typeof(value) === 'object') ? '' : value
3819                     };
3820                     
3821                     if(typeof(config.width) != 'undefined'){
3822                         td.style = 'width:' +  config.width + 'px';
3823                     }
3824                     
3825                     row.cn.push(td);
3826                    
3827                 }
3828                 
3829                 tbody.createChild(row);
3830                 
3831             });
3832         }
3833         
3834         
3835         if(renders.length){
3836             var _this = this;
3837             Roo.each(renders, function(r){
3838                 _this.renderColumn(r);
3839             })
3840         }
3841 //        
3842 //        if(this.loadMask){
3843 //            this.maskEl.hide();
3844 //        }
3845     },
3846     
3847     onBeforeLoad : function()
3848     {
3849         Roo.log('ds onBeforeLoad');
3850         
3851         this.clear();
3852         
3853 //        if(this.loadMask){
3854 //            this.maskEl.show();
3855 //        }
3856     },
3857     
3858     clear : function()
3859     {
3860         this.el.select('tbody', true).first().dom.innerHTML = '';
3861     },
3862     
3863     getSelectionModel : function(){
3864         if(!this.selModel){
3865             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3866         }
3867         return this.selModel;
3868     },
3869     
3870     renderColumn : function(r)
3871     {
3872         var _this = this;
3873         r.cfg.render(Roo.get(r.id));
3874         
3875         if(r.cfg.cn){
3876             Roo.each(r.cfg.cn, function(c){
3877                 var child = {
3878                     id: r.id,
3879                     cfg: c
3880                 }
3881                 _this.renderColumn(child);
3882             })
3883         }
3884     }
3885    
3886 });
3887
3888  
3889
3890  /*
3891  * - LGPL
3892  *
3893  * table cell
3894  * 
3895  */
3896
3897 /**
3898  * @class Roo.bootstrap.TableCell
3899  * @extends Roo.bootstrap.Component
3900  * Bootstrap TableCell class
3901  * @cfg {String} html cell contain text
3902  * @cfg {String} cls cell class
3903  * @cfg {String} tag cell tag (td|th) default td
3904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3905  * @cfg {String} align Aligns the content in a cell
3906  * @cfg {String} axis Categorizes cells
3907  * @cfg {String} bgcolor Specifies the background color of a cell
3908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3909  * @cfg {Number} colspan Specifies the number of columns a cell should span
3910  * @cfg {String} headers Specifies one or more header cells a cell is related to
3911  * @cfg {Number} height Sets the height of a cell
3912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3913  * @cfg {Number} rowspan Sets the number of rows a cell should span
3914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3915  * @cfg {String} valign Vertical aligns the content in a cell
3916  * @cfg {Number} width Specifies the width of a cell
3917  * 
3918  * @constructor
3919  * Create a new TableCell
3920  * @param {Object} config The config object
3921  */
3922
3923 Roo.bootstrap.TableCell = function(config){
3924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3925 };
3926
3927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3928     
3929     html: false,
3930     cls: false,
3931     tag: false,
3932     abbr: false,
3933     align: false,
3934     axis: false,
3935     bgcolor: false,
3936     charoff: false,
3937     colspan: false,
3938     headers: false,
3939     height: false,
3940     nowrap: false,
3941     rowspan: false,
3942     scope: false,
3943     valign: false,
3944     width: false,
3945     
3946     
3947     getAutoCreate : function(){
3948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3949         
3950         cfg = {
3951             tag: 'td'
3952         }
3953         
3954         if(this.tag){
3955             cfg.tag = this.tag;
3956         }
3957         
3958         if (this.html) {
3959             cfg.html=this.html
3960         }
3961         if (this.cls) {
3962             cfg.cls=this.cls
3963         }
3964         if (this.abbr) {
3965             cfg.abbr=this.abbr
3966         }
3967         if (this.align) {
3968             cfg.align=this.align
3969         }
3970         if (this.axis) {
3971             cfg.axis=this.axis
3972         }
3973         if (this.bgcolor) {
3974             cfg.bgcolor=this.bgcolor
3975         }
3976         if (this.charoff) {
3977             cfg.charoff=this.charoff
3978         }
3979         if (this.colspan) {
3980             cfg.colspan=this.colspan
3981         }
3982         if (this.headers) {
3983             cfg.headers=this.headers
3984         }
3985         if (this.height) {
3986             cfg.height=this.height
3987         }
3988         if (this.nowrap) {
3989             cfg.nowrap=this.nowrap
3990         }
3991         if (this.rowspan) {
3992             cfg.rowspan=this.rowspan
3993         }
3994         if (this.scope) {
3995             cfg.scope=this.scope
3996         }
3997         if (this.valign) {
3998             cfg.valign=this.valign
3999         }
4000         if (this.width) {
4001             cfg.width=this.width
4002         }
4003         
4004         
4005         return cfg;
4006     }
4007    
4008 });
4009
4010  
4011
4012  /*
4013  * - LGPL
4014  *
4015  * table row
4016  * 
4017  */
4018
4019 /**
4020  * @class Roo.bootstrap.TableRow
4021  * @extends Roo.bootstrap.Component
4022  * Bootstrap TableRow class
4023  * @cfg {String} cls row class
4024  * @cfg {String} align Aligns the content in a table row
4025  * @cfg {String} bgcolor Specifies a background color for a table row
4026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4027  * @cfg {String} valign Vertical aligns the content in a table row
4028  * 
4029  * @constructor
4030  * Create a new TableRow
4031  * @param {Object} config The config object
4032  */
4033
4034 Roo.bootstrap.TableRow = function(config){
4035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4036 };
4037
4038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4039     
4040     cls: false,
4041     align: false,
4042     bgcolor: false,
4043     charoff: false,
4044     valign: false,
4045     
4046     getAutoCreate : function(){
4047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4048         
4049         cfg = {
4050             tag: 'tr'
4051         }
4052             
4053         if(this.cls){
4054             cfg.cls = this.cls;
4055         }
4056         if(this.align){
4057             cfg.align = this.align;
4058         }
4059         if(this.bgcolor){
4060             cfg.bgcolor = this.bgcolor;
4061         }
4062         if(this.charoff){
4063             cfg.charoff = this.charoff;
4064         }
4065         if(this.valign){
4066             cfg.valign = this.valign;
4067         }
4068         
4069         return cfg;
4070     }
4071    
4072 });
4073
4074  
4075
4076  /*
4077  * - LGPL
4078  *
4079  * table body
4080  * 
4081  */
4082
4083 /**
4084  * @class Roo.bootstrap.TableBody
4085  * @extends Roo.bootstrap.Component
4086  * Bootstrap TableBody class
4087  * @cfg {String} cls element class
4088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4089  * @cfg {String} align Aligns the content inside the element
4090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4092  * 
4093  * @constructor
4094  * Create a new TableBody
4095  * @param {Object} config The config object
4096  */
4097
4098 Roo.bootstrap.TableBody = function(config){
4099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4100 };
4101
4102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4103     
4104     cls: false,
4105     tag: false,
4106     align: false,
4107     charoff: false,
4108     valign: false,
4109     
4110     getAutoCreate : function(){
4111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4112         
4113         cfg = {
4114             tag: 'tbody'
4115         }
4116             
4117         if (this.cls) {
4118             cfg.cls=this.cls
4119         }
4120         if(this.tag){
4121             cfg.tag = this.tag;
4122         }
4123         
4124         if(this.align){
4125             cfg.align = this.align;
4126         }
4127         if(this.charoff){
4128             cfg.charoff = this.charoff;
4129         }
4130         if(this.valign){
4131             cfg.valign = this.valign;
4132         }
4133         
4134         return cfg;
4135     }
4136     
4137     
4138 //    initEvents : function()
4139 //    {
4140 //        
4141 //        if(!this.store){
4142 //            return;
4143 //        }
4144 //        
4145 //        this.store = Roo.factory(this.store, Roo.data);
4146 //        this.store.on('load', this.onLoad, this);
4147 //        
4148 //        this.store.load();
4149 //        
4150 //    },
4151 //    
4152 //    onLoad: function () 
4153 //    {   
4154 //        this.fireEvent('load', this);
4155 //    }
4156 //    
4157 //   
4158 });
4159
4160  
4161
4162  /*
4163  * Based on:
4164  * Ext JS Library 1.1.1
4165  * Copyright(c) 2006-2007, Ext JS, LLC.
4166  *
4167  * Originally Released Under LGPL - original licence link has changed is not relivant.
4168  *
4169  * Fork - LGPL
4170  * <script type="text/javascript">
4171  */
4172
4173 // as we use this in bootstrap.
4174 Roo.namespace('Roo.form');
4175  /**
4176  * @class Roo.form.Action
4177  * Internal Class used to handle form actions
4178  * @constructor
4179  * @param {Roo.form.BasicForm} el The form element or its id
4180  * @param {Object} config Configuration options
4181  */
4182
4183  
4184  
4185 // define the action interface
4186 Roo.form.Action = function(form, options){
4187     this.form = form;
4188     this.options = options || {};
4189 };
4190 /**
4191  * Client Validation Failed
4192  * @const 
4193  */
4194 Roo.form.Action.CLIENT_INVALID = 'client';
4195 /**
4196  * Server Validation Failed
4197  * @const 
4198  */
4199 Roo.form.Action.SERVER_INVALID = 'server';
4200  /**
4201  * Connect to Server Failed
4202  * @const 
4203  */
4204 Roo.form.Action.CONNECT_FAILURE = 'connect';
4205 /**
4206  * Reading Data from Server Failed
4207  * @const 
4208  */
4209 Roo.form.Action.LOAD_FAILURE = 'load';
4210
4211 Roo.form.Action.prototype = {
4212     type : 'default',
4213     failureType : undefined,
4214     response : undefined,
4215     result : undefined,
4216
4217     // interface method
4218     run : function(options){
4219
4220     },
4221
4222     // interface method
4223     success : function(response){
4224
4225     },
4226
4227     // interface method
4228     handleResponse : function(response){
4229
4230     },
4231
4232     // default connection failure
4233     failure : function(response){
4234         
4235         this.response = response;
4236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4237         this.form.afterAction(this, false);
4238     },
4239
4240     processResponse : function(response){
4241         this.response = response;
4242         if(!response.responseText){
4243             return true;
4244         }
4245         this.result = this.handleResponse(response);
4246         return this.result;
4247     },
4248
4249     // utility functions used internally
4250     getUrl : function(appendParams){
4251         var url = this.options.url || this.form.url || this.form.el.dom.action;
4252         if(appendParams){
4253             var p = this.getParams();
4254             if(p){
4255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4256             }
4257         }
4258         return url;
4259     },
4260
4261     getMethod : function(){
4262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4263     },
4264
4265     getParams : function(){
4266         var bp = this.form.baseParams;
4267         var p = this.options.params;
4268         if(p){
4269             if(typeof p == "object"){
4270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4271             }else if(typeof p == 'string' && bp){
4272                 p += '&' + Roo.urlEncode(bp);
4273             }
4274         }else if(bp){
4275             p = Roo.urlEncode(bp);
4276         }
4277         return p;
4278     },
4279
4280     createCallback : function(){
4281         return {
4282             success: this.success,
4283             failure: this.failure,
4284             scope: this,
4285             timeout: (this.form.timeout*1000),
4286             upload: this.form.fileUpload ? this.success : undefined
4287         };
4288     }
4289 };
4290
4291 Roo.form.Action.Submit = function(form, options){
4292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4293 };
4294
4295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4296     type : 'submit',
4297
4298     haveProgress : false,
4299     uploadComplete : false,
4300     
4301     // uploadProgress indicator.
4302     uploadProgress : function()
4303     {
4304         if (!this.form.progressUrl) {
4305             return;
4306         }
4307         
4308         if (!this.haveProgress) {
4309             Roo.MessageBox.progress("Uploading", "Uploading");
4310         }
4311         if (this.uploadComplete) {
4312            Roo.MessageBox.hide();
4313            return;
4314         }
4315         
4316         this.haveProgress = true;
4317    
4318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4319         
4320         var c = new Roo.data.Connection();
4321         c.request({
4322             url : this.form.progressUrl,
4323             params: {
4324                 id : uid
4325             },
4326             method: 'GET',
4327             success : function(req){
4328                //console.log(data);
4329                 var rdata = false;
4330                 var edata;
4331                 try  {
4332                    rdata = Roo.decode(req.responseText)
4333                 } catch (e) {
4334                     Roo.log("Invalid data from server..");
4335                     Roo.log(edata);
4336                     return;
4337                 }
4338                 if (!rdata || !rdata.success) {
4339                     Roo.log(rdata);
4340                     Roo.MessageBox.alert(Roo.encode(rdata));
4341                     return;
4342                 }
4343                 var data = rdata.data;
4344                 
4345                 if (this.uploadComplete) {
4346                    Roo.MessageBox.hide();
4347                    return;
4348                 }
4349                    
4350                 if (data){
4351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4353                     );
4354                 }
4355                 this.uploadProgress.defer(2000,this);
4356             },
4357        
4358             failure: function(data) {
4359                 Roo.log('progress url failed ');
4360                 Roo.log(data);
4361             },
4362             scope : this
4363         });
4364            
4365     },
4366     
4367     
4368     run : function()
4369     {
4370         // run get Values on the form, so it syncs any secondary forms.
4371         this.form.getValues();
4372         
4373         var o = this.options;
4374         var method = this.getMethod();
4375         var isPost = method == 'POST';
4376         if(o.clientValidation === false || this.form.isValid()){
4377             
4378             if (this.form.progressUrl) {
4379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4380                     (new Date() * 1) + '' + Math.random());
4381                     
4382             } 
4383             
4384             
4385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4386                 form:this.form.el.dom,
4387                 url:this.getUrl(!isPost),
4388                 method: method,
4389                 params:isPost ? this.getParams() : null,
4390                 isUpload: this.form.fileUpload
4391             }));
4392             
4393             this.uploadProgress();
4394
4395         }else if (o.clientValidation !== false){ // client validation failed
4396             this.failureType = Roo.form.Action.CLIENT_INVALID;
4397             this.form.afterAction(this, false);
4398         }
4399     },
4400
4401     success : function(response)
4402     {
4403         this.uploadComplete= true;
4404         if (this.haveProgress) {
4405             Roo.MessageBox.hide();
4406         }
4407         
4408         
4409         var result = this.processResponse(response);
4410         if(result === true || result.success){
4411             this.form.afterAction(this, true);
4412             return;
4413         }
4414         if(result.errors){
4415             this.form.markInvalid(result.errors);
4416             this.failureType = Roo.form.Action.SERVER_INVALID;
4417         }
4418         this.form.afterAction(this, false);
4419     },
4420     failure : function(response)
4421     {
4422         this.uploadComplete= true;
4423         if (this.haveProgress) {
4424             Roo.MessageBox.hide();
4425         }
4426         
4427         this.response = response;
4428         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4429         this.form.afterAction(this, false);
4430     },
4431     
4432     handleResponse : function(response){
4433         if(this.form.errorReader){
4434             var rs = this.form.errorReader.read(response);
4435             var errors = [];
4436             if(rs.records){
4437                 for(var i = 0, len = rs.records.length; i < len; i++) {
4438                     var r = rs.records[i];
4439                     errors[i] = r.data;
4440                 }
4441             }
4442             if(errors.length < 1){
4443                 errors = null;
4444             }
4445             return {
4446                 success : rs.success,
4447                 errors : errors
4448             };
4449         }
4450         var ret = false;
4451         try {
4452             ret = Roo.decode(response.responseText);
4453         } catch (e) {
4454             ret = {
4455                 success: false,
4456                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4457                 errors : []
4458             };
4459         }
4460         return ret;
4461         
4462     }
4463 });
4464
4465
4466 Roo.form.Action.Load = function(form, options){
4467     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4468     this.reader = this.form.reader;
4469 };
4470
4471 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4472     type : 'load',
4473
4474     run : function(){
4475         
4476         Roo.Ajax.request(Roo.apply(
4477                 this.createCallback(), {
4478                     method:this.getMethod(),
4479                     url:this.getUrl(false),
4480                     params:this.getParams()
4481         }));
4482     },
4483
4484     success : function(response){
4485         
4486         var result = this.processResponse(response);
4487         if(result === true || !result.success || !result.data){
4488             this.failureType = Roo.form.Action.LOAD_FAILURE;
4489             this.form.afterAction(this, false);
4490             return;
4491         }
4492         this.form.clearInvalid();
4493         this.form.setValues(result.data);
4494         this.form.afterAction(this, true);
4495     },
4496
4497     handleResponse : function(response){
4498         if(this.form.reader){
4499             var rs = this.form.reader.read(response);
4500             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4501             return {
4502                 success : rs.success,
4503                 data : data
4504             };
4505         }
4506         return Roo.decode(response.responseText);
4507     }
4508 });
4509
4510 Roo.form.Action.ACTION_TYPES = {
4511     'load' : Roo.form.Action.Load,
4512     'submit' : Roo.form.Action.Submit
4513 };/*
4514  * - LGPL
4515  *
4516  * form
4517  * 
4518  */
4519
4520 /**
4521  * @class Roo.bootstrap.Form
4522  * @extends Roo.bootstrap.Component
4523  * Bootstrap Form class
4524  * @cfg {String} method  GET | POST (default POST)
4525  * @cfg {String} labelAlign top | left (default top)
4526   * @cfg {String} align left  | right - for navbars
4527
4528  * 
4529  * @constructor
4530  * Create a new Form
4531  * @param {Object} config The config object
4532  */
4533
4534
4535 Roo.bootstrap.Form = function(config){
4536     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4537     this.addEvents({
4538         /**
4539          * @event clientvalidation
4540          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4541          * @param {Form} this
4542          * @param {Boolean} valid true if the form has passed client-side validation
4543          */
4544         clientvalidation: true,
4545         /**
4546          * @event beforeaction
4547          * Fires before any action is performed. Return false to cancel the action.
4548          * @param {Form} this
4549          * @param {Action} action The action to be performed
4550          */
4551         beforeaction: true,
4552         /**
4553          * @event actionfailed
4554          * Fires when an action fails.
4555          * @param {Form} this
4556          * @param {Action} action The action that failed
4557          */
4558         actionfailed : true,
4559         /**
4560          * @event actioncomplete
4561          * Fires when an action is completed.
4562          * @param {Form} this
4563          * @param {Action} action The action that completed
4564          */
4565         actioncomplete : true
4566     });
4567     
4568 };
4569
4570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4571       
4572      /**
4573      * @cfg {String} method
4574      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4575      */
4576     method : 'POST',
4577     /**
4578      * @cfg {String} url
4579      * The URL to use for form actions if one isn't supplied in the action options.
4580      */
4581     /**
4582      * @cfg {Boolean} fileUpload
4583      * Set to true if this form is a file upload.
4584      */
4585      
4586     /**
4587      * @cfg {Object} baseParams
4588      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4589      */
4590       
4591     /**
4592      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4593      */
4594     timeout: 30,
4595     /**
4596      * @cfg {Sting} align (left|right) for navbar forms
4597      */
4598     align : 'left',
4599
4600     // private
4601     activeAction : null,
4602  
4603     /**
4604      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4605      * element by passing it or its id or mask the form itself by passing in true.
4606      * @type Mixed
4607      */
4608     waitMsgTarget : false,
4609     
4610      
4611     
4612     /**
4613      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4614      * element by passing it or its id or mask the form itself by passing in true.
4615      * @type Mixed
4616      */
4617     
4618     getAutoCreate : function(){
4619         
4620         var cfg = {
4621             tag: 'form',
4622             method : this.method || 'POST',
4623             id : this.id || Roo.id(),
4624             cls : ''
4625         }
4626         if (this.parent().xtype.match(/^Nav/)) {
4627             cfg.cls = 'navbar-form navbar-' + this.align;
4628             
4629         }
4630         
4631         if (this.labelAlign == 'left' ) {
4632             cfg.cls += ' form-horizontal';
4633         }
4634         
4635         
4636         return cfg;
4637     },
4638     initEvents : function()
4639     {
4640         this.el.on('submit', this.onSubmit, this);
4641         
4642         
4643     },
4644     // private
4645     onSubmit : function(e){
4646         e.stopEvent();
4647     },
4648     
4649      /**
4650      * Returns true if client-side validation on the form is successful.
4651      * @return Boolean
4652      */
4653     isValid : function(){
4654         var items = this.getItems();
4655         var valid = true;
4656         items.each(function(f){
4657            if(!f.validate()){
4658                valid = false;
4659                
4660            }
4661         });
4662         return valid;
4663     },
4664     /**
4665      * Returns true if any fields in this form have changed since their original load.
4666      * @return Boolean
4667      */
4668     isDirty : function(){
4669         var dirty = false;
4670         var items = this.getItems();
4671         items.each(function(f){
4672            if(f.isDirty()){
4673                dirty = true;
4674                return false;
4675            }
4676            return true;
4677         });
4678         return dirty;
4679     },
4680      /**
4681      * Performs a predefined action (submit or load) or custom actions you define on this form.
4682      * @param {String} actionName The name of the action type
4683      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
4684      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4685      * accept other config options):
4686      * <pre>
4687 Property          Type             Description
4688 ----------------  ---------------  ----------------------------------------------------------------------------------
4689 url               String           The url for the action (defaults to the form's url)
4690 method            String           The form method to use (defaults to the form's method, or POST if not defined)
4691 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
4692 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
4693                                    validate the form on the client (defaults to false)
4694      * </pre>
4695      * @return {BasicForm} this
4696      */
4697     doAction : function(action, options){
4698         if(typeof action == 'string'){
4699             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4700         }
4701         if(this.fireEvent('beforeaction', this, action) !== false){
4702             this.beforeAction(action);
4703             action.run.defer(100, action);
4704         }
4705         return this;
4706     },
4707     
4708     // private
4709     beforeAction : function(action){
4710         var o = action.options;
4711         
4712         // not really supported yet.. ??
4713         
4714         //if(this.waitMsgTarget === true){
4715             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4716         //}else if(this.waitMsgTarget){
4717         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4718         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4719         //}else {
4720         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4721        // }
4722          
4723     },
4724
4725     // private
4726     afterAction : function(action, success){
4727         this.activeAction = null;
4728         var o = action.options;
4729         
4730         //if(this.waitMsgTarget === true){
4731             this.el.unmask();
4732         //}else if(this.waitMsgTarget){
4733         //    this.waitMsgTarget.unmask();
4734         //}else{
4735         //    Roo.MessageBox.updateProgress(1);
4736         //    Roo.MessageBox.hide();
4737        // }
4738         // 
4739         if(success){
4740             if(o.reset){
4741                 this.reset();
4742             }
4743             Roo.callback(o.success, o.scope, [this, action]);
4744             this.fireEvent('actioncomplete', this, action);
4745             
4746         }else{
4747             
4748             // failure condition..
4749             // we have a scenario where updates need confirming.
4750             // eg. if a locking scenario exists..
4751             // we look for { errors : { needs_confirm : true }} in the response.
4752             if (
4753                 (typeof(action.result) != 'undefined')  &&
4754                 (typeof(action.result.errors) != 'undefined')  &&
4755                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4756            ){
4757                 var _t = this;
4758                 Roo.log("not supported yet");
4759                  /*
4760                 
4761                 Roo.MessageBox.confirm(
4762                     "Change requires confirmation",
4763                     action.result.errorMsg,
4764                     function(r) {
4765                         if (r != 'yes') {
4766                             return;
4767                         }
4768                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4769                     }
4770                     
4771                 );
4772                 */
4773                 
4774                 
4775                 return;
4776             }
4777             
4778             Roo.callback(o.failure, o.scope, [this, action]);
4779             // show an error message if no failed handler is set..
4780             if (!this.hasListener('actionfailed')) {
4781                 Roo.log("need to add dialog support");
4782                 /*
4783                 Roo.MessageBox.alert("Error",
4784                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4785                         action.result.errorMsg :
4786                         "Saving Failed, please check your entries or try again"
4787                 );
4788                 */
4789             }
4790             
4791             this.fireEvent('actionfailed', this, action);
4792         }
4793         
4794     },
4795     /**
4796      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4797      * @param {String} id The value to search for
4798      * @return Field
4799      */
4800     findField : function(id){
4801         var items = this.getItems();
4802         var field = items.get(id);
4803         if(!field){
4804              items.each(function(f){
4805                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4806                     field = f;
4807                     return false;
4808                 }
4809                 return true;
4810             });
4811         }
4812         return field || null;
4813     },
4814      /**
4815      * Mark fields in this form invalid in bulk.
4816      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4817      * @return {BasicForm} this
4818      */
4819     markInvalid : function(errors){
4820         if(errors instanceof Array){
4821             for(var i = 0, len = errors.length; i < len; i++){
4822                 var fieldError = errors[i];
4823                 var f = this.findField(fieldError.id);
4824                 if(f){
4825                     f.markInvalid(fieldError.msg);
4826                 }
4827             }
4828         }else{
4829             var field, id;
4830             for(id in errors){
4831                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4832                     field.markInvalid(errors[id]);
4833                 }
4834             }
4835         }
4836         //Roo.each(this.childForms || [], function (f) {
4837         //    f.markInvalid(errors);
4838         //});
4839         
4840         return this;
4841     },
4842
4843     /**
4844      * Set values for fields in this form in bulk.
4845      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4846      * @return {BasicForm} this
4847      */
4848     setValues : function(values){
4849         if(values instanceof Array){ // array of objects
4850             for(var i = 0, len = values.length; i < len; i++){
4851                 var v = values[i];
4852                 var f = this.findField(v.id);
4853                 if(f){
4854                     f.setValue(v.value);
4855                     if(this.trackResetOnLoad){
4856                         f.originalValue = f.getValue();
4857                     }
4858                 }
4859             }
4860         }else{ // object hash
4861             var field, id;
4862             for(id in values){
4863                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4864                     
4865                     if (field.setFromData && 
4866                         field.valueField && 
4867                         field.displayField &&
4868                         // combos' with local stores can 
4869                         // be queried via setValue()
4870                         // to set their value..
4871                         (field.store && !field.store.isLocal)
4872                         ) {
4873                         // it's a combo
4874                         var sd = { };
4875                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4876                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4877                         field.setFromData(sd);
4878                         
4879                     } else {
4880                         field.setValue(values[id]);
4881                     }
4882                     
4883                     
4884                     if(this.trackResetOnLoad){
4885                         field.originalValue = field.getValue();
4886                     }
4887                 }
4888             }
4889         }
4890          
4891         //Roo.each(this.childForms || [], function (f) {
4892         //    f.setValues(values);
4893         //});
4894                 
4895         return this;
4896     },
4897
4898     /**
4899      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4900      * they are returned as an array.
4901      * @param {Boolean} asString
4902      * @return {Object}
4903      */
4904     getValues : function(asString){
4905         //if (this.childForms) {
4906             // copy values from the child forms
4907         //    Roo.each(this.childForms, function (f) {
4908         //        this.setValues(f.getValues());
4909         //    }, this);
4910         //}
4911         
4912         
4913         
4914         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4915         if(asString === true){
4916             return fs;
4917         }
4918         return Roo.urlDecode(fs);
4919     },
4920     
4921     /**
4922      * Returns the fields in this form as an object with key/value pairs. 
4923      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4924      * @return {Object}
4925      */
4926     getFieldValues : function(with_hidden)
4927     {
4928         var items = this.getItems();
4929         var ret = {};
4930         items.each(function(f){
4931             if (!f.getName()) {
4932                 return;
4933             }
4934             var v = f.getValue();
4935             if (f.inputType =='radio') {
4936                 if (typeof(ret[f.getName()]) == 'undefined') {
4937                     ret[f.getName()] = ''; // empty..
4938                 }
4939                 
4940                 if (!f.el.dom.checked) {
4941                     return;
4942                     
4943                 }
4944                 v = f.el.dom.value;
4945                 
4946             }
4947             
4948             // not sure if this supported any more..
4949             if ((typeof(v) == 'object') && f.getRawValue) {
4950                 v = f.getRawValue() ; // dates..
4951             }
4952             // combo boxes where name != hiddenName...
4953             if (f.name != f.getName()) {
4954                 ret[f.name] = f.getRawValue();
4955             }
4956             ret[f.getName()] = v;
4957         });
4958         
4959         return ret;
4960     },
4961
4962     /**
4963      * Clears all invalid messages in this form.
4964      * @return {BasicForm} this
4965      */
4966     clearInvalid : function(){
4967         var items = this.getItems();
4968         
4969         items.each(function(f){
4970            f.clearInvalid();
4971         });
4972         
4973         
4974         
4975         return this;
4976     },
4977
4978     /**
4979      * Resets this form.
4980      * @return {BasicForm} this
4981      */
4982     reset : function(){
4983         var items = this.getItems();
4984         items.each(function(f){
4985             f.reset();
4986         });
4987         
4988         Roo.each(this.childForms || [], function (f) {
4989             f.reset();
4990         });
4991        
4992         
4993         return this;
4994     },
4995     getItems : function()
4996     {
4997         var r=new Roo.util.MixedCollection(false, function(o){
4998             return o.id || (o.id = Roo.id());
4999         });
5000         var iter = function(el) {
5001             if (el.inputEl) {
5002                 r.add(el);
5003             }
5004             if (!el.items) {
5005                 return;
5006             }
5007             Roo.each(el.items,function(e) {
5008                 iter(e);
5009             });
5010             
5011             
5012         };
5013         iter(this);
5014         return r;
5015         
5016         
5017         
5018         
5019     }
5020     
5021 });
5022
5023  
5024 /*
5025  * Based on:
5026  * Ext JS Library 1.1.1
5027  * Copyright(c) 2006-2007, Ext JS, LLC.
5028  *
5029  * Originally Released Under LGPL - original licence link has changed is not relivant.
5030  *
5031  * Fork - LGPL
5032  * <script type="text/javascript">
5033  */
5034 /**
5035  * @class Roo.form.VTypes
5036  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5037  * @singleton
5038  */
5039 Roo.form.VTypes = function(){
5040     // closure these in so they are only created once.
5041     var alpha = /^[a-zA-Z_]+$/;
5042     var alphanum = /^[a-zA-Z0-9_]+$/;
5043     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5044     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5045
5046     // All these messages and functions are configurable
5047     return {
5048         /**
5049          * The function used to validate email addresses
5050          * @param {String} value The email address
5051          */
5052         'email' : function(v){
5053             return email.test(v);
5054         },
5055         /**
5056          * The error text to display when the email validation function returns false
5057          * @type String
5058          */
5059         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5060         /**
5061          * The keystroke filter mask to be applied on email input
5062          * @type RegExp
5063          */
5064         'emailMask' : /[a-z0-9_\.\-@]/i,
5065
5066         /**
5067          * The function used to validate URLs
5068          * @param {String} value The URL
5069          */
5070         'url' : function(v){
5071             return url.test(v);
5072         },
5073         /**
5074          * The error text to display when the url validation function returns false
5075          * @type String
5076          */
5077         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5078         
5079         /**
5080          * The function used to validate alpha values
5081          * @param {String} value The value
5082          */
5083         'alpha' : function(v){
5084             return alpha.test(v);
5085         },
5086         /**
5087          * The error text to display when the alpha validation function returns false
5088          * @type String
5089          */
5090         'alphaText' : 'This field should only contain letters and _',
5091         /**
5092          * The keystroke filter mask to be applied on alpha input
5093          * @type RegExp
5094          */
5095         'alphaMask' : /[a-z_]/i,
5096
5097         /**
5098          * The function used to validate alphanumeric values
5099          * @param {String} value The value
5100          */
5101         'alphanum' : function(v){
5102             return alphanum.test(v);
5103         },
5104         /**
5105          * The error text to display when the alphanumeric validation function returns false
5106          * @type String
5107          */
5108         'alphanumText' : 'This field should only contain letters, numbers and _',
5109         /**
5110          * The keystroke filter mask to be applied on alphanumeric input
5111          * @type RegExp
5112          */
5113         'alphanumMask' : /[a-z0-9_]/i
5114     };
5115 }();/*
5116  * - LGPL
5117  *
5118  * Input
5119  * 
5120  */
5121
5122 /**
5123  * @class Roo.bootstrap.Input
5124  * @extends Roo.bootstrap.Component
5125  * Bootstrap Input class
5126  * @cfg {Boolean} disabled is it disabled
5127  * @cfg {String} fieldLabel - the label associated
5128  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5129  * @cfg {String} name name of the input
5130  * @cfg {string} fieldLabel - the label associated
5131  * @cfg {string}  inputType - input / file submit ...
5132  * @cfg {string} placeholder - placeholder to put in text.
5133  * @cfg {string}  before - input group add on before
5134  * @cfg {string} after - input group add on after
5135  * @cfg {string} size - (lg|sm) or leave empty..
5136  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5137  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5138  * @cfg {Number} md colspan out of 12 for computer-sized screens
5139  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5140  * @cfg {string} value default value of the input
5141  * @cfg {Number} labelWidth set the width of label (0-12)
5142  * @cfg {String} labelAlign (top|left)
5143  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5144  * 
5145  * 
5146  * @constructor
5147  * Create a new Input
5148  * @param {Object} config The config object
5149  */
5150
5151 Roo.bootstrap.Input = function(config){
5152     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5153    
5154         this.addEvents({
5155             /**
5156              * @event focus
5157              * Fires when this field receives input focus.
5158              * @param {Roo.form.Field} this
5159              */
5160             focus : true,
5161             /**
5162              * @event blur
5163              * Fires when this field loses input focus.
5164              * @param {Roo.form.Field} this
5165              */
5166             blur : true,
5167             /**
5168              * @event specialkey
5169              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5170              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5171              * @param {Roo.form.Field} this
5172              * @param {Roo.EventObject} e The event object
5173              */
5174             specialkey : true,
5175             /**
5176              * @event change
5177              * Fires just before the field blurs if the field value has changed.
5178              * @param {Roo.form.Field} this
5179              * @param {Mixed} newValue The new value
5180              * @param {Mixed} oldValue The original value
5181              */
5182             change : true,
5183             /**
5184              * @event invalid
5185              * Fires after the field has been marked as invalid.
5186              * @param {Roo.form.Field} this
5187              * @param {String} msg The validation message
5188              */
5189             invalid : true,
5190             /**
5191              * @event valid
5192              * Fires after the field has been validated with no errors.
5193              * @param {Roo.form.Field} this
5194              */
5195             valid : true,
5196              /**
5197              * @event keyup
5198              * Fires after the key up
5199              * @param {Roo.form.Field} this
5200              * @param {Roo.EventObject}  e The event Object
5201              */
5202             keyup : true
5203         });
5204 };
5205
5206 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5207      /**
5208      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5209       automatic validation (defaults to "keyup").
5210      */
5211     validationEvent : "keyup",
5212      /**
5213      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5214      */
5215     validateOnBlur : true,
5216     /**
5217      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5218      */
5219     validationDelay : 250,
5220      /**
5221      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5222      */
5223     focusClass : "x-form-focus",  // not needed???
5224     
5225        
5226     /**
5227      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5228      */
5229     invalidClass : "has-error",
5230     
5231     /**
5232      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5233      */
5234     selectOnFocus : false,
5235     
5236      /**
5237      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5238      */
5239     maskRe : null,
5240        /**
5241      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5242      */
5243     vtype : null,
5244     
5245       /**
5246      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5247      */
5248     disableKeyFilter : false,
5249     
5250        /**
5251      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5252      */
5253     disabled : false,
5254      /**
5255      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5256      */
5257     allowBlank : true,
5258     /**
5259      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5260      */
5261     blankText : "This field is required",
5262     
5263      /**
5264      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5265      */
5266     minLength : 0,
5267     /**
5268      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5269      */
5270     maxLength : Number.MAX_VALUE,
5271     /**
5272      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5273      */
5274     minLengthText : "The minimum length for this field is {0}",
5275     /**
5276      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5277      */
5278     maxLengthText : "The maximum length for this field is {0}",
5279   
5280     
5281     /**
5282      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5283      * If available, this function will be called only after the basic validators all return true, and will be passed the
5284      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5285      */
5286     validator : null,
5287     /**
5288      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5289      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5290      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5291      */
5292     regex : null,
5293     /**
5294      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5295      */
5296     regexText : "",
5297     
5298     
5299     
5300     fieldLabel : '',
5301     inputType : 'text',
5302     
5303     name : false,
5304     placeholder: false,
5305     before : false,
5306     after : false,
5307     size : false,
5308     // private
5309     hasFocus : false,
5310     preventMark: false,
5311     isFormField : true,
5312     value : '',
5313     labelWidth : 2,
5314     labelAlign : false,
5315     readOnly : false,
5316     
5317     parentLabelAlign : function()
5318     {
5319         var parent = this;
5320         while (parent.parent()) {
5321             parent = parent.parent();
5322             if (typeof(parent.labelAlign) !='undefined') {
5323                 return parent.labelAlign;
5324             }
5325         }
5326         return 'left';
5327         
5328     },
5329     
5330     getAutoCreate : function(){
5331         
5332         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5333         
5334         var id = Roo.id();
5335         
5336         var cfg = {};
5337         
5338         if(this.inputType != 'hidden'){
5339             cfg.cls = 'form-group' //input-group
5340         }
5341         
5342         var input =  {
5343             tag: 'input',
5344             id : id,
5345             type : this.inputType,
5346             value : this.value,
5347             cls : 'form-control',
5348             placeholder : this.placeholder || ''
5349             
5350         };
5351         
5352         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5353             input.maxLength = this.maxLength;
5354         }
5355         
5356         if (this.disabled) {
5357             input.disabled=true;
5358         }
5359         
5360         if (this.readOnly) {
5361             input.readonly=true;
5362         }
5363         
5364         if (this.name) {
5365             input.name = this.name;
5366         }
5367         if (this.size) {
5368             input.cls += ' input-' + this.size;
5369         }
5370         var settings=this;
5371         ['xs','sm','md','lg'].map(function(size){
5372             if (settings[size]) {
5373                 cfg.cls += ' col-' + size + '-' + settings[size];
5374             }
5375         });
5376         
5377         var inputblock = input;
5378         
5379         if (this.before || this.after) {
5380             
5381             inputblock = {
5382                 cls : 'input-group',
5383                 cn :  [] 
5384             };
5385             if (this.before) {
5386                 inputblock.cn.push({
5387                     tag :'span',
5388                     cls : 'input-group-addon',
5389                     html : this.before
5390                 });
5391             }
5392             inputblock.cn.push(input);
5393             if (this.after) {
5394                 inputblock.cn.push({
5395                     tag :'span',
5396                     cls : 'input-group-addon',
5397                     html : this.after
5398                 });
5399             }
5400             
5401         };
5402         
5403         if (align ==='left' && this.fieldLabel.length) {
5404                 Roo.log("left and has label");
5405                 cfg.cn = [
5406                     
5407                     {
5408                         tag: 'label',
5409                         'for' :  id,
5410                         cls : 'control-label col-sm-' + this.labelWidth,
5411                         html : this.fieldLabel
5412                         
5413                     },
5414                     {
5415                         cls : "col-sm-" + (12 - this.labelWidth), 
5416                         cn: [
5417                             inputblock
5418                         ]
5419                     }
5420                     
5421                 ];
5422         } else if ( this.fieldLabel.length) {
5423                 Roo.log(" label");
5424                  cfg.cn = [
5425                    
5426                     {
5427                         tag: 'label',
5428                         //cls : 'input-group-addon',
5429                         html : this.fieldLabel
5430                         
5431                     },
5432                     
5433                     inputblock
5434                     
5435                 ];
5436
5437         } else {
5438             
5439                 Roo.log(" no label && no align");
5440                 cfg.cn = [
5441                     
5442                         inputblock
5443                     
5444                 ];
5445                 
5446                 
5447         };
5448         Roo.log('input-parentType: ' + this.parentType);
5449         
5450         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5451            cfg.cls += ' navbar-form';
5452            Roo.log(cfg);
5453         }
5454         
5455         return cfg;
5456         
5457     },
5458     /**
5459      * return the real input element.
5460      */
5461     inputEl: function ()
5462     {
5463         return this.el.select('input.form-control',true).first();
5464     },
5465     setDisabled : function(v)
5466     {
5467         var i  = this.inputEl().dom;
5468         if (!v) {
5469             i.removeAttribute('disabled');
5470             return;
5471             
5472         }
5473         i.setAttribute('disabled','true');
5474     },
5475     initEvents : function()
5476     {
5477         
5478         this.inputEl().on("keydown" , this.fireKey,  this);
5479         this.inputEl().on("focus", this.onFocus,  this);
5480         this.inputEl().on("blur", this.onBlur,  this);
5481         
5482         this.inputEl().relayEvent('keyup', this);
5483
5484         // reference to original value for reset
5485         this.originalValue = this.getValue();
5486         //Roo.form.TextField.superclass.initEvents.call(this);
5487         if(this.validationEvent == 'keyup'){
5488             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5489             this.inputEl().on('keyup', this.filterValidation, this);
5490         }
5491         else if(this.validationEvent !== false){
5492             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5493         }
5494         
5495         if(this.selectOnFocus){
5496             this.on("focus", this.preFocus, this);
5497             
5498         }
5499         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5500             this.inputEl().on("keypress", this.filterKeys, this);
5501         }
5502        /* if(this.grow){
5503             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5504             this.el.on("click", this.autoSize,  this);
5505         }
5506         */
5507         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5508             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5509         }
5510         
5511     },
5512     filterValidation : function(e){
5513         if(!e.isNavKeyPress()){
5514             this.validationTask.delay(this.validationDelay);
5515         }
5516     },
5517      /**
5518      * Validates the field value
5519      * @return {Boolean} True if the value is valid, else false
5520      */
5521     validate : function(){
5522         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5523         if(this.disabled || this.validateValue(this.getRawValue())){
5524             this.clearInvalid();
5525             return true;
5526         }
5527         return false;
5528     },
5529     
5530     
5531     /**
5532      * Validates a value according to the field's validation rules and marks the field as invalid
5533      * if the validation fails
5534      * @param {Mixed} value The value to validate
5535      * @return {Boolean} True if the value is valid, else false
5536      */
5537     validateValue : function(value){
5538         if(value.length < 1)  { // if it's blank
5539              if(this.allowBlank){
5540                 this.clearInvalid();
5541                 return true;
5542              }else{
5543                 this.markInvalid(this.blankText);
5544                 return false;
5545              }
5546         }
5547         if(value.length < this.minLength){
5548             this.markInvalid(String.format(this.minLengthText, this.minLength));
5549             return false;
5550         }
5551         if(value.length > this.maxLength){
5552             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5553             return false;
5554         }
5555         if(this.vtype){
5556             var vt = Roo.form.VTypes;
5557             if(!vt[this.vtype](value, this)){
5558                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5559                 return false;
5560             }
5561         }
5562         if(typeof this.validator == "function"){
5563             var msg = this.validator(value);
5564             if(msg !== true){
5565                 this.markInvalid(msg);
5566                 return false;
5567             }
5568         }
5569         if(this.regex && !this.regex.test(value)){
5570             this.markInvalid(this.regexText);
5571             return false;
5572         }
5573         return true;
5574     },
5575
5576     
5577     
5578      // private
5579     fireKey : function(e){
5580         //Roo.log('field ' + e.getKey());
5581         if(e.isNavKeyPress()){
5582             this.fireEvent("specialkey", this, e);
5583         }
5584     },
5585     focus : function (selectText){
5586         if(this.rendered){
5587             this.inputEl().focus();
5588             if(selectText === true){
5589                 this.inputEl().dom.select();
5590             }
5591         }
5592         return this;
5593     } ,
5594     
5595     onFocus : function(){
5596         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5597            // this.el.addClass(this.focusClass);
5598         }
5599         if(!this.hasFocus){
5600             this.hasFocus = true;
5601             this.startValue = this.getValue();
5602             this.fireEvent("focus", this);
5603         }
5604     },
5605     
5606     beforeBlur : Roo.emptyFn,
5607
5608     
5609     // private
5610     onBlur : function(){
5611         this.beforeBlur();
5612         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5613             //this.el.removeClass(this.focusClass);
5614         }
5615         this.hasFocus = false;
5616         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5617             this.validate();
5618         }
5619         var v = this.getValue();
5620         if(String(v) !== String(this.startValue)){
5621             this.fireEvent('change', this, v, this.startValue);
5622         }
5623         this.fireEvent("blur", this);
5624     },
5625     
5626     /**
5627      * Resets the current field value to the originally loaded value and clears any validation messages
5628      */
5629     reset : function(){
5630         this.setValue(this.originalValue);
5631         this.clearInvalid();
5632     },
5633      /**
5634      * Returns the name of the field
5635      * @return {Mixed} name The name field
5636      */
5637     getName: function(){
5638         return this.name;
5639     },
5640      /**
5641      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
5642      * @return {Mixed} value The field value
5643      */
5644     getValue : function(){
5645         return this.inputEl().getValue();
5646     },
5647     /**
5648      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
5649      * @return {Mixed} value The field value
5650      */
5651     getRawValue : function(){
5652         var v = this.inputEl().getValue();
5653         
5654         return v;
5655     },
5656     
5657     /**
5658      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
5659      * @param {Mixed} value The value to set
5660      */
5661     setRawValue : function(v){
5662         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5663     },
5664     
5665     selectText : function(start, end){
5666         var v = this.getRawValue();
5667         if(v.length > 0){
5668             start = start === undefined ? 0 : start;
5669             end = end === undefined ? v.length : end;
5670             var d = this.inputEl().dom;
5671             if(d.setSelectionRange){
5672                 d.setSelectionRange(start, end);
5673             }else if(d.createTextRange){
5674                 var range = d.createTextRange();
5675                 range.moveStart("character", start);
5676                 range.moveEnd("character", v.length-end);
5677                 range.select();
5678             }
5679         }
5680     },
5681     
5682     /**
5683      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
5684      * @param {Mixed} value The value to set
5685      */
5686     setValue : function(v){
5687         this.value = v;
5688         if(this.rendered){
5689             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5690             this.validate();
5691         }
5692     },
5693     
5694     /*
5695     processValue : function(value){
5696         if(this.stripCharsRe){
5697             var newValue = value.replace(this.stripCharsRe, '');
5698             if(newValue !== value){
5699                 this.setRawValue(newValue);
5700                 return newValue;
5701             }
5702         }
5703         return value;
5704     },
5705   */
5706     preFocus : function(){
5707         
5708         if(this.selectOnFocus){
5709             this.inputEl().dom.select();
5710         }
5711     },
5712     filterKeys : function(e){
5713         var k = e.getKey();
5714         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5715             return;
5716         }
5717         var c = e.getCharCode(), cc = String.fromCharCode(c);
5718         if(Roo.isIE && (e.isSpecialKey() || !cc)){
5719             return;
5720         }
5721         if(!this.maskRe.test(cc)){
5722             e.stopEvent();
5723         }
5724     },
5725      /**
5726      * Clear any invalid styles/messages for this field
5727      */
5728     clearInvalid : function(){
5729         
5730         if(!this.el || this.preventMark){ // not rendered
5731             return;
5732         }
5733         this.el.removeClass(this.invalidClass);
5734         /*
5735         switch(this.msgTarget){
5736             case 'qtip':
5737                 this.el.dom.qtip = '';
5738                 break;
5739             case 'title':
5740                 this.el.dom.title = '';
5741                 break;
5742             case 'under':
5743                 if(this.errorEl){
5744                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5745                 }
5746                 break;
5747             case 'side':
5748                 if(this.errorIcon){
5749                     this.errorIcon.dom.qtip = '';
5750                     this.errorIcon.hide();
5751                     this.un('resize', this.alignErrorIcon, this);
5752                 }
5753                 break;
5754             default:
5755                 var t = Roo.getDom(this.msgTarget);
5756                 t.innerHTML = '';
5757                 t.style.display = 'none';
5758                 break;
5759         }
5760         */
5761         this.fireEvent('valid', this);
5762     },
5763      /**
5764      * Mark this field as invalid
5765      * @param {String} msg The validation message
5766      */
5767     markInvalid : function(msg){
5768         if(!this.el  || this.preventMark){ // not rendered
5769             return;
5770         }
5771         this.el.addClass(this.invalidClass);
5772         /*
5773         msg = msg || this.invalidText;
5774         switch(this.msgTarget){
5775             case 'qtip':
5776                 this.el.dom.qtip = msg;
5777                 this.el.dom.qclass = 'x-form-invalid-tip';
5778                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5779                     Roo.QuickTips.enable();
5780                 }
5781                 break;
5782             case 'title':
5783                 this.el.dom.title = msg;
5784                 break;
5785             case 'under':
5786                 if(!this.errorEl){
5787                     var elp = this.el.findParent('.x-form-element', 5, true);
5788                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5789                     this.errorEl.setWidth(elp.getWidth(true)-20);
5790                 }
5791                 this.errorEl.update(msg);
5792                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5793                 break;
5794             case 'side':
5795                 if(!this.errorIcon){
5796                     var elp = this.el.findParent('.x-form-element', 5, true);
5797                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5798                 }
5799                 this.alignErrorIcon();
5800                 this.errorIcon.dom.qtip = msg;
5801                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5802                 this.errorIcon.show();
5803                 this.on('resize', this.alignErrorIcon, this);
5804                 break;
5805             default:
5806                 var t = Roo.getDom(this.msgTarget);
5807                 t.innerHTML = msg;
5808                 t.style.display = this.msgDisplay;
5809                 break;
5810         }
5811         */
5812         this.fireEvent('invalid', this, msg);
5813     },
5814     // private
5815     SafariOnKeyDown : function(event)
5816     {
5817         // this is a workaround for a password hang bug on chrome/ webkit.
5818         
5819         var isSelectAll = false;
5820         
5821         if(this.inputEl().dom.selectionEnd > 0){
5822             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5823         }
5824         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5825             event.preventDefault();
5826             this.setValue('');
5827             return;
5828         }
5829         
5830         if(isSelectAll){ // backspace and delete key
5831             
5832             event.preventDefault();
5833             // this is very hacky as keydown always get's upper case.
5834             //
5835             var cc = String.fromCharCode(event.getCharCode());
5836             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5837             
5838         }
5839     },
5840     adjustWidth : function(tag, w){
5841         tag = tag.toLowerCase();
5842         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5843             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5844                 if(tag == 'input'){
5845                     return w + 2;
5846                 }
5847                 if(tag == 'textarea'){
5848                     return w-2;
5849                 }
5850             }else if(Roo.isOpera){
5851                 if(tag == 'input'){
5852                     return w + 2;
5853                 }
5854                 if(tag == 'textarea'){
5855                     return w-2;
5856                 }
5857             }
5858         }
5859         return w;
5860     }
5861     
5862 });
5863
5864  
5865 /*
5866  * - LGPL
5867  *
5868  * Input
5869  * 
5870  */
5871
5872 /**
5873  * @class Roo.bootstrap.TextArea
5874  * @extends Roo.bootstrap.Input
5875  * Bootstrap TextArea class
5876  * @cfg {Number} cols Specifies the visible width of a text area
5877  * @cfg {Number} rows Specifies the visible number of lines in a text area
5878  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5879  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5880  * @cfg {string} html text
5881  * 
5882  * @constructor
5883  * Create a new TextArea
5884  * @param {Object} config The config object
5885  */
5886
5887 Roo.bootstrap.TextArea = function(config){
5888     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5889    
5890 };
5891
5892 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5893      
5894     cols : false,
5895     rows : 5,
5896     readOnly : false,
5897     warp : 'soft',
5898     resize : false,
5899     value: false,
5900     html: false,
5901     
5902     getAutoCreate : function(){
5903         
5904         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5905         
5906         var id = Roo.id();
5907         
5908         var cfg = {};
5909         
5910         var input =  {
5911             tag: 'textarea',
5912             id : id,
5913             warp : this.warp,
5914             rows : this.rows,
5915             value : this.value || '',
5916             html: this.html || '',
5917             cls : 'form-control',
5918             placeholder : this.placeholder || '' 
5919             
5920         };
5921         
5922         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5923             input.maxLength = this.maxLength;
5924         }
5925         
5926         if(this.resize){
5927             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5928         }
5929         
5930         if(this.cols){
5931             input.cols = this.cols;
5932         }
5933         
5934         if (this.readOnly) {
5935             input.readonly = true;
5936         }
5937         
5938         if (this.name) {
5939             input.name = this.name;
5940         }
5941         
5942         if (this.size) {
5943             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5944         }
5945         
5946         var settings=this;
5947         ['xs','sm','md','lg'].map(function(size){
5948             if (settings[size]) {
5949                 cfg.cls += ' col-' + size + '-' + settings[size];
5950             }
5951         });
5952         
5953         var inputblock = input;
5954         
5955         if (this.before || this.after) {
5956             
5957             inputblock = {
5958                 cls : 'input-group',
5959                 cn :  [] 
5960             };
5961             if (this.before) {
5962                 inputblock.cn.push({
5963                     tag :'span',
5964                     cls : 'input-group-addon',
5965                     html : this.before
5966                 });
5967             }
5968             inputblock.cn.push(input);
5969             if (this.after) {
5970                 inputblock.cn.push({
5971                     tag :'span',
5972                     cls : 'input-group-addon',
5973                     html : this.after
5974                 });
5975             }
5976             
5977         }
5978         
5979         if (align ==='left' && this.fieldLabel.length) {
5980                 Roo.log("left and has label");
5981                 cfg.cn = [
5982                     
5983                     {
5984                         tag: 'label',
5985                         'for' :  id,
5986                         cls : 'control-label col-sm-' + this.labelWidth,
5987                         html : this.fieldLabel
5988                         
5989                     },
5990                     {
5991                         cls : "col-sm-" + (12 - this.labelWidth), 
5992                         cn: [
5993                             inputblock
5994                         ]
5995                     }
5996                     
5997                 ];
5998         } else if ( this.fieldLabel.length) {
5999                 Roo.log(" label");
6000                  cfg.cn = [
6001                    
6002                     {
6003                         tag: 'label',
6004                         //cls : 'input-group-addon',
6005                         html : this.fieldLabel
6006                         
6007                     },
6008                     
6009                     inputblock
6010                     
6011                 ];
6012
6013         } else {
6014             
6015                    Roo.log(" no label && no align");
6016                 cfg.cn = [
6017                     
6018                         inputblock
6019                     
6020                 ];
6021                 
6022                 
6023         }
6024         
6025         if (this.disabled) {
6026             input.disabled=true;
6027         }
6028         
6029         return cfg;
6030         
6031     },
6032     /**
6033      * return the real textarea element.
6034      */
6035     inputEl: function ()
6036     {
6037         return this.el.select('textarea.form-control',true).first();
6038     }
6039 });
6040
6041  
6042 /*
6043  * - LGPL
6044  *
6045  * trigger field - base class for combo..
6046  * 
6047  */
6048  
6049 /**
6050  * @class Roo.bootstrap.TriggerField
6051  * @extends Roo.bootstrap.Input
6052  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6053  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6054  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6055  * for which you can provide a custom implementation.  For example:
6056  * <pre><code>
6057 var trigger = new Roo.bootstrap.TriggerField();
6058 trigger.onTriggerClick = myTriggerFn;
6059 trigger.applyTo('my-field');
6060 </code></pre>
6061  *
6062  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6063  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6064  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6065  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6066  * @constructor
6067  * Create a new TriggerField.
6068  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6069  * to the base TextField)
6070  */
6071 Roo.bootstrap.TriggerField = function(config){
6072     this.mimicing = false;
6073     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6074 };
6075
6076 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6077     /**
6078      * @cfg {String} triggerClass A CSS class to apply to the trigger
6079      */
6080      /**
6081      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6082      */
6083     hideTrigger:false,
6084
6085     /** @cfg {Boolean} grow @hide */
6086     /** @cfg {Number} growMin @hide */
6087     /** @cfg {Number} growMax @hide */
6088
6089     /**
6090      * @hide 
6091      * @method
6092      */
6093     autoSize: Roo.emptyFn,
6094     // private
6095     monitorTab : true,
6096     // private
6097     deferHeight : true,
6098
6099     
6100     actionMode : 'wrap',
6101     
6102     
6103     
6104     getAutoCreate : function(){
6105        
6106         var parent = this.parent();
6107         
6108         var align = this.parentLabelAlign();
6109         
6110         var id = Roo.id();
6111         
6112         var cfg = {
6113             cls: 'form-group' //input-group
6114         };
6115         
6116         
6117         var input =  {
6118             tag: 'input',
6119             id : id,
6120             type : this.inputType,
6121             cls : 'form-control',
6122             autocomplete: 'off',
6123             placeholder : this.placeholder || '' 
6124             
6125         };
6126         if (this.name) {
6127             input.name = this.name;
6128         }
6129         if (this.size) {
6130             input.cls += ' input-' + this.size;
6131         }
6132         
6133         if (this.disabled) {
6134             input.disabled=true;
6135         }
6136         
6137         var inputblock = input;
6138         
6139         if (this.before || this.after) {
6140             
6141             inputblock = {
6142                 cls : 'input-group',
6143                 cn :  [] 
6144             };
6145             if (this.before) {
6146                 inputblock.cn.push({
6147                     tag :'span',
6148                     cls : 'input-group-addon',
6149                     html : this.before
6150                 });
6151             }
6152             inputblock.cn.push(input);
6153             if (this.after) {
6154                 inputblock.cn.push({
6155                     tag :'span',
6156                     cls : 'input-group-addon',
6157                     html : this.after
6158                 });
6159             }
6160             
6161         };
6162         
6163         var box = {
6164             tag: 'div',
6165             cn: [
6166                 {
6167                     tag: 'input',
6168                     type : 'hidden',
6169                     cls: 'form-hidden-field'
6170                 },
6171                 inputblock
6172             ]
6173             
6174         };
6175         
6176         if(this.multiple){
6177             Roo.log('multiple');
6178             
6179             box = {
6180                 tag: 'div',
6181                 cn: [
6182                     {
6183                         tag: 'input',
6184                         type : 'hidden',
6185                         cls: 'form-hidden-field'
6186                     },
6187                     {
6188                         tag: 'ul',
6189                         cls: 'select2-choices',
6190                         cn:[
6191                             {
6192                                 tag: 'li',
6193                                 cls: 'select2-search-field',
6194                                 cn: [
6195
6196                                     inputblock
6197                                 ]
6198                             }
6199                         ]
6200                     }
6201                 ]
6202             }
6203         };
6204         
6205         var combobox = {
6206             cls: 'select2-container input-group',
6207             cn: [
6208                 box,
6209                 {
6210                     tag: 'ul',
6211                     cls: 'typeahead typeahead-long dropdown-menu',
6212                     style: 'display:none'
6213                 }
6214             ]
6215         };
6216         
6217         if(!this.multiple){
6218             combobox.cn.push({
6219                 tag :'span',
6220                 cls : 'input-group-addon btn dropdown-toggle',
6221                 cn : [
6222                     {
6223                         tag: 'span',
6224                         cls: 'caret'
6225                     },
6226                     {
6227                         tag: 'span',
6228                         cls: 'combobox-clear',
6229                         cn  : [
6230                             {
6231                                 tag : 'i',
6232                                 cls: 'icon-remove'
6233                             }
6234                         ]
6235                     }
6236                 ]
6237
6238             })
6239         }
6240         
6241         if(this.multiple){
6242             combobox.cls += ' select2-container-multi';
6243         }
6244         
6245         if (align ==='left' && this.fieldLabel.length) {
6246             
6247                 Roo.log("left and has label");
6248                 cfg.cn = [
6249                     
6250                     {
6251                         tag: 'label',
6252                         'for' :  id,
6253                         cls : 'control-label col-sm-' + this.labelWidth,
6254                         html : this.fieldLabel
6255                         
6256                     },
6257                     {
6258                         cls : "col-sm-" + (12 - this.labelWidth), 
6259                         cn: [
6260                             combobox
6261                         ]
6262                     }
6263                     
6264                 ];
6265         } else if ( this.fieldLabel.length) {
6266                 Roo.log(" label");
6267                  cfg.cn = [
6268                    
6269                     {
6270                         tag: 'label',
6271                         //cls : 'input-group-addon',
6272                         html : this.fieldLabel
6273                         
6274                     },
6275                     
6276                     combobox
6277                     
6278                 ];
6279
6280         } else {
6281             
6282                 Roo.log(" no label && no align");
6283                 cfg = combobox
6284                      
6285                 
6286         }
6287          
6288         var settings=this;
6289         ['xs','sm','md','lg'].map(function(size){
6290             if (settings[size]) {
6291                 cfg.cls += ' col-' + size + '-' + settings[size];
6292             }
6293         });
6294         
6295         return cfg;
6296         
6297     },
6298     
6299     
6300     
6301     // private
6302     onResize : function(w, h){
6303 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6304 //        if(typeof w == 'number'){
6305 //            var x = w - this.trigger.getWidth();
6306 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6307 //            this.trigger.setStyle('left', x+'px');
6308 //        }
6309     },
6310
6311     // private
6312     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6313
6314     // private
6315     getResizeEl : function(){
6316         return this.inputEl();
6317     },
6318
6319     // private
6320     getPositionEl : function(){
6321         return this.inputEl();
6322     },
6323
6324     // private
6325     alignErrorIcon : function(){
6326         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6327     },
6328
6329     // private
6330     initEvents : function(){
6331         
6332         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6333         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6334         if(!this.multiple){
6335             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6336             if(this.hideTrigger){
6337                 this.trigger.setDisplayed(false);
6338             }
6339             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6340         }
6341         
6342         if(this.multiple){
6343             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6344         }
6345         
6346         //this.trigger.addClassOnOver('x-form-trigger-over');
6347         //this.trigger.addClassOnClick('x-form-trigger-click');
6348         
6349         //if(!this.width){
6350         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6351         //}
6352     },
6353
6354     // private
6355     initTrigger : function(){
6356        
6357     },
6358
6359     // private
6360     onDestroy : function(){
6361         if(this.trigger){
6362             this.trigger.removeAllListeners();
6363           //  this.trigger.remove();
6364         }
6365         //if(this.wrap){
6366         //    this.wrap.remove();
6367         //}
6368         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6369     },
6370
6371     // private
6372     onFocus : function(){
6373         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6374         /*
6375         if(!this.mimicing){
6376             this.wrap.addClass('x-trigger-wrap-focus');
6377             this.mimicing = true;
6378             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6379             if(this.monitorTab){
6380                 this.el.on("keydown", this.checkTab, this);
6381             }
6382         }
6383         */
6384     },
6385
6386     // private
6387     checkTab : function(e){
6388         if(e.getKey() == e.TAB){
6389             this.triggerBlur();
6390         }
6391     },
6392
6393     // private
6394     onBlur : function(){
6395         // do nothing
6396     },
6397
6398     // private
6399     mimicBlur : function(e, t){
6400         /*
6401         if(!this.wrap.contains(t) && this.validateBlur()){
6402             this.triggerBlur();
6403         }
6404         */
6405     },
6406
6407     // private
6408     triggerBlur : function(){
6409         this.mimicing = false;
6410         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6411         if(this.monitorTab){
6412             this.el.un("keydown", this.checkTab, this);
6413         }
6414         //this.wrap.removeClass('x-trigger-wrap-focus');
6415         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6416     },
6417
6418     // private
6419     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6420     validateBlur : function(e, t){
6421         return true;
6422     },
6423
6424     // private
6425     onDisable : function(){
6426         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6427         //if(this.wrap){
6428         //    this.wrap.addClass('x-item-disabled');
6429         //}
6430     },
6431
6432     // private
6433     onEnable : function(){
6434         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6435         //if(this.wrap){
6436         //    this.el.removeClass('x-item-disabled');
6437         //}
6438     },
6439
6440     // private
6441     onShow : function(){
6442         var ae = this.getActionEl();
6443         
6444         if(ae){
6445             ae.dom.style.display = '';
6446             ae.dom.style.visibility = 'visible';
6447         }
6448     },
6449
6450     // private
6451     
6452     onHide : function(){
6453         var ae = this.getActionEl();
6454         ae.dom.style.display = 'none';
6455     },
6456
6457     /**
6458      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6459      * by an implementing function.
6460      * @method
6461      * @param {EventObject} e
6462      */
6463     onTriggerClick : Roo.emptyFn
6464 });
6465  /*
6466  * Based on:
6467  * Ext JS Library 1.1.1
6468  * Copyright(c) 2006-2007, Ext JS, LLC.
6469  *
6470  * Originally Released Under LGPL - original licence link has changed is not relivant.
6471  *
6472  * Fork - LGPL
6473  * <script type="text/javascript">
6474  */
6475
6476
6477 /**
6478  * @class Roo.data.SortTypes
6479  * @singleton
6480  * Defines the default sorting (casting?) comparison functions used when sorting data.
6481  */
6482 Roo.data.SortTypes = {
6483     /**
6484      * Default sort that does nothing
6485      * @param {Mixed} s The value being converted
6486      * @return {Mixed} The comparison value
6487      */
6488     none : function(s){
6489         return s;
6490     },
6491     
6492     /**
6493      * The regular expression used to strip tags
6494      * @type {RegExp}
6495      * @property
6496      */
6497     stripTagsRE : /<\/?[^>]+>/gi,
6498     
6499     /**
6500      * Strips all HTML tags to sort on text only
6501      * @param {Mixed} s The value being converted
6502      * @return {String} The comparison value
6503      */
6504     asText : function(s){
6505         return String(s).replace(this.stripTagsRE, "");
6506     },
6507     
6508     /**
6509      * Strips all HTML tags to sort on text only - Case insensitive
6510      * @param {Mixed} s The value being converted
6511      * @return {String} The comparison value
6512      */
6513     asUCText : function(s){
6514         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6515     },
6516     
6517     /**
6518      * Case insensitive string
6519      * @param {Mixed} s The value being converted
6520      * @return {String} The comparison value
6521      */
6522     asUCString : function(s) {
6523         return String(s).toUpperCase();
6524     },
6525     
6526     /**
6527      * Date sorting
6528      * @param {Mixed} s The value being converted
6529      * @return {Number} The comparison value
6530      */
6531     asDate : function(s) {
6532         if(!s){
6533             return 0;
6534         }
6535         if(s instanceof Date){
6536             return s.getTime();
6537         }
6538         return Date.parse(String(s));
6539     },
6540     
6541     /**
6542      * Float sorting
6543      * @param {Mixed} s The value being converted
6544      * @return {Float} The comparison value
6545      */
6546     asFloat : function(s) {
6547         var val = parseFloat(String(s).replace(/,/g, ""));
6548         if(isNaN(val)) val = 0;
6549         return val;
6550     },
6551     
6552     /**
6553      * Integer sorting
6554      * @param {Mixed} s The value being converted
6555      * @return {Number} The comparison value
6556      */
6557     asInt : function(s) {
6558         var val = parseInt(String(s).replace(/,/g, ""));
6559         if(isNaN(val)) val = 0;
6560         return val;
6561     }
6562 };/*
6563  * Based on:
6564  * Ext JS Library 1.1.1
6565  * Copyright(c) 2006-2007, Ext JS, LLC.
6566  *
6567  * Originally Released Under LGPL - original licence link has changed is not relivant.
6568  *
6569  * Fork - LGPL
6570  * <script type="text/javascript">
6571  */
6572
6573 /**
6574 * @class Roo.data.Record
6575  * Instances of this class encapsulate both record <em>definition</em> information, and record
6576  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6577  * to access Records cached in an {@link Roo.data.Store} object.<br>
6578  * <p>
6579  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6580  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6581  * objects.<br>
6582  * <p>
6583  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6584  * @constructor
6585  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6586  * {@link #create}. The parameters are the same.
6587  * @param {Array} data An associative Array of data values keyed by the field name.
6588  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6589  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6590  * not specified an integer id is generated.
6591  */
6592 Roo.data.Record = function(data, id){
6593     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6594     this.data = data;
6595 };
6596
6597 /**
6598  * Generate a constructor for a specific record layout.
6599  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6600  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6601  * Each field definition object may contain the following properties: <ul>
6602  * <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,
6603  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6604  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6605  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6606  * is being used, then this is a string containing the javascript expression to reference the data relative to 
6607  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6608  * to the data item relative to the record element. If the mapping expression is the same as the field name,
6609  * this may be omitted.</p></li>
6610  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6611  * <ul><li>auto (Default, implies no conversion)</li>
6612  * <li>string</li>
6613  * <li>int</li>
6614  * <li>float</li>
6615  * <li>boolean</li>
6616  * <li>date</li></ul></p></li>
6617  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6618  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6619  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6620  * by the Reader into an object that will be stored in the Record. It is passed the
6621  * following parameters:<ul>
6622  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6623  * </ul></p></li>
6624  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6625  * </ul>
6626  * <br>usage:<br><pre><code>
6627 var TopicRecord = Roo.data.Record.create(
6628     {name: 'title', mapping: 'topic_title'},
6629     {name: 'author', mapping: 'username'},
6630     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6631     {name: 'lastPost', mapping: 'post_time', type: 'date'},
6632     {name: 'lastPoster', mapping: 'user2'},
6633     {name: 'excerpt', mapping: 'post_text'}
6634 );
6635
6636 var myNewRecord = new TopicRecord({
6637     title: 'Do my job please',
6638     author: 'noobie',
6639     totalPosts: 1,
6640     lastPost: new Date(),
6641     lastPoster: 'Animal',
6642     excerpt: 'No way dude!'
6643 });
6644 myStore.add(myNewRecord);
6645 </code></pre>
6646  * @method create
6647  * @static
6648  */
6649 Roo.data.Record.create = function(o){
6650     var f = function(){
6651         f.superclass.constructor.apply(this, arguments);
6652     };
6653     Roo.extend(f, Roo.data.Record);
6654     var p = f.prototype;
6655     p.fields = new Roo.util.MixedCollection(false, function(field){
6656         return field.name;
6657     });
6658     for(var i = 0, len = o.length; i < len; i++){
6659         p.fields.add(new Roo.data.Field(o[i]));
6660     }
6661     f.getField = function(name){
6662         return p.fields.get(name);  
6663     };
6664     return f;
6665 };
6666
6667 Roo.data.Record.AUTO_ID = 1000;
6668 Roo.data.Record.EDIT = 'edit';
6669 Roo.data.Record.REJECT = 'reject';
6670 Roo.data.Record.COMMIT = 'commit';
6671
6672 Roo.data.Record.prototype = {
6673     /**
6674      * Readonly flag - true if this record has been modified.
6675      * @type Boolean
6676      */
6677     dirty : false,
6678     editing : false,
6679     error: null,
6680     modified: null,
6681
6682     // private
6683     join : function(store){
6684         this.store = store;
6685     },
6686
6687     /**
6688      * Set the named field to the specified value.
6689      * @param {String} name The name of the field to set.
6690      * @param {Object} value The value to set the field to.
6691      */
6692     set : function(name, value){
6693         if(this.data[name] == value){
6694             return;
6695         }
6696         this.dirty = true;
6697         if(!this.modified){
6698             this.modified = {};
6699         }
6700         if(typeof this.modified[name] == 'undefined'){
6701             this.modified[name] = this.data[name];
6702         }
6703         this.data[name] = value;
6704         if(!this.editing && this.store){
6705             this.store.afterEdit(this);
6706         }       
6707     },
6708
6709     /**
6710      * Get the value of the named field.
6711      * @param {String} name The name of the field to get the value of.
6712      * @return {Object} The value of the field.
6713      */
6714     get : function(name){
6715         return this.data[name]; 
6716     },
6717
6718     // private
6719     beginEdit : function(){
6720         this.editing = true;
6721         this.modified = {}; 
6722     },
6723
6724     // private
6725     cancelEdit : function(){
6726         this.editing = false;
6727         delete this.modified;
6728     },
6729
6730     // private
6731     endEdit : function(){
6732         this.editing = false;
6733         if(this.dirty && this.store){
6734             this.store.afterEdit(this);
6735         }
6736     },
6737
6738     /**
6739      * Usually called by the {@link Roo.data.Store} which owns the Record.
6740      * Rejects all changes made to the Record since either creation, or the last commit operation.
6741      * Modified fields are reverted to their original values.
6742      * <p>
6743      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6744      * of reject operations.
6745      */
6746     reject : function(){
6747         var m = this.modified;
6748         for(var n in m){
6749             if(typeof m[n] != "function"){
6750                 this.data[n] = m[n];
6751             }
6752         }
6753         this.dirty = false;
6754         delete this.modified;
6755         this.editing = false;
6756         if(this.store){
6757             this.store.afterReject(this);
6758         }
6759     },
6760
6761     /**
6762      * Usually called by the {@link Roo.data.Store} which owns the Record.
6763      * Commits all changes made to the Record since either creation, or the last commit operation.
6764      * <p>
6765      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6766      * of commit operations.
6767      */
6768     commit : function(){
6769         this.dirty = false;
6770         delete this.modified;
6771         this.editing = false;
6772         if(this.store){
6773             this.store.afterCommit(this);
6774         }
6775     },
6776
6777     // private
6778     hasError : function(){
6779         return this.error != null;
6780     },
6781
6782     // private
6783     clearError : function(){
6784         this.error = null;
6785     },
6786
6787     /**
6788      * Creates a copy of this record.
6789      * @param {String} id (optional) A new record id if you don't want to use this record's id
6790      * @return {Record}
6791      */
6792     copy : function(newId) {
6793         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6794     }
6795 };/*
6796  * Based on:
6797  * Ext JS Library 1.1.1
6798  * Copyright(c) 2006-2007, Ext JS, LLC.
6799  *
6800  * Originally Released Under LGPL - original licence link has changed is not relivant.
6801  *
6802  * Fork - LGPL
6803  * <script type="text/javascript">
6804  */
6805
6806
6807
6808 /**
6809  * @class Roo.data.Store
6810  * @extends Roo.util.Observable
6811  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6812  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6813  * <p>
6814  * 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
6815  * has no knowledge of the format of the data returned by the Proxy.<br>
6816  * <p>
6817  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6818  * instances from the data object. These records are cached and made available through accessor functions.
6819  * @constructor
6820  * Creates a new Store.
6821  * @param {Object} config A config object containing the objects needed for the Store to access data,
6822  * and read the data into Records.
6823  */
6824 Roo.data.Store = function(config){
6825     this.data = new Roo.util.MixedCollection(false);
6826     this.data.getKey = function(o){
6827         return o.id;
6828     };
6829     this.baseParams = {};
6830     // private
6831     this.paramNames = {
6832         "start" : "start",
6833         "limit" : "limit",
6834         "sort" : "sort",
6835         "dir" : "dir",
6836         "multisort" : "_multisort"
6837     };
6838
6839     if(config && config.data){
6840         this.inlineData = config.data;
6841         delete config.data;
6842     }
6843
6844     Roo.apply(this, config);
6845     
6846     if(this.reader){ // reader passed
6847         this.reader = Roo.factory(this.reader, Roo.data);
6848         this.reader.xmodule = this.xmodule || false;
6849         if(!this.recordType){
6850             this.recordType = this.reader.recordType;
6851         }
6852         if(this.reader.onMetaChange){
6853             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6854         }
6855     }
6856
6857     if(this.recordType){
6858         this.fields = this.recordType.prototype.fields;
6859     }
6860     this.modified = [];
6861
6862     this.addEvents({
6863         /**
6864          * @event datachanged
6865          * Fires when the data cache has changed, and a widget which is using this Store
6866          * as a Record cache should refresh its view.
6867          * @param {Store} this
6868          */
6869         datachanged : true,
6870         /**
6871          * @event metachange
6872          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6873          * @param {Store} this
6874          * @param {Object} meta The JSON metadata
6875          */
6876         metachange : true,
6877         /**
6878          * @event add
6879          * Fires when Records have been added to the Store
6880          * @param {Store} this
6881          * @param {Roo.data.Record[]} records The array of Records added
6882          * @param {Number} index The index at which the record(s) were added
6883          */
6884         add : true,
6885         /**
6886          * @event remove
6887          * Fires when a Record has been removed from the Store
6888          * @param {Store} this
6889          * @param {Roo.data.Record} record The Record that was removed
6890          * @param {Number} index The index at which the record was removed
6891          */
6892         remove : true,
6893         /**
6894          * @event update
6895          * Fires when a Record has been updated
6896          * @param {Store} this
6897          * @param {Roo.data.Record} record The Record that was updated
6898          * @param {String} operation The update operation being performed.  Value may be one of:
6899          * <pre><code>
6900  Roo.data.Record.EDIT
6901  Roo.data.Record.REJECT
6902  Roo.data.Record.COMMIT
6903          * </code></pre>
6904          */
6905         update : true,
6906         /**
6907          * @event clear
6908          * Fires when the data cache has been cleared.
6909          * @param {Store} this
6910          */
6911         clear : true,
6912         /**
6913          * @event beforeload
6914          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6915          * the load action will be canceled.
6916          * @param {Store} this
6917          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6918          */
6919         beforeload : true,
6920         /**
6921          * @event beforeloadadd
6922          * Fires after a new set of Records has been loaded.
6923          * @param {Store} this
6924          * @param {Roo.data.Record[]} records The Records that were loaded
6925          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6926          */
6927         beforeloadadd : true,
6928         /**
6929          * @event load
6930          * Fires after a new set of Records has been loaded, before they are added to the store.
6931          * @param {Store} this
6932          * @param {Roo.data.Record[]} records The Records that were loaded
6933          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6934          * @params {Object} return from reader
6935          */
6936         load : true,
6937         /**
6938          * @event loadexception
6939          * Fires if an exception occurs in the Proxy during loading.
6940          * Called with the signature of the Proxy's "loadexception" event.
6941          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6942          * 
6943          * @param {Proxy} 
6944          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6945          * @param {Object} load options 
6946          * @param {Object} jsonData from your request (normally this contains the Exception)
6947          */
6948         loadexception : true
6949     });
6950     
6951     if(this.proxy){
6952         this.proxy = Roo.factory(this.proxy, Roo.data);
6953         this.proxy.xmodule = this.xmodule || false;
6954         this.relayEvents(this.proxy,  ["loadexception"]);
6955     }
6956     this.sortToggle = {};
6957     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6958
6959     Roo.data.Store.superclass.constructor.call(this);
6960
6961     if(this.inlineData){
6962         this.loadData(this.inlineData);
6963         delete this.inlineData;
6964     }
6965 };
6966
6967 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6968      /**
6969     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6970     * without a remote query - used by combo/forms at present.
6971     */
6972     
6973     /**
6974     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6975     */
6976     /**
6977     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6978     */
6979     /**
6980     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6981     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6982     */
6983     /**
6984     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6985     * on any HTTP request
6986     */
6987     /**
6988     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6989     */
6990     /**
6991     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6992     */
6993     multiSort: false,
6994     /**
6995     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6996     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6997     */
6998     remoteSort : false,
6999
7000     /**
7001     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7002      * loaded or when a record is removed. (defaults to false).
7003     */
7004     pruneModifiedRecords : false,
7005
7006     // private
7007     lastOptions : null,
7008
7009     /**
7010      * Add Records to the Store and fires the add event.
7011      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7012      */
7013     add : function(records){
7014         records = [].concat(records);
7015         for(var i = 0, len = records.length; i < len; i++){
7016             records[i].join(this);
7017         }
7018         var index = this.data.length;
7019         this.data.addAll(records);
7020         this.fireEvent("add", this, records, index);
7021     },
7022
7023     /**
7024      * Remove a Record from the Store and fires the remove event.
7025      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7026      */
7027     remove : function(record){
7028         var index = this.data.indexOf(record);
7029         this.data.removeAt(index);
7030         if(this.pruneModifiedRecords){
7031             this.modified.remove(record);
7032         }
7033         this.fireEvent("remove", this, record, index);
7034     },
7035
7036     /**
7037      * Remove all Records from the Store and fires the clear event.
7038      */
7039     removeAll : function(){
7040         this.data.clear();
7041         if(this.pruneModifiedRecords){
7042             this.modified = [];
7043         }
7044         this.fireEvent("clear", this);
7045     },
7046
7047     /**
7048      * Inserts Records to the Store at the given index and fires the add event.
7049      * @param {Number} index The start index at which to insert the passed Records.
7050      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7051      */
7052     insert : function(index, records){
7053         records = [].concat(records);
7054         for(var i = 0, len = records.length; i < len; i++){
7055             this.data.insert(index, records[i]);
7056             records[i].join(this);
7057         }
7058         this.fireEvent("add", this, records, index);
7059     },
7060
7061     /**
7062      * Get the index within the cache of the passed Record.
7063      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7064      * @return {Number} The index of the passed Record. Returns -1 if not found.
7065      */
7066     indexOf : function(record){
7067         return this.data.indexOf(record);
7068     },
7069
7070     /**
7071      * Get the index within the cache of the Record with the passed id.
7072      * @param {String} id The id of the Record to find.
7073      * @return {Number} The index of the Record. Returns -1 if not found.
7074      */
7075     indexOfId : function(id){
7076         return this.data.indexOfKey(id);
7077     },
7078
7079     /**
7080      * Get the Record with the specified id.
7081      * @param {String} id The id of the Record to find.
7082      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7083      */
7084     getById : function(id){
7085         return this.data.key(id);
7086     },
7087
7088     /**
7089      * Get the Record at the specified index.
7090      * @param {Number} index The index of the Record to find.
7091      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7092      */
7093     getAt : function(index){
7094         return this.data.itemAt(index);
7095     },
7096
7097     /**
7098      * Returns a range of Records between specified indices.
7099      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7100      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7101      * @return {Roo.data.Record[]} An array of Records
7102      */
7103     getRange : function(start, end){
7104         return this.data.getRange(start, end);
7105     },
7106
7107     // private
7108     storeOptions : function(o){
7109         o = Roo.apply({}, o);
7110         delete o.callback;
7111         delete o.scope;
7112         this.lastOptions = o;
7113     },
7114
7115     /**
7116      * Loads the Record cache from the configured Proxy using the configured Reader.
7117      * <p>
7118      * If using remote paging, then the first load call must specify the <em>start</em>
7119      * and <em>limit</em> properties in the options.params property to establish the initial
7120      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7121      * <p>
7122      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7123      * and this call will return before the new data has been loaded. Perform any post-processing
7124      * in a callback function, or in a "load" event handler.</strong>
7125      * <p>
7126      * @param {Object} options An object containing properties which control loading options:<ul>
7127      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7128      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7129      * passed the following arguments:<ul>
7130      * <li>r : Roo.data.Record[]</li>
7131      * <li>options: Options object from the load call</li>
7132      * <li>success: Boolean success indicator</li></ul></li>
7133      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7134      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7135      * </ul>
7136      */
7137     load : function(options){
7138         options = options || {};
7139         if(this.fireEvent("beforeload", this, options) !== false){
7140             this.storeOptions(options);
7141             var p = Roo.apply(options.params || {}, this.baseParams);
7142             // if meta was not loaded from remote source.. try requesting it.
7143             if (!this.reader.metaFromRemote) {
7144                 p._requestMeta = 1;
7145             }
7146             if(this.sortInfo && this.remoteSort){
7147                 var pn = this.paramNames;
7148                 p[pn["sort"]] = this.sortInfo.field;
7149                 p[pn["dir"]] = this.sortInfo.direction;
7150             }
7151             if (this.multiSort) {
7152                 var pn = this.paramNames;
7153                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7154             }
7155             
7156             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7157         }
7158     },
7159
7160     /**
7161      * Reloads the Record cache from the configured Proxy using the configured Reader and
7162      * the options from the last load operation performed.
7163      * @param {Object} options (optional) An object containing properties which may override the options
7164      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7165      * the most recently used options are reused).
7166      */
7167     reload : function(options){
7168         this.load(Roo.applyIf(options||{}, this.lastOptions));
7169     },
7170
7171     // private
7172     // Called as a callback by the Reader during a load operation.
7173     loadRecords : function(o, options, success){
7174         if(!o || success === false){
7175             if(success !== false){
7176                 this.fireEvent("load", this, [], options, o);
7177             }
7178             if(options.callback){
7179                 options.callback.call(options.scope || this, [], options, false);
7180             }
7181             return;
7182         }
7183         // if data returned failure - throw an exception.
7184         if (o.success === false) {
7185             // show a message if no listener is registered.
7186             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7187                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7188             }
7189             // loadmask wil be hooked into this..
7190             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7191             return;
7192         }
7193         var r = o.records, t = o.totalRecords || r.length;
7194         
7195         this.fireEvent("beforeloadadd", this, r, options, o);
7196         
7197         if(!options || options.add !== true){
7198             if(this.pruneModifiedRecords){
7199                 this.modified = [];
7200             }
7201             for(var i = 0, len = r.length; i < len; i++){
7202                 r[i].join(this);
7203             }
7204             if(this.snapshot){
7205                 this.data = this.snapshot;
7206                 delete this.snapshot;
7207             }
7208             this.data.clear();
7209             this.data.addAll(r);
7210             this.totalLength = t;
7211             this.applySort();
7212             this.fireEvent("datachanged", this);
7213         }else{
7214             this.totalLength = Math.max(t, this.data.length+r.length);
7215             this.add(r);
7216         }
7217         this.fireEvent("load", this, r, options, o);
7218         if(options.callback){
7219             options.callback.call(options.scope || this, r, options, true);
7220         }
7221     },
7222
7223
7224     /**
7225      * Loads data from a passed data block. A Reader which understands the format of the data
7226      * must have been configured in the constructor.
7227      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7228      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7229      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7230      */
7231     loadData : function(o, append){
7232         var r = this.reader.readRecords(o);
7233         this.loadRecords(r, {add: append}, true);
7234     },
7235
7236     /**
7237      * Gets the number of cached records.
7238      * <p>
7239      * <em>If using paging, this may not be the total size of the dataset. If the data object
7240      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7241      * the data set size</em>
7242      */
7243     getCount : function(){
7244         return this.data.length || 0;
7245     },
7246
7247     /**
7248      * Gets the total number of records in the dataset as returned by the server.
7249      * <p>
7250      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7251      * the dataset size</em>
7252      */
7253     getTotalCount : function(){
7254         return this.totalLength || 0;
7255     },
7256
7257     /**
7258      * Returns the sort state of the Store as an object with two properties:
7259      * <pre><code>
7260  field {String} The name of the field by which the Records are sorted
7261  direction {String} The sort order, "ASC" or "DESC"
7262      * </code></pre>
7263      */
7264     getSortState : function(){
7265         return this.sortInfo;
7266     },
7267
7268     // private
7269     applySort : function(){
7270         if(this.sortInfo && !this.remoteSort){
7271             var s = this.sortInfo, f = s.field;
7272             var st = this.fields.get(f).sortType;
7273             var fn = function(r1, r2){
7274                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7275                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7276             };
7277             this.data.sort(s.direction, fn);
7278             if(this.snapshot && this.snapshot != this.data){
7279                 this.snapshot.sort(s.direction, fn);
7280             }
7281         }
7282     },
7283
7284     /**
7285      * Sets the default sort column and order to be used by the next load operation.
7286      * @param {String} fieldName The name of the field to sort by.
7287      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7288      */
7289     setDefaultSort : function(field, dir){
7290         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7291     },
7292
7293     /**
7294      * Sort the Records.
7295      * If remote sorting is used, the sort is performed on the server, and the cache is
7296      * reloaded. If local sorting is used, the cache is sorted internally.
7297      * @param {String} fieldName The name of the field to sort by.
7298      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7299      */
7300     sort : function(fieldName, dir){
7301         var f = this.fields.get(fieldName);
7302         if(!dir){
7303             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7304             
7305             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7306                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7307             }else{
7308                 dir = f.sortDir;
7309             }
7310         }
7311         this.sortToggle[f.name] = dir;
7312         this.sortInfo = {field: f.name, direction: dir};
7313         if(!this.remoteSort){
7314             this.applySort();
7315             this.fireEvent("datachanged", this);
7316         }else{
7317             this.load(this.lastOptions);
7318         }
7319     },
7320
7321     /**
7322      * Calls the specified function for each of the Records in the cache.
7323      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7324      * Returning <em>false</em> aborts and exits the iteration.
7325      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7326      */
7327     each : function(fn, scope){
7328         this.data.each(fn, scope);
7329     },
7330
7331     /**
7332      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7333      * (e.g., during paging).
7334      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7335      */
7336     getModifiedRecords : function(){
7337         return this.modified;
7338     },
7339
7340     // private
7341     createFilterFn : function(property, value, anyMatch){
7342         if(!value.exec){ // not a regex
7343             value = String(value);
7344             if(value.length == 0){
7345                 return false;
7346             }
7347             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7348         }
7349         return function(r){
7350             return value.test(r.data[property]);
7351         };
7352     },
7353
7354     /**
7355      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7356      * @param {String} property A field on your records
7357      * @param {Number} start The record index to start at (defaults to 0)
7358      * @param {Number} end The last record index to include (defaults to length - 1)
7359      * @return {Number} The sum
7360      */
7361     sum : function(property, start, end){
7362         var rs = this.data.items, v = 0;
7363         start = start || 0;
7364         end = (end || end === 0) ? end : rs.length-1;
7365
7366         for(var i = start; i <= end; i++){
7367             v += (rs[i].data[property] || 0);
7368         }
7369         return v;
7370     },
7371
7372     /**
7373      * Filter the records by a specified property.
7374      * @param {String} field A field on your records
7375      * @param {String/RegExp} value Either a string that the field
7376      * should start with or a RegExp to test against the field
7377      * @param {Boolean} anyMatch True to match any part not just the beginning
7378      */
7379     filter : function(property, value, anyMatch){
7380         var fn = this.createFilterFn(property, value, anyMatch);
7381         return fn ? this.filterBy(fn) : this.clearFilter();
7382     },
7383
7384     /**
7385      * Filter by a function. The specified function will be called with each
7386      * record in this data source. If the function returns true the record is included,
7387      * otherwise it is filtered.
7388      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7389      * @param {Object} scope (optional) The scope of the function (defaults to this)
7390      */
7391     filterBy : function(fn, scope){
7392         this.snapshot = this.snapshot || this.data;
7393         this.data = this.queryBy(fn, scope||this);
7394         this.fireEvent("datachanged", this);
7395     },
7396
7397     /**
7398      * Query the records by a specified property.
7399      * @param {String} field A field on your records
7400      * @param {String/RegExp} value Either a string that the field
7401      * should start with or a RegExp to test against the field
7402      * @param {Boolean} anyMatch True to match any part not just the beginning
7403      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7404      */
7405     query : function(property, value, anyMatch){
7406         var fn = this.createFilterFn(property, value, anyMatch);
7407         return fn ? this.queryBy(fn) : this.data.clone();
7408     },
7409
7410     /**
7411      * Query by a function. The specified function will be called with each
7412      * record in this data source. If the function returns true the record is included
7413      * in the results.
7414      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7415      * @param {Object} scope (optional) The scope of the function (defaults to this)
7416       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7417      **/
7418     queryBy : function(fn, scope){
7419         var data = this.snapshot || this.data;
7420         return data.filterBy(fn, scope||this);
7421     },
7422
7423     /**
7424      * Collects unique values for a particular dataIndex from this store.
7425      * @param {String} dataIndex The property to collect
7426      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7427      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7428      * @return {Array} An array of the unique values
7429      **/
7430     collect : function(dataIndex, allowNull, bypassFilter){
7431         var d = (bypassFilter === true && this.snapshot) ?
7432                 this.snapshot.items : this.data.items;
7433         var v, sv, r = [], l = {};
7434         for(var i = 0, len = d.length; i < len; i++){
7435             v = d[i].data[dataIndex];
7436             sv = String(v);
7437             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7438                 l[sv] = true;
7439                 r[r.length] = v;
7440             }
7441         }
7442         return r;
7443     },
7444
7445     /**
7446      * Revert to a view of the Record cache with no filtering applied.
7447      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7448      */
7449     clearFilter : function(suppressEvent){
7450         if(this.snapshot && this.snapshot != this.data){
7451             this.data = this.snapshot;
7452             delete this.snapshot;
7453             if(suppressEvent !== true){
7454                 this.fireEvent("datachanged", this);
7455             }
7456         }
7457     },
7458
7459     // private
7460     afterEdit : function(record){
7461         if(this.modified.indexOf(record) == -1){
7462             this.modified.push(record);
7463         }
7464         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7465     },
7466     
7467     // private
7468     afterReject : function(record){
7469         this.modified.remove(record);
7470         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7471     },
7472
7473     // private
7474     afterCommit : function(record){
7475         this.modified.remove(record);
7476         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7477     },
7478
7479     /**
7480      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7481      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7482      */
7483     commitChanges : function(){
7484         var m = this.modified.slice(0);
7485         this.modified = [];
7486         for(var i = 0, len = m.length; i < len; i++){
7487             m[i].commit();
7488         }
7489     },
7490
7491     /**
7492      * Cancel outstanding changes on all changed records.
7493      */
7494     rejectChanges : function(){
7495         var m = this.modified.slice(0);
7496         this.modified = [];
7497         for(var i = 0, len = m.length; i < len; i++){
7498             m[i].reject();
7499         }
7500     },
7501
7502     onMetaChange : function(meta, rtype, o){
7503         this.recordType = rtype;
7504         this.fields = rtype.prototype.fields;
7505         delete this.snapshot;
7506         this.sortInfo = meta.sortInfo || this.sortInfo;
7507         this.modified = [];
7508         this.fireEvent('metachange', this, this.reader.meta);
7509     },
7510     
7511     moveIndex : function(data, type)
7512     {
7513         var index = this.indexOf(data);
7514         
7515         var newIndex = index + type;
7516         
7517         this.remove(data);
7518         
7519         this.insert(newIndex, data);
7520         
7521     }
7522 });/*
7523  * Based on:
7524  * Ext JS Library 1.1.1
7525  * Copyright(c) 2006-2007, Ext JS, LLC.
7526  *
7527  * Originally Released Under LGPL - original licence link has changed is not relivant.
7528  *
7529  * Fork - LGPL
7530  * <script type="text/javascript">
7531  */
7532
7533 /**
7534  * @class Roo.data.SimpleStore
7535  * @extends Roo.data.Store
7536  * Small helper class to make creating Stores from Array data easier.
7537  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7538  * @cfg {Array} fields An array of field definition objects, or field name strings.
7539  * @cfg {Array} data The multi-dimensional array of data
7540  * @constructor
7541  * @param {Object} config
7542  */
7543 Roo.data.SimpleStore = function(config){
7544     Roo.data.SimpleStore.superclass.constructor.call(this, {
7545         isLocal : true,
7546         reader: new Roo.data.ArrayReader({
7547                 id: config.id
7548             },
7549             Roo.data.Record.create(config.fields)
7550         ),
7551         proxy : new Roo.data.MemoryProxy(config.data)
7552     });
7553     this.load();
7554 };
7555 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7556  * Based on:
7557  * Ext JS Library 1.1.1
7558  * Copyright(c) 2006-2007, Ext JS, LLC.
7559  *
7560  * Originally Released Under LGPL - original licence link has changed is not relivant.
7561  *
7562  * Fork - LGPL
7563  * <script type="text/javascript">
7564  */
7565
7566 /**
7567 /**
7568  * @extends Roo.data.Store
7569  * @class Roo.data.JsonStore
7570  * Small helper class to make creating Stores for JSON data easier. <br/>
7571 <pre><code>
7572 var store = new Roo.data.JsonStore({
7573     url: 'get-images.php',
7574     root: 'images',
7575     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7576 });
7577 </code></pre>
7578  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7579  * JsonReader and HttpProxy (unless inline data is provided).</b>
7580  * @cfg {Array} fields An array of field definition objects, or field name strings.
7581  * @constructor
7582  * @param {Object} config
7583  */
7584 Roo.data.JsonStore = function(c){
7585     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7586         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7587         reader: new Roo.data.JsonReader(c, c.fields)
7588     }));
7589 };
7590 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7591  * Based on:
7592  * Ext JS Library 1.1.1
7593  * Copyright(c) 2006-2007, Ext JS, LLC.
7594  *
7595  * Originally Released Under LGPL - original licence link has changed is not relivant.
7596  *
7597  * Fork - LGPL
7598  * <script type="text/javascript">
7599  */
7600
7601  
7602 Roo.data.Field = function(config){
7603     if(typeof config == "string"){
7604         config = {name: config};
7605     }
7606     Roo.apply(this, config);
7607     
7608     if(!this.type){
7609         this.type = "auto";
7610     }
7611     
7612     var st = Roo.data.SortTypes;
7613     // named sortTypes are supported, here we look them up
7614     if(typeof this.sortType == "string"){
7615         this.sortType = st[this.sortType];
7616     }
7617     
7618     // set default sortType for strings and dates
7619     if(!this.sortType){
7620         switch(this.type){
7621             case "string":
7622                 this.sortType = st.asUCString;
7623                 break;
7624             case "date":
7625                 this.sortType = st.asDate;
7626                 break;
7627             default:
7628                 this.sortType = st.none;
7629         }
7630     }
7631
7632     // define once
7633     var stripRe = /[\$,%]/g;
7634
7635     // prebuilt conversion function for this field, instead of
7636     // switching every time we're reading a value
7637     if(!this.convert){
7638         var cv, dateFormat = this.dateFormat;
7639         switch(this.type){
7640             case "":
7641             case "auto":
7642             case undefined:
7643                 cv = function(v){ return v; };
7644                 break;
7645             case "string":
7646                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7647                 break;
7648             case "int":
7649                 cv = function(v){
7650                     return v !== undefined && v !== null && v !== '' ?
7651                            parseInt(String(v).replace(stripRe, ""), 10) : '';
7652                     };
7653                 break;
7654             case "float":
7655                 cv = function(v){
7656                     return v !== undefined && v !== null && v !== '' ?
7657                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
7658                     };
7659                 break;
7660             case "bool":
7661             case "boolean":
7662                 cv = function(v){ return v === true || v === "true" || v == 1; };
7663                 break;
7664             case "date":
7665                 cv = function(v){
7666                     if(!v){
7667                         return '';
7668                     }
7669                     if(v instanceof Date){
7670                         return v;
7671                     }
7672                     if(dateFormat){
7673                         if(dateFormat == "timestamp"){
7674                             return new Date(v*1000);
7675                         }
7676                         return Date.parseDate(v, dateFormat);
7677                     }
7678                     var parsed = Date.parse(v);
7679                     return parsed ? new Date(parsed) : null;
7680                 };
7681              break;
7682             
7683         }
7684         this.convert = cv;
7685     }
7686 };
7687
7688 Roo.data.Field.prototype = {
7689     dateFormat: null,
7690     defaultValue: "",
7691     mapping: null,
7692     sortType : null,
7693     sortDir : "ASC"
7694 };/*
7695  * Based on:
7696  * Ext JS Library 1.1.1
7697  * Copyright(c) 2006-2007, Ext JS, LLC.
7698  *
7699  * Originally Released Under LGPL - original licence link has changed is not relivant.
7700  *
7701  * Fork - LGPL
7702  * <script type="text/javascript">
7703  */
7704  
7705 // Base class for reading structured data from a data source.  This class is intended to be
7706 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7707
7708 /**
7709  * @class Roo.data.DataReader
7710  * Base class for reading structured data from a data source.  This class is intended to be
7711  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7712  */
7713
7714 Roo.data.DataReader = function(meta, recordType){
7715     
7716     this.meta = meta;
7717     
7718     this.recordType = recordType instanceof Array ? 
7719         Roo.data.Record.create(recordType) : recordType;
7720 };
7721
7722 Roo.data.DataReader.prototype = {
7723      /**
7724      * Create an empty record
7725      * @param {Object} data (optional) - overlay some values
7726      * @return {Roo.data.Record} record created.
7727      */
7728     newRow :  function(d) {
7729         var da =  {};
7730         this.recordType.prototype.fields.each(function(c) {
7731             switch( c.type) {
7732                 case 'int' : da[c.name] = 0; break;
7733                 case 'date' : da[c.name] = new Date(); break;
7734                 case 'float' : da[c.name] = 0.0; break;
7735                 case 'boolean' : da[c.name] = false; break;
7736                 default : da[c.name] = ""; break;
7737             }
7738             
7739         });
7740         return new this.recordType(Roo.apply(da, d));
7741     }
7742     
7743 };/*
7744  * Based on:
7745  * Ext JS Library 1.1.1
7746  * Copyright(c) 2006-2007, Ext JS, LLC.
7747  *
7748  * Originally Released Under LGPL - original licence link has changed is not relivant.
7749  *
7750  * Fork - LGPL
7751  * <script type="text/javascript">
7752  */
7753
7754 /**
7755  * @class Roo.data.DataProxy
7756  * @extends Roo.data.Observable
7757  * This class is an abstract base class for implementations which provide retrieval of
7758  * unformatted data objects.<br>
7759  * <p>
7760  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7761  * (of the appropriate type which knows how to parse the data object) to provide a block of
7762  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7763  * <p>
7764  * Custom implementations must implement the load method as described in
7765  * {@link Roo.data.HttpProxy#load}.
7766  */
7767 Roo.data.DataProxy = function(){
7768     this.addEvents({
7769         /**
7770          * @event beforeload
7771          * Fires before a network request is made to retrieve a data object.
7772          * @param {Object} This DataProxy object.
7773          * @param {Object} params The params parameter to the load function.
7774          */
7775         beforeload : true,
7776         /**
7777          * @event load
7778          * Fires before the load method's callback is called.
7779          * @param {Object} This DataProxy object.
7780          * @param {Object} o The data object.
7781          * @param {Object} arg The callback argument object passed to the load function.
7782          */
7783         load : true,
7784         /**
7785          * @event loadexception
7786          * Fires if an Exception occurs during data retrieval.
7787          * @param {Object} This DataProxy object.
7788          * @param {Object} o The data object.
7789          * @param {Object} arg The callback argument object passed to the load function.
7790          * @param {Object} e The Exception.
7791          */
7792         loadexception : true
7793     });
7794     Roo.data.DataProxy.superclass.constructor.call(this);
7795 };
7796
7797 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7798
7799     /**
7800      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7801      */
7802 /*
7803  * Based on:
7804  * Ext JS Library 1.1.1
7805  * Copyright(c) 2006-2007, Ext JS, LLC.
7806  *
7807  * Originally Released Under LGPL - original licence link has changed is not relivant.
7808  *
7809  * Fork - LGPL
7810  * <script type="text/javascript">
7811  */
7812 /**
7813  * @class Roo.data.MemoryProxy
7814  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7815  * to the Reader when its load method is called.
7816  * @constructor
7817  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7818  */
7819 Roo.data.MemoryProxy = function(data){
7820     if (data.data) {
7821         data = data.data;
7822     }
7823     Roo.data.MemoryProxy.superclass.constructor.call(this);
7824     this.data = data;
7825 };
7826
7827 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7828     /**
7829      * Load data from the requested source (in this case an in-memory
7830      * data object passed to the constructor), read the data object into
7831      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7832      * process that block using the passed callback.
7833      * @param {Object} params This parameter is not used by the MemoryProxy class.
7834      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7835      * object into a block of Roo.data.Records.
7836      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7837      * The function must be passed <ul>
7838      * <li>The Record block object</li>
7839      * <li>The "arg" argument from the load function</li>
7840      * <li>A boolean success indicator</li>
7841      * </ul>
7842      * @param {Object} scope The scope in which to call the callback
7843      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7844      */
7845     load : function(params, reader, callback, scope, arg){
7846         params = params || {};
7847         var result;
7848         try {
7849             result = reader.readRecords(this.data);
7850         }catch(e){
7851             this.fireEvent("loadexception", this, arg, null, e);
7852             callback.call(scope, null, arg, false);
7853             return;
7854         }
7855         callback.call(scope, result, arg, true);
7856     },
7857     
7858     // private
7859     update : function(params, records){
7860         
7861     }
7862 });/*
7863  * Based on:
7864  * Ext JS Library 1.1.1
7865  * Copyright(c) 2006-2007, Ext JS, LLC.
7866  *
7867  * Originally Released Under LGPL - original licence link has changed is not relivant.
7868  *
7869  * Fork - LGPL
7870  * <script type="text/javascript">
7871  */
7872 /**
7873  * @class Roo.data.HttpProxy
7874  * @extends Roo.data.DataProxy
7875  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7876  * configured to reference a certain URL.<br><br>
7877  * <p>
7878  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7879  * from which the running page was served.<br><br>
7880  * <p>
7881  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7882  * <p>
7883  * Be aware that to enable the browser to parse an XML document, the server must set
7884  * the Content-Type header in the HTTP response to "text/xml".
7885  * @constructor
7886  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7887  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7888  * will be used to make the request.
7889  */
7890 Roo.data.HttpProxy = function(conn){
7891     Roo.data.HttpProxy.superclass.constructor.call(this);
7892     // is conn a conn config or a real conn?
7893     this.conn = conn;
7894     this.useAjax = !conn || !conn.events;
7895   
7896 };
7897
7898 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7899     // thse are take from connection...
7900     
7901     /**
7902      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7903      */
7904     /**
7905      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7906      * extra parameters to each request made by this object. (defaults to undefined)
7907      */
7908     /**
7909      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7910      *  to each request made by this object. (defaults to undefined)
7911      */
7912     /**
7913      * @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)
7914      */
7915     /**
7916      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7917      */
7918      /**
7919      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7920      * @type Boolean
7921      */
7922   
7923
7924     /**
7925      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7926      * @type Boolean
7927      */
7928     /**
7929      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7930      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7931      * a finer-grained basis than the DataProxy events.
7932      */
7933     getConnection : function(){
7934         return this.useAjax ? Roo.Ajax : this.conn;
7935     },
7936
7937     /**
7938      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7939      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7940      * process that block using the passed callback.
7941      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7942      * for the request to the remote server.
7943      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7944      * object into a block of Roo.data.Records.
7945      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7946      * The function must be passed <ul>
7947      * <li>The Record block object</li>
7948      * <li>The "arg" argument from the load function</li>
7949      * <li>A boolean success indicator</li>
7950      * </ul>
7951      * @param {Object} scope The scope in which to call the callback
7952      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7953      */
7954     load : function(params, reader, callback, scope, arg){
7955         if(this.fireEvent("beforeload", this, params) !== false){
7956             var  o = {
7957                 params : params || {},
7958                 request: {
7959                     callback : callback,
7960                     scope : scope,
7961                     arg : arg
7962                 },
7963                 reader: reader,
7964                 callback : this.loadResponse,
7965                 scope: this
7966             };
7967             if(this.useAjax){
7968                 Roo.applyIf(o, this.conn);
7969                 if(this.activeRequest){
7970                     Roo.Ajax.abort(this.activeRequest);
7971                 }
7972                 this.activeRequest = Roo.Ajax.request(o);
7973             }else{
7974                 this.conn.request(o);
7975             }
7976         }else{
7977             callback.call(scope||this, null, arg, false);
7978         }
7979     },
7980
7981     // private
7982     loadResponse : function(o, success, response){
7983         delete this.activeRequest;
7984         if(!success){
7985             this.fireEvent("loadexception", this, o, response);
7986             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7987             return;
7988         }
7989         var result;
7990         try {
7991             result = o.reader.read(response);
7992         }catch(e){
7993             this.fireEvent("loadexception", this, o, response, e);
7994             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7995             return;
7996         }
7997         
7998         this.fireEvent("load", this, o, o.request.arg);
7999         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8000     },
8001
8002     // private
8003     update : function(dataSet){
8004
8005     },
8006
8007     // private
8008     updateResponse : function(dataSet){
8009
8010     }
8011 });/*
8012  * Based on:
8013  * Ext JS Library 1.1.1
8014  * Copyright(c) 2006-2007, Ext JS, LLC.
8015  *
8016  * Originally Released Under LGPL - original licence link has changed is not relivant.
8017  *
8018  * Fork - LGPL
8019  * <script type="text/javascript">
8020  */
8021
8022 /**
8023  * @class Roo.data.ScriptTagProxy
8024  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8025  * other than the originating domain of the running page.<br><br>
8026  * <p>
8027  * <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
8028  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8029  * <p>
8030  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8031  * source code that is used as the source inside a &lt;script> tag.<br><br>
8032  * <p>
8033  * In order for the browser to process the returned data, the server must wrap the data object
8034  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8035  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8036  * depending on whether the callback name was passed:
8037  * <p>
8038  * <pre><code>
8039 boolean scriptTag = false;
8040 String cb = request.getParameter("callback");
8041 if (cb != null) {
8042     scriptTag = true;
8043     response.setContentType("text/javascript");
8044 } else {
8045     response.setContentType("application/x-json");
8046 }
8047 Writer out = response.getWriter();
8048 if (scriptTag) {
8049     out.write(cb + "(");
8050 }
8051 out.print(dataBlock.toJsonString());
8052 if (scriptTag) {
8053     out.write(");");
8054 }
8055 </pre></code>
8056  *
8057  * @constructor
8058  * @param {Object} config A configuration object.
8059  */
8060 Roo.data.ScriptTagProxy = function(config){
8061     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8062     Roo.apply(this, config);
8063     this.head = document.getElementsByTagName("head")[0];
8064 };
8065
8066 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8067
8068 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8069     /**
8070      * @cfg {String} url The URL from which to request the data object.
8071      */
8072     /**
8073      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8074      */
8075     timeout : 30000,
8076     /**
8077      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8078      * the server the name of the callback function set up by the load call to process the returned data object.
8079      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8080      * javascript output which calls this named function passing the data object as its only parameter.
8081      */
8082     callbackParam : "callback",
8083     /**
8084      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8085      * name to the request.
8086      */
8087     nocache : true,
8088
8089     /**
8090      * Load data from the configured URL, read the data object into
8091      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8092      * process that block using the passed callback.
8093      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8094      * for the request to the remote server.
8095      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8096      * object into a block of Roo.data.Records.
8097      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8098      * The function must be passed <ul>
8099      * <li>The Record block object</li>
8100      * <li>The "arg" argument from the load function</li>
8101      * <li>A boolean success indicator</li>
8102      * </ul>
8103      * @param {Object} scope The scope in which to call the callback
8104      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8105      */
8106     load : function(params, reader, callback, scope, arg){
8107         if(this.fireEvent("beforeload", this, params) !== false){
8108
8109             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8110
8111             var url = this.url;
8112             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8113             if(this.nocache){
8114                 url += "&_dc=" + (new Date().getTime());
8115             }
8116             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8117             var trans = {
8118                 id : transId,
8119                 cb : "stcCallback"+transId,
8120                 scriptId : "stcScript"+transId,
8121                 params : params,
8122                 arg : arg,
8123                 url : url,
8124                 callback : callback,
8125                 scope : scope,
8126                 reader : reader
8127             };
8128             var conn = this;
8129
8130             window[trans.cb] = function(o){
8131                 conn.handleResponse(o, trans);
8132             };
8133
8134             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8135
8136             if(this.autoAbort !== false){
8137                 this.abort();
8138             }
8139
8140             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8141
8142             var script = document.createElement("script");
8143             script.setAttribute("src", url);
8144             script.setAttribute("type", "text/javascript");
8145             script.setAttribute("id", trans.scriptId);
8146             this.head.appendChild(script);
8147
8148             this.trans = trans;
8149         }else{
8150             callback.call(scope||this, null, arg, false);
8151         }
8152     },
8153
8154     // private
8155     isLoading : function(){
8156         return this.trans ? true : false;
8157     },
8158
8159     /**
8160      * Abort the current server request.
8161      */
8162     abort : function(){
8163         if(this.isLoading()){
8164             this.destroyTrans(this.trans);
8165         }
8166     },
8167
8168     // private
8169     destroyTrans : function(trans, isLoaded){
8170         this.head.removeChild(document.getElementById(trans.scriptId));
8171         clearTimeout(trans.timeoutId);
8172         if(isLoaded){
8173             window[trans.cb] = undefined;
8174             try{
8175                 delete window[trans.cb];
8176             }catch(e){}
8177         }else{
8178             // if hasn't been loaded, wait for load to remove it to prevent script error
8179             window[trans.cb] = function(){
8180                 window[trans.cb] = undefined;
8181                 try{
8182                     delete window[trans.cb];
8183                 }catch(e){}
8184             };
8185         }
8186     },
8187
8188     // private
8189     handleResponse : function(o, trans){
8190         this.trans = false;
8191         this.destroyTrans(trans, true);
8192         var result;
8193         try {
8194             result = trans.reader.readRecords(o);
8195         }catch(e){
8196             this.fireEvent("loadexception", this, o, trans.arg, e);
8197             trans.callback.call(trans.scope||window, null, trans.arg, false);
8198             return;
8199         }
8200         this.fireEvent("load", this, o, trans.arg);
8201         trans.callback.call(trans.scope||window, result, trans.arg, true);
8202     },
8203
8204     // private
8205     handleFailure : function(trans){
8206         this.trans = false;
8207         this.destroyTrans(trans, false);
8208         this.fireEvent("loadexception", this, null, trans.arg);
8209         trans.callback.call(trans.scope||window, null, trans.arg, false);
8210     }
8211 });/*
8212  * Based on:
8213  * Ext JS Library 1.1.1
8214  * Copyright(c) 2006-2007, Ext JS, LLC.
8215  *
8216  * Originally Released Under LGPL - original licence link has changed is not relivant.
8217  *
8218  * Fork - LGPL
8219  * <script type="text/javascript">
8220  */
8221
8222 /**
8223  * @class Roo.data.JsonReader
8224  * @extends Roo.data.DataReader
8225  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8226  * based on mappings in a provided Roo.data.Record constructor.
8227  * 
8228  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8229  * in the reply previously. 
8230  * 
8231  * <p>
8232  * Example code:
8233  * <pre><code>
8234 var RecordDef = Roo.data.Record.create([
8235     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8236     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8237 ]);
8238 var myReader = new Roo.data.JsonReader({
8239     totalProperty: "results",    // The property which contains the total dataset size (optional)
8240     root: "rows",                // The property which contains an Array of row objects
8241     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8242 }, RecordDef);
8243 </code></pre>
8244  * <p>
8245  * This would consume a JSON file like this:
8246  * <pre><code>
8247 { 'results': 2, 'rows': [
8248     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8249     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8250 }
8251 </code></pre>
8252  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8253  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8254  * paged from the remote server.
8255  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8256  * @cfg {String} root name of the property which contains the Array of row objects.
8257  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8258  * @constructor
8259  * Create a new JsonReader
8260  * @param {Object} meta Metadata configuration options
8261  * @param {Object} recordType Either an Array of field definition objects,
8262  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8263  */
8264 Roo.data.JsonReader = function(meta, recordType){
8265     
8266     meta = meta || {};
8267     // set some defaults:
8268     Roo.applyIf(meta, {
8269         totalProperty: 'total',
8270         successProperty : 'success',
8271         root : 'data',
8272         id : 'id'
8273     });
8274     
8275     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8276 };
8277 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8278     
8279     /**
8280      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8281      * Used by Store query builder to append _requestMeta to params.
8282      * 
8283      */
8284     metaFromRemote : false,
8285     /**
8286      * This method is only used by a DataProxy which has retrieved data from a remote server.
8287      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8288      * @return {Object} data A data block which is used by an Roo.data.Store object as
8289      * a cache of Roo.data.Records.
8290      */
8291     read : function(response){
8292         var json = response.responseText;
8293        
8294         var o = /* eval:var:o */ eval("("+json+")");
8295         if(!o) {
8296             throw {message: "JsonReader.read: Json object not found"};
8297         }
8298         
8299         if(o.metaData){
8300             
8301             delete this.ef;
8302             this.metaFromRemote = true;
8303             this.meta = o.metaData;
8304             this.recordType = Roo.data.Record.create(o.metaData.fields);
8305             this.onMetaChange(this.meta, this.recordType, o);
8306         }
8307         return this.readRecords(o);
8308     },
8309
8310     // private function a store will implement
8311     onMetaChange : function(meta, recordType, o){
8312
8313     },
8314
8315     /**
8316          * @ignore
8317          */
8318     simpleAccess: function(obj, subsc) {
8319         return obj[subsc];
8320     },
8321
8322         /**
8323          * @ignore
8324          */
8325     getJsonAccessor: function(){
8326         var re = /[\[\.]/;
8327         return function(expr) {
8328             try {
8329                 return(re.test(expr))
8330                     ? new Function("obj", "return obj." + expr)
8331                     : function(obj){
8332                         return obj[expr];
8333                     };
8334             } catch(e){}
8335             return Roo.emptyFn;
8336         };
8337     }(),
8338
8339     /**
8340      * Create a data block containing Roo.data.Records from an XML document.
8341      * @param {Object} o An object which contains an Array of row objects in the property specified
8342      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8343      * which contains the total size of the dataset.
8344      * @return {Object} data A data block which is used by an Roo.data.Store object as
8345      * a cache of Roo.data.Records.
8346      */
8347     readRecords : function(o){
8348         /**
8349          * After any data loads, the raw JSON data is available for further custom processing.
8350          * @type Object
8351          */
8352         this.o = o;
8353         var s = this.meta, Record = this.recordType,
8354             f = Record.prototype.fields, fi = f.items, fl = f.length;
8355
8356 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8357         if (!this.ef) {
8358             if(s.totalProperty) {
8359                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8360                 }
8361                 if(s.successProperty) {
8362                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8363                 }
8364                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8365                 if (s.id) {
8366                         var g = this.getJsonAccessor(s.id);
8367                         this.getId = function(rec) {
8368                                 var r = g(rec);
8369                                 return (r === undefined || r === "") ? null : r;
8370                         };
8371                 } else {
8372                         this.getId = function(){return null;};
8373                 }
8374             this.ef = [];
8375             for(var jj = 0; jj < fl; jj++){
8376                 f = fi[jj];
8377                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8378                 this.ef[jj] = this.getJsonAccessor(map);
8379             }
8380         }
8381
8382         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8383         if(s.totalProperty){
8384             var vt = parseInt(this.getTotal(o), 10);
8385             if(!isNaN(vt)){
8386                 totalRecords = vt;
8387             }
8388         }
8389         if(s.successProperty){
8390             var vs = this.getSuccess(o);
8391             if(vs === false || vs === 'false'){
8392                 success = false;
8393             }
8394         }
8395         var records = [];
8396             for(var i = 0; i < c; i++){
8397                     var n = root[i];
8398                 var values = {};
8399                 var id = this.getId(n);
8400                 for(var j = 0; j < fl; j++){
8401                     f = fi[j];
8402                 var v = this.ef[j](n);
8403                 if (!f.convert) {
8404                     Roo.log('missing convert for ' + f.name);
8405                     Roo.log(f);
8406                     continue;
8407                 }
8408                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8409                 }
8410                 var record = new Record(values, id);
8411                 record.json = n;
8412                 records[i] = record;
8413             }
8414             return {
8415             raw : o,
8416                 success : success,
8417                 records : records,
8418                 totalRecords : totalRecords
8419             };
8420     }
8421 });/*
8422  * Based on:
8423  * Ext JS Library 1.1.1
8424  * Copyright(c) 2006-2007, Ext JS, LLC.
8425  *
8426  * Originally Released Under LGPL - original licence link has changed is not relivant.
8427  *
8428  * Fork - LGPL
8429  * <script type="text/javascript">
8430  */
8431
8432 /**
8433  * @class Roo.data.ArrayReader
8434  * @extends Roo.data.DataReader
8435  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8436  * Each element of that Array represents a row of data fields. The
8437  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8438  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8439  * <p>
8440  * Example code:.
8441  * <pre><code>
8442 var RecordDef = Roo.data.Record.create([
8443     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8444     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8445 ]);
8446 var myReader = new Roo.data.ArrayReader({
8447     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8448 }, RecordDef);
8449 </code></pre>
8450  * <p>
8451  * This would consume an Array like this:
8452  * <pre><code>
8453 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8454   </code></pre>
8455  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8456  * @constructor
8457  * Create a new JsonReader
8458  * @param {Object} meta Metadata configuration options.
8459  * @param {Object} recordType Either an Array of field definition objects
8460  * as specified to {@link Roo.data.Record#create},
8461  * or an {@link Roo.data.Record} object
8462  * created using {@link Roo.data.Record#create}.
8463  */
8464 Roo.data.ArrayReader = function(meta, recordType){
8465     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8466 };
8467
8468 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8469     /**
8470      * Create a data block containing Roo.data.Records from an XML document.
8471      * @param {Object} o An Array of row objects which represents the dataset.
8472      * @return {Object} data A data block which is used by an Roo.data.Store object as
8473      * a cache of Roo.data.Records.
8474      */
8475     readRecords : function(o){
8476         var sid = this.meta ? this.meta.id : null;
8477         var recordType = this.recordType, fields = recordType.prototype.fields;
8478         var records = [];
8479         var root = o;
8480             for(var i = 0; i < root.length; i++){
8481                     var n = root[i];
8482                 var values = {};
8483                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8484                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8485                 var f = fields.items[j];
8486                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8487                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8488                 v = f.convert(v);
8489                 values[f.name] = v;
8490             }
8491                 var record = new recordType(values, id);
8492                 record.json = n;
8493                 records[records.length] = record;
8494             }
8495             return {
8496                 records : records,
8497                 totalRecords : records.length
8498             };
8499     }
8500 });/*
8501  * - LGPL
8502  * * 
8503  */
8504
8505 /**
8506  * @class Roo.bootstrap.ComboBox
8507  * @extends Roo.bootstrap.TriggerField
8508  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8509  * @cfg {Boolean} append (true|false) default false
8510  * @constructor
8511  * Create a new ComboBox.
8512  * @param {Object} config Configuration options
8513  */
8514 Roo.bootstrap.ComboBox = function(config){
8515     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8516     this.addEvents({
8517         /**
8518          * @event expand
8519          * Fires when the dropdown list is expanded
8520              * @param {Roo.bootstrap.ComboBox} combo This combo box
8521              */
8522         'expand' : true,
8523         /**
8524          * @event collapse
8525          * Fires when the dropdown list is collapsed
8526              * @param {Roo.bootstrap.ComboBox} combo This combo box
8527              */
8528         'collapse' : true,
8529         /**
8530          * @event beforeselect
8531          * Fires before a list item is selected. Return false to cancel the selection.
8532              * @param {Roo.bootstrap.ComboBox} combo This combo box
8533              * @param {Roo.data.Record} record The data record returned from the underlying store
8534              * @param {Number} index The index of the selected item in the dropdown list
8535              */
8536         'beforeselect' : true,
8537         /**
8538          * @event select
8539          * Fires when a list item is selected
8540              * @param {Roo.bootstrap.ComboBox} combo This combo box
8541              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8542              * @param {Number} index The index of the selected item in the dropdown list
8543              */
8544         'select' : true,
8545         /**
8546          * @event beforequery
8547          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8548          * The event object passed has these properties:
8549              * @param {Roo.bootstrap.ComboBox} combo This combo box
8550              * @param {String} query The query
8551              * @param {Boolean} forceAll true to force "all" query
8552              * @param {Boolean} cancel true to cancel the query
8553              * @param {Object} e The query event object
8554              */
8555         'beforequery': true,
8556          /**
8557          * @event add
8558          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8559              * @param {Roo.bootstrap.ComboBox} combo This combo box
8560              */
8561         'add' : true,
8562         /**
8563          * @event edit
8564          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8565              * @param {Roo.bootstrap.ComboBox} combo This combo box
8566              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8567              */
8568         'edit' : true,
8569         /**
8570          * @event remove
8571          * Fires when the remove value from the combobox array
8572              * @param {Roo.bootstrap.ComboBox} combo This combo box
8573              */
8574         'remove' : true
8575         
8576     });
8577     
8578     
8579     this.selectedIndex = -1;
8580     if(this.mode == 'local'){
8581         if(config.queryDelay === undefined){
8582             this.queryDelay = 10;
8583         }
8584         if(config.minChars === undefined){
8585             this.minChars = 0;
8586         }
8587     }
8588 };
8589
8590 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8591      
8592     /**
8593      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8594      * rendering into an Roo.Editor, defaults to false)
8595      */
8596     /**
8597      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8598      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8599      */
8600     /**
8601      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8602      */
8603     /**
8604      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8605      * the dropdown list (defaults to undefined, with no header element)
8606      */
8607
8608      /**
8609      * @cfg {String/Roo.Template} tpl The template to use to render the output
8610      */
8611      
8612      /**
8613      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8614      */
8615     listWidth: undefined,
8616     /**
8617      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8618      * mode = 'remote' or 'text' if mode = 'local')
8619      */
8620     displayField: undefined,
8621     /**
8622      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8623      * mode = 'remote' or 'value' if mode = 'local'). 
8624      * Note: use of a valueField requires the user make a selection
8625      * in order for a value to be mapped.
8626      */
8627     valueField: undefined,
8628     
8629     
8630     /**
8631      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8632      * field's data value (defaults to the underlying DOM element's name)
8633      */
8634     hiddenName: undefined,
8635     /**
8636      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8637      */
8638     listClass: '',
8639     /**
8640      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8641      */
8642     selectedClass: 'active',
8643     
8644     /**
8645      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8646      */
8647     shadow:'sides',
8648     /**
8649      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8650      * anchor positions (defaults to 'tl-bl')
8651      */
8652     listAlign: 'tl-bl?',
8653     /**
8654      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8655      */
8656     maxHeight: 300,
8657     /**
8658      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
8659      * query specified by the allQuery config option (defaults to 'query')
8660      */
8661     triggerAction: 'query',
8662     /**
8663      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8664      * (defaults to 4, does not apply if editable = false)
8665      */
8666     minChars : 4,
8667     /**
8668      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8669      * delay (typeAheadDelay) if it matches a known value (defaults to false)
8670      */
8671     typeAhead: false,
8672     /**
8673      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8674      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8675      */
8676     queryDelay: 500,
8677     /**
8678      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8679      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
8680      */
8681     pageSize: 0,
8682     /**
8683      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
8684      * when editable = true (defaults to false)
8685      */
8686     selectOnFocus:false,
8687     /**
8688      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8689      */
8690     queryParam: 'query',
8691     /**
8692      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
8693      * when mode = 'remote' (defaults to 'Loading...')
8694      */
8695     loadingText: 'Loading...',
8696     /**
8697      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8698      */
8699     resizable: false,
8700     /**
8701      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8702      */
8703     handleHeight : 8,
8704     /**
8705      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8706      * traditional select (defaults to true)
8707      */
8708     editable: true,
8709     /**
8710      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8711      */
8712     allQuery: '',
8713     /**
8714      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8715      */
8716     mode: 'remote',
8717     /**
8718      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8719      * listWidth has a higher value)
8720      */
8721     minListWidth : 70,
8722     /**
8723      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8724      * allow the user to set arbitrary text into the field (defaults to false)
8725      */
8726     forceSelection:false,
8727     /**
8728      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8729      * if typeAhead = true (defaults to 250)
8730      */
8731     typeAheadDelay : 250,
8732     /**
8733      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8734      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8735      */
8736     valueNotFoundText : undefined,
8737     /**
8738      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8739      */
8740     blockFocus : false,
8741     
8742     /**
8743      * @cfg {Boolean} disableClear Disable showing of clear button.
8744      */
8745     disableClear : false,
8746     /**
8747      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8748      */
8749     alwaysQuery : false,
8750     
8751     /**
8752      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8753      */
8754     multiple : false,
8755     
8756     //private
8757     addicon : false,
8758     editicon: false,
8759     
8760     page: 0,
8761     hasQuery: false,
8762     append: false,
8763     loadNext: false,
8764     item: [],
8765     
8766     // element that contains real text value.. (when hidden is used..)
8767      
8768     // private
8769     initEvents: function(){
8770         
8771         if (!this.store) {
8772             throw "can not find store for combo";
8773         }
8774         this.store = Roo.factory(this.store, Roo.data);
8775         
8776         
8777         
8778         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8779         
8780         
8781         if(this.hiddenName){
8782             
8783             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8784             
8785             this.hiddenField.dom.value =
8786                 this.hiddenValue !== undefined ? this.hiddenValue :
8787                 this.value !== undefined ? this.value : '';
8788
8789             // prevent input submission
8790             this.el.dom.removeAttribute('name');
8791             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8792              
8793              
8794         }
8795         //if(Roo.isGecko){
8796         //    this.el.dom.setAttribute('autocomplete', 'off');
8797         //}
8798
8799         var cls = 'x-combo-list';
8800         this.list = this.el.select('ul.dropdown-menu',true).first();
8801
8802         //this.list = new Roo.Layer({
8803         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8804         //});
8805         
8806         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8807         this.list.setWidth(lw);
8808         
8809         this.list.on('mouseover', this.onViewOver, this);
8810         this.list.on('mousemove', this.onViewMove, this);
8811         
8812         this.list.on('scroll', this.onViewScroll, this);
8813         
8814         /*
8815         this.list.swallowEvent('mousewheel');
8816         this.assetHeight = 0;
8817
8818         if(this.title){
8819             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8820             this.assetHeight += this.header.getHeight();
8821         }
8822
8823         this.innerList = this.list.createChild({cls:cls+'-inner'});
8824         this.innerList.on('mouseover', this.onViewOver, this);
8825         this.innerList.on('mousemove', this.onViewMove, this);
8826         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8827         
8828         if(this.allowBlank && !this.pageSize && !this.disableClear){
8829             this.footer = this.list.createChild({cls:cls+'-ft'});
8830             this.pageTb = new Roo.Toolbar(this.footer);
8831            
8832         }
8833         if(this.pageSize){
8834             this.footer = this.list.createChild({cls:cls+'-ft'});
8835             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8836                     {pageSize: this.pageSize});
8837             
8838         }
8839         
8840         if (this.pageTb && this.allowBlank && !this.disableClear) {
8841             var _this = this;
8842             this.pageTb.add(new Roo.Toolbar.Fill(), {
8843                 cls: 'x-btn-icon x-btn-clear',
8844                 text: '&#160;',
8845                 handler: function()
8846                 {
8847                     _this.collapse();
8848                     _this.clearValue();
8849                     _this.onSelect(false, -1);
8850                 }
8851             });
8852         }
8853         if (this.footer) {
8854             this.assetHeight += this.footer.getHeight();
8855         }
8856         */
8857             
8858         if(!this.tpl){
8859             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8860         }
8861
8862         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8863             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8864         });
8865         //this.view.wrapEl.setDisplayed(false);
8866         this.view.on('click', this.onViewClick, this);
8867         
8868         
8869         
8870         this.store.on('beforeload', this.onBeforeLoad, this);
8871         this.store.on('load', this.onLoad, this);
8872         this.store.on('loadexception', this.onLoadException, this);
8873         /*
8874         if(this.resizable){
8875             this.resizer = new Roo.Resizable(this.list,  {
8876                pinned:true, handles:'se'
8877             });
8878             this.resizer.on('resize', function(r, w, h){
8879                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8880                 this.listWidth = w;
8881                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8882                 this.restrictHeight();
8883             }, this);
8884             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8885         }
8886         */
8887         if(!this.editable){
8888             this.editable = true;
8889             this.setEditable(false);
8890         }
8891         
8892         /*
8893         
8894         if (typeof(this.events.add.listeners) != 'undefined') {
8895             
8896             this.addicon = this.wrap.createChild(
8897                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8898        
8899             this.addicon.on('click', function(e) {
8900                 this.fireEvent('add', this);
8901             }, this);
8902         }
8903         if (typeof(this.events.edit.listeners) != 'undefined') {
8904             
8905             this.editicon = this.wrap.createChild(
8906                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8907             if (this.addicon) {
8908                 this.editicon.setStyle('margin-left', '40px');
8909             }
8910             this.editicon.on('click', function(e) {
8911                 
8912                 // we fire even  if inothing is selected..
8913                 this.fireEvent('edit', this, this.lastData );
8914                 
8915             }, this);
8916         }
8917         */
8918         
8919         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8920             "up" : function(e){
8921                 this.inKeyMode = true;
8922                 this.selectPrev();
8923             },
8924
8925             "down" : function(e){
8926                 if(!this.isExpanded()){
8927                     this.onTriggerClick();
8928                 }else{
8929                     this.inKeyMode = true;
8930                     this.selectNext();
8931                 }
8932             },
8933
8934             "enter" : function(e){
8935                 this.onViewClick();
8936                 //return true;
8937             },
8938
8939             "esc" : function(e){
8940                 this.collapse();
8941             },
8942
8943             "tab" : function(e){
8944                 this.collapse();
8945                 
8946                 if(this.fireEvent("specialkey", this, e)){
8947                     this.onViewClick(false);
8948                 }
8949                 
8950                 return true;
8951             },
8952
8953             scope : this,
8954
8955             doRelay : function(foo, bar, hname){
8956                 if(hname == 'down' || this.scope.isExpanded()){
8957                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8958                 }
8959                 return true;
8960             },
8961
8962             forceKeyDown: true
8963         });
8964         
8965         
8966         this.queryDelay = Math.max(this.queryDelay || 10,
8967                 this.mode == 'local' ? 10 : 250);
8968         
8969         
8970         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8971         
8972         if(this.typeAhead){
8973             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8974         }
8975         if(this.editable !== false){
8976             this.inputEl().on("keyup", this.onKeyUp, this);
8977         }
8978         if(this.forceSelection){
8979             this.on('blur', this.doForce, this);
8980         }
8981         
8982         if(this.multiple){
8983             this.choices = this.el.select('ul.select2-choices', true).first();
8984             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8985         }
8986     },
8987
8988     onDestroy : function(){
8989         if(this.view){
8990             this.view.setStore(null);
8991             this.view.el.removeAllListeners();
8992             this.view.el.remove();
8993             this.view.purgeListeners();
8994         }
8995         if(this.list){
8996             this.list.dom.innerHTML  = '';
8997         }
8998         if(this.store){
8999             this.store.un('beforeload', this.onBeforeLoad, this);
9000             this.store.un('load', this.onLoad, this);
9001             this.store.un('loadexception', this.onLoadException, this);
9002         }
9003         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9004     },
9005
9006     // private
9007     fireKey : function(e){
9008         if(e.isNavKeyPress() && !this.list.isVisible()){
9009             this.fireEvent("specialkey", this, e);
9010         }
9011     },
9012
9013     // private
9014     onResize: function(w, h){
9015 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9016 //        
9017 //        if(typeof w != 'number'){
9018 //            // we do not handle it!?!?
9019 //            return;
9020 //        }
9021 //        var tw = this.trigger.getWidth();
9022 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9023 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9024 //        var x = w - tw;
9025 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9026 //            
9027 //        //this.trigger.setStyle('left', x+'px');
9028 //        
9029 //        if(this.list && this.listWidth === undefined){
9030 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9031 //            this.list.setWidth(lw);
9032 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9033 //        }
9034         
9035     
9036         
9037     },
9038
9039     /**
9040      * Allow or prevent the user from directly editing the field text.  If false is passed,
9041      * the user will only be able to select from the items defined in the dropdown list.  This method
9042      * is the runtime equivalent of setting the 'editable' config option at config time.
9043      * @param {Boolean} value True to allow the user to directly edit the field text
9044      */
9045     setEditable : function(value){
9046         if(value == this.editable){
9047             return;
9048         }
9049         this.editable = value;
9050         if(!value){
9051             this.inputEl().dom.setAttribute('readOnly', true);
9052             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9053             this.inputEl().addClass('x-combo-noedit');
9054         }else{
9055             this.inputEl().dom.setAttribute('readOnly', false);
9056             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9057             this.inputEl().removeClass('x-combo-noedit');
9058         }
9059     },
9060
9061     // private
9062     
9063     onBeforeLoad : function(combo,opts){
9064         if(!this.hasFocus){
9065             return;
9066         }
9067          if (!opts.add) {
9068             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9069          }
9070         this.restrictHeight();
9071         this.selectedIndex = -1;
9072     },
9073
9074     // private
9075     onLoad : function(){
9076         
9077         this.hasQuery = false;
9078         
9079         if(!this.hasFocus){
9080             return;
9081         }
9082         
9083         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9084             this.loading.hide();
9085         }
9086         
9087         if(this.store.getCount() > 0){
9088             this.expand();
9089             this.restrictHeight();
9090             if(this.lastQuery == this.allQuery){
9091                 if(this.editable){
9092                     this.inputEl().dom.select();
9093                 }
9094                 if(!this.selectByValue(this.value, true)){
9095                     this.select(0, true);
9096                 }
9097             }else{
9098                 this.selectNext();
9099                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9100                     this.taTask.delay(this.typeAheadDelay);
9101                 }
9102             }
9103         }else{
9104             this.onEmptyResults();
9105         }
9106         
9107         //this.el.focus();
9108     },
9109     // private
9110     onLoadException : function()
9111     {
9112         this.hasQuery = false;
9113         
9114         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9115             this.loading.hide();
9116         }
9117         
9118         this.collapse();
9119         Roo.log(this.store.reader.jsonData);
9120         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9121             // fixme
9122             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9123         }
9124         
9125         
9126     },
9127     // private
9128     onTypeAhead : function(){
9129         if(this.store.getCount() > 0){
9130             var r = this.store.getAt(0);
9131             var newValue = r.data[this.displayField];
9132             var len = newValue.length;
9133             var selStart = this.getRawValue().length;
9134             
9135             if(selStart != len){
9136                 this.setRawValue(newValue);
9137                 this.selectText(selStart, newValue.length);
9138             }
9139         }
9140     },
9141
9142     // private
9143     onSelect : function(record, index){
9144         
9145         if(this.fireEvent('beforeselect', this, record, index) !== false){
9146         
9147             this.setFromData(index > -1 ? record.data : false);
9148             
9149             this.collapse();
9150             this.fireEvent('select', this, record, index);
9151         }
9152     },
9153
9154     /**
9155      * Returns the currently selected field value or empty string if no value is set.
9156      * @return {String} value The selected value
9157      */
9158     getValue : function(){
9159         
9160         if(this.multiple){
9161             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9162         }
9163         
9164         if(this.valueField){
9165             return typeof this.value != 'undefined' ? this.value : '';
9166         }else{
9167             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9168         }
9169     },
9170
9171     /**
9172      * Clears any text/value currently set in the field
9173      */
9174     clearValue : function(){
9175         if(this.hiddenField){
9176             this.hiddenField.dom.value = '';
9177         }
9178         this.value = '';
9179         this.setRawValue('');
9180         this.lastSelectionText = '';
9181         
9182     },
9183
9184     /**
9185      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9186      * will be displayed in the field.  If the value does not match the data value of an existing item,
9187      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9188      * Otherwise the field will be blank (although the value will still be set).
9189      * @param {String} value The value to match
9190      */
9191     setValue : function(v){
9192         if(this.multiple){
9193             this.syncValue();
9194             return;
9195         }
9196         
9197         var text = v;
9198         if(this.valueField){
9199             var r = this.findRecord(this.valueField, v);
9200             if(r){
9201                 text = r.data[this.displayField];
9202             }else if(this.valueNotFoundText !== undefined){
9203                 text = this.valueNotFoundText;
9204             }
9205         }
9206         this.lastSelectionText = text;
9207         if(this.hiddenField){
9208             this.hiddenField.dom.value = v;
9209         }
9210         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9211         this.value = v;
9212     },
9213     /**
9214      * @property {Object} the last set data for the element
9215      */
9216     
9217     lastData : false,
9218     /**
9219      * Sets the value of the field based on a object which is related to the record format for the store.
9220      * @param {Object} value the value to set as. or false on reset?
9221      */
9222     setFromData : function(o){
9223         
9224         if(this.multiple){
9225             this.addItem(o);
9226             return;
9227         }
9228             
9229         var dv = ''; // display value
9230         var vv = ''; // value value..
9231         this.lastData = o;
9232         if (this.displayField) {
9233             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9234         } else {
9235             // this is an error condition!!!
9236             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9237         }
9238         
9239         if(this.valueField){
9240             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9241         }
9242         
9243         if(this.hiddenField){
9244             this.hiddenField.dom.value = vv;
9245             
9246             this.lastSelectionText = dv;
9247             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9248             this.value = vv;
9249             return;
9250         }
9251         // no hidden field.. - we store the value in 'value', but still display
9252         // display field!!!!
9253         this.lastSelectionText = dv;
9254         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9255         this.value = vv;
9256         
9257         
9258     },
9259     // private
9260     reset : function(){
9261         // overridden so that last data is reset..
9262         this.setValue(this.originalValue);
9263         this.clearInvalid();
9264         this.lastData = false;
9265         if (this.view) {
9266             this.view.clearSelections();
9267         }
9268     },
9269     // private
9270     findRecord : function(prop, value){
9271         var record;
9272         if(this.store.getCount() > 0){
9273             this.store.each(function(r){
9274                 if(r.data[prop] == value){
9275                     record = r;
9276                     return false;
9277                 }
9278                 return true;
9279             });
9280         }
9281         return record;
9282     },
9283     
9284     getName: function()
9285     {
9286         // returns hidden if it's set..
9287         if (!this.rendered) {return ''};
9288         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9289         
9290     },
9291     // private
9292     onViewMove : function(e, t){
9293         this.inKeyMode = false;
9294     },
9295
9296     // private
9297     onViewOver : function(e, t){
9298         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9299             return;
9300         }
9301         var item = this.view.findItemFromChild(t);
9302         if(item){
9303             var index = this.view.indexOf(item);
9304             this.select(index, false);
9305         }
9306     },
9307
9308     // private
9309     onViewClick : function(doFocus)
9310     {
9311         var index = this.view.getSelectedIndexes()[0];
9312         var r = this.store.getAt(index);
9313         if(r){
9314             this.onSelect(r, index);
9315         }
9316         if(doFocus !== false && !this.blockFocus){
9317             this.inputEl().focus();
9318         }
9319     },
9320
9321     // private
9322     restrictHeight : function(){
9323         //this.innerList.dom.style.height = '';
9324         //var inner = this.innerList.dom;
9325         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9326         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9327         //this.list.beginUpdate();
9328         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9329         this.list.alignTo(this.inputEl(), this.listAlign);
9330         //this.list.endUpdate();
9331     },
9332
9333     // private
9334     onEmptyResults : function(){
9335         this.collapse();
9336     },
9337
9338     /**
9339      * Returns true if the dropdown list is expanded, else false.
9340      */
9341     isExpanded : function(){
9342         return this.list.isVisible();
9343     },
9344
9345     /**
9346      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9347      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9348      * @param {String} value The data value of the item to select
9349      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9350      * selected item if it is not currently in view (defaults to true)
9351      * @return {Boolean} True if the value matched an item in the list, else false
9352      */
9353     selectByValue : function(v, scrollIntoView){
9354         if(v !== undefined && v !== null){
9355             var r = this.findRecord(this.valueField || this.displayField, v);
9356             if(r){
9357                 this.select(this.store.indexOf(r), scrollIntoView);
9358                 return true;
9359             }
9360         }
9361         return false;
9362     },
9363
9364     /**
9365      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9366      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9367      * @param {Number} index The zero-based index of the list item to select
9368      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9369      * selected item if it is not currently in view (defaults to true)
9370      */
9371     select : function(index, scrollIntoView){
9372         this.selectedIndex = index;
9373         this.view.select(index);
9374         if(scrollIntoView !== false){
9375             var el = this.view.getNode(index);
9376             if(el){
9377                 //this.innerList.scrollChildIntoView(el, false);
9378                 
9379             }
9380         }
9381     },
9382
9383     // private
9384     selectNext : function(){
9385         var ct = this.store.getCount();
9386         if(ct > 0){
9387             if(this.selectedIndex == -1){
9388                 this.select(0);
9389             }else if(this.selectedIndex < ct-1){
9390                 this.select(this.selectedIndex+1);
9391             }
9392         }
9393     },
9394
9395     // private
9396     selectPrev : function(){
9397         var ct = this.store.getCount();
9398         if(ct > 0){
9399             if(this.selectedIndex == -1){
9400                 this.select(0);
9401             }else if(this.selectedIndex != 0){
9402                 this.select(this.selectedIndex-1);
9403             }
9404         }
9405     },
9406
9407     // private
9408     onKeyUp : function(e){
9409         if(this.editable !== false && !e.isSpecialKey()){
9410             this.lastKey = e.getKey();
9411             this.dqTask.delay(this.queryDelay);
9412         }
9413     },
9414
9415     // private
9416     validateBlur : function(){
9417         return !this.list || !this.list.isVisible();   
9418     },
9419
9420     // private
9421     initQuery : function(){
9422         this.doQuery(this.getRawValue());
9423     },
9424
9425     // private
9426     doForce : function(){
9427         if(this.el.dom.value.length > 0){
9428             this.el.dom.value =
9429                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9430              
9431         }
9432     },
9433
9434     /**
9435      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9436      * query allowing the query action to be canceled if needed.
9437      * @param {String} query The SQL query to execute
9438      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9439      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9440      * saved in the current store (defaults to false)
9441      */
9442     doQuery : function(q, forceAll){
9443         
9444         if(q === undefined || q === null){
9445             q = '';
9446         }
9447         var qe = {
9448             query: q,
9449             forceAll: forceAll,
9450             combo: this,
9451             cancel:false
9452         };
9453         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9454             return false;
9455         }
9456         q = qe.query;
9457         
9458         forceAll = qe.forceAll;
9459         if(forceAll === true || (q.length >= this.minChars)){
9460             
9461             this.hasQuery = true;
9462             
9463             if(this.lastQuery != q || this.alwaysQuery){
9464                 this.lastQuery = q;
9465                 if(this.mode == 'local'){
9466                     this.selectedIndex = -1;
9467                     if(forceAll){
9468                         this.store.clearFilter();
9469                     }else{
9470                         this.store.filter(this.displayField, q);
9471                     }
9472                     this.onLoad();
9473                 }else{
9474                     this.store.baseParams[this.queryParam] = q;
9475                     
9476                     var options = {params : this.getParams(q)};
9477                     
9478                     if(this.loadNext){
9479                         options.add = true;
9480                         options.params.start = this.page * this.pageSize;
9481                     }
9482                     
9483                     this.store.load(options);
9484                     this.expand();
9485                 }
9486             }else{
9487                 this.selectedIndex = -1;
9488                 this.onLoad();   
9489             }
9490         }
9491         
9492         this.loadNext = false;
9493     },
9494
9495     // private
9496     getParams : function(q){
9497         var p = {};
9498         //p[this.queryParam] = q;
9499         
9500         if(this.pageSize){
9501             p.start = 0;
9502             p.limit = this.pageSize;
9503         }
9504         return p;
9505     },
9506
9507     /**
9508      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9509      */
9510     collapse : function(){
9511         if(!this.isExpanded()){
9512             return;
9513         }
9514         
9515         this.list.hide();
9516         Roo.get(document).un('mousedown', this.collapseIf, this);
9517         Roo.get(document).un('mousewheel', this.collapseIf, this);
9518         if (!this.editable) {
9519             Roo.get(document).un('keydown', this.listKeyPress, this);
9520         }
9521         this.fireEvent('collapse', this);
9522     },
9523
9524     // private
9525     collapseIf : function(e){
9526         var in_combo  = e.within(this.el);
9527         var in_list =  e.within(this.list);
9528         
9529         if (in_combo || in_list) {
9530             //e.stopPropagation();
9531             return;
9532         }
9533
9534         this.collapse();
9535         
9536     },
9537
9538     /**
9539      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9540      */
9541     expand : function(){
9542        
9543         if(this.isExpanded() || !this.hasFocus){
9544             return;
9545         }
9546          Roo.log('expand');
9547         this.list.alignTo(this.inputEl(), this.listAlign);
9548         this.list.show();
9549         Roo.get(document).on('mousedown', this.collapseIf, this);
9550         Roo.get(document).on('mousewheel', this.collapseIf, this);
9551         if (!this.editable) {
9552             Roo.get(document).on('keydown', this.listKeyPress, this);
9553         }
9554         
9555         this.fireEvent('expand', this);
9556     },
9557
9558     // private
9559     // Implements the default empty TriggerField.onTriggerClick function
9560     onTriggerClick : function()
9561     {
9562         Roo.log('trigger click');
9563         
9564         if(this.disabled){
9565             return;
9566         }
9567         
9568         this.page = 0;
9569         this.loadNext = false;
9570         
9571         if(this.isExpanded()){
9572             this.collapse();
9573             if (!this.blockFocus) {
9574                 this.inputEl().focus();
9575             }
9576             
9577         }else {
9578             this.hasFocus = true;
9579             if(this.triggerAction == 'all') {
9580                 this.doQuery(this.allQuery, true);
9581             } else {
9582                 this.doQuery(this.getRawValue());
9583             }
9584             if (!this.blockFocus) {
9585                 this.inputEl().focus();
9586             }
9587         }
9588     },
9589     listKeyPress : function(e)
9590     {
9591         //Roo.log('listkeypress');
9592         // scroll to first matching element based on key pres..
9593         if (e.isSpecialKey()) {
9594             return false;
9595         }
9596         var k = String.fromCharCode(e.getKey()).toUpperCase();
9597         //Roo.log(k);
9598         var match  = false;
9599         var csel = this.view.getSelectedNodes();
9600         var cselitem = false;
9601         if (csel.length) {
9602             var ix = this.view.indexOf(csel[0]);
9603             cselitem  = this.store.getAt(ix);
9604             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9605                 cselitem = false;
9606             }
9607             
9608         }
9609         
9610         this.store.each(function(v) { 
9611             if (cselitem) {
9612                 // start at existing selection.
9613                 if (cselitem.id == v.id) {
9614                     cselitem = false;
9615                 }
9616                 return true;
9617             }
9618                 
9619             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9620                 match = this.store.indexOf(v);
9621                 return false;
9622             }
9623             return true;
9624         }, this);
9625         
9626         if (match === false) {
9627             return true; // no more action?
9628         }
9629         // scroll to?
9630         this.view.select(match);
9631         var sn = Roo.get(this.view.getSelectedNodes()[0])
9632         //sn.scrollIntoView(sn.dom.parentNode, false);
9633     },
9634     
9635     onViewScroll : function(e, t){
9636         
9637         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9638             return;
9639         }
9640         
9641         this.hasQuery = true;
9642         
9643         this.loading = this.list.select('.loading', true).first();
9644         
9645         if(this.loading === null){
9646             this.list.createChild({
9647                 tag: 'div',
9648                 cls: 'loading select2-more-results select2-active',
9649                 html: 'Loading more results...'
9650             })
9651             
9652             this.loading = this.list.select('.loading', true).first();
9653             
9654             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9655             
9656             this.loading.hide();
9657         }
9658         
9659         this.loading.show();
9660         
9661         var _combo = this;
9662         
9663         this.page++;
9664         this.loadNext = true;
9665         
9666         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9667         
9668         return;
9669     },
9670     
9671     addItem : function(o)
9672     {   
9673         var dv = ''; // display value
9674         
9675         if (this.displayField) {
9676             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9677         } else {
9678             // this is an error condition!!!
9679             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9680         }
9681         
9682         if(!dv.length){
9683             return;
9684         }
9685         
9686         var choice = this.choices.createChild({
9687             tag: 'li',
9688             cls: 'select2-search-choice',
9689             cn: [
9690                 {
9691                     tag: 'div',
9692                     html: dv
9693                 },
9694                 {
9695                     tag: 'a',
9696                     href: '#',
9697                     cls: 'select2-search-choice-close',
9698                     tabindex: '-1'
9699                 }
9700             ]
9701             
9702         }, this.searchField);
9703         
9704         var close = choice.select('a.select2-search-choice-close', true).first()
9705         
9706         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9707         
9708         this.item.push(o);
9709         this.lastData = o;
9710         
9711         this.syncValue();
9712         
9713         this.inputEl().dom.value = '';
9714         
9715     },
9716     
9717     onRemoveItem : function(e, _self, o)
9718     {
9719         Roo.log('remove item');
9720         var index = this.item.indexOf(o.data) * 1;
9721         
9722         if( index < 0){
9723             Roo.log('not this item?!');
9724             return;
9725         }
9726         
9727         this.item.splice(index, 1);
9728         o.item.remove();
9729         
9730         this.syncValue();
9731         
9732         this.fireEvent('remove', this);
9733         
9734     },
9735     
9736     syncValue : function()
9737     {
9738         if(!this.item.length){
9739             this.clearValue();
9740             return;
9741         }
9742             
9743         var value = [];
9744         var _this = this;
9745         Roo.each(this.item, function(i){
9746             if(_this.valueField){
9747                 value.push(i[_this.valueField]);
9748                 return;
9749             }
9750
9751             value.push(i);
9752         });
9753
9754         this.value = value.join(',');
9755
9756         if(this.hiddenField){
9757             this.hiddenField.dom.value = this.value;
9758         }
9759     },
9760     
9761     clearItem : function()
9762     {
9763         if(!this.multiple){
9764             return;
9765         }
9766         
9767         this.item = [];
9768         
9769         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9770            c.remove();
9771         });
9772         
9773         this.syncValue();
9774     }
9775     
9776     
9777
9778     /** 
9779     * @cfg {Boolean} grow 
9780     * @hide 
9781     */
9782     /** 
9783     * @cfg {Number} growMin 
9784     * @hide 
9785     */
9786     /** 
9787     * @cfg {Number} growMax 
9788     * @hide 
9789     */
9790     /**
9791      * @hide
9792      * @method autoSize
9793      */
9794 });
9795 /*
9796  * Based on:
9797  * Ext JS Library 1.1.1
9798  * Copyright(c) 2006-2007, Ext JS, LLC.
9799  *
9800  * Originally Released Under LGPL - original licence link has changed is not relivant.
9801  *
9802  * Fork - LGPL
9803  * <script type="text/javascript">
9804  */
9805
9806 /**
9807  * @class Roo.View
9808  * @extends Roo.util.Observable
9809  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9810  * This class also supports single and multi selection modes. <br>
9811  * Create a data model bound view:
9812  <pre><code>
9813  var store = new Roo.data.Store(...);
9814
9815  var view = new Roo.View({
9816     el : "my-element",
9817     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9818  
9819     singleSelect: true,
9820     selectedClass: "ydataview-selected",
9821     store: store
9822  });
9823
9824  // listen for node click?
9825  view.on("click", function(vw, index, node, e){
9826  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9827  });
9828
9829  // load XML data
9830  dataModel.load("foobar.xml");
9831  </code></pre>
9832  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9833  * <br><br>
9834  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9835  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9836  * 
9837  * Note: old style constructor is still suported (container, template, config)
9838  * 
9839  * @constructor
9840  * Create a new View
9841  * @param {Object} config The config object
9842  * 
9843  */
9844 Roo.View = function(config, depreciated_tpl, depreciated_config){
9845     
9846     if (typeof(depreciated_tpl) == 'undefined') {
9847         // new way.. - universal constructor.
9848         Roo.apply(this, config);
9849         this.el  = Roo.get(this.el);
9850     } else {
9851         // old format..
9852         this.el  = Roo.get(config);
9853         this.tpl = depreciated_tpl;
9854         Roo.apply(this, depreciated_config);
9855     }
9856     this.wrapEl  = this.el.wrap().wrap();
9857     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9858     
9859     
9860     if(typeof(this.tpl) == "string"){
9861         this.tpl = new Roo.Template(this.tpl);
9862     } else {
9863         // support xtype ctors..
9864         this.tpl = new Roo.factory(this.tpl, Roo);
9865     }
9866     
9867     
9868     this.tpl.compile();
9869    
9870   
9871     
9872      
9873     /** @private */
9874     this.addEvents({
9875         /**
9876          * @event beforeclick
9877          * Fires before a click is processed. Returns false to cancel the default action.
9878          * @param {Roo.View} this
9879          * @param {Number} index The index of the target node
9880          * @param {HTMLElement} node The target node
9881          * @param {Roo.EventObject} e The raw event object
9882          */
9883             "beforeclick" : true,
9884         /**
9885          * @event click
9886          * Fires when a template node is clicked.
9887          * @param {Roo.View} this
9888          * @param {Number} index The index of the target node
9889          * @param {HTMLElement} node The target node
9890          * @param {Roo.EventObject} e The raw event object
9891          */
9892             "click" : true,
9893         /**
9894          * @event dblclick
9895          * Fires when a template node is double clicked.
9896          * @param {Roo.View} this
9897          * @param {Number} index The index of the target node
9898          * @param {HTMLElement} node The target node
9899          * @param {Roo.EventObject} e The raw event object
9900          */
9901             "dblclick" : true,
9902         /**
9903          * @event contextmenu
9904          * Fires when a template node is right clicked.
9905          * @param {Roo.View} this
9906          * @param {Number} index The index of the target node
9907          * @param {HTMLElement} node The target node
9908          * @param {Roo.EventObject} e The raw event object
9909          */
9910             "contextmenu" : true,
9911         /**
9912          * @event selectionchange
9913          * Fires when the selected nodes change.
9914          * @param {Roo.View} this
9915          * @param {Array} selections Array of the selected nodes
9916          */
9917             "selectionchange" : true,
9918     
9919         /**
9920          * @event beforeselect
9921          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9922          * @param {Roo.View} this
9923          * @param {HTMLElement} node The node to be selected
9924          * @param {Array} selections Array of currently selected nodes
9925          */
9926             "beforeselect" : true,
9927         /**
9928          * @event preparedata
9929          * Fires on every row to render, to allow you to change the data.
9930          * @param {Roo.View} this
9931          * @param {Object} data to be rendered (change this)
9932          */
9933           "preparedata" : true
9934           
9935           
9936         });
9937
9938
9939
9940     this.el.on({
9941         "click": this.onClick,
9942         "dblclick": this.onDblClick,
9943         "contextmenu": this.onContextMenu,
9944         scope:this
9945     });
9946
9947     this.selections = [];
9948     this.nodes = [];
9949     this.cmp = new Roo.CompositeElementLite([]);
9950     if(this.store){
9951         this.store = Roo.factory(this.store, Roo.data);
9952         this.setStore(this.store, true);
9953     }
9954     
9955     if ( this.footer && this.footer.xtype) {
9956            
9957          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9958         
9959         this.footer.dataSource = this.store
9960         this.footer.container = fctr;
9961         this.footer = Roo.factory(this.footer, Roo);
9962         fctr.insertFirst(this.el);
9963         
9964         // this is a bit insane - as the paging toolbar seems to detach the el..
9965 //        dom.parentNode.parentNode.parentNode
9966          // they get detached?
9967     }
9968     
9969     
9970     Roo.View.superclass.constructor.call(this);
9971     
9972     
9973 };
9974
9975 Roo.extend(Roo.View, Roo.util.Observable, {
9976     
9977      /**
9978      * @cfg {Roo.data.Store} store Data store to load data from.
9979      */
9980     store : false,
9981     
9982     /**
9983      * @cfg {String|Roo.Element} el The container element.
9984      */
9985     el : '',
9986     
9987     /**
9988      * @cfg {String|Roo.Template} tpl The template used by this View 
9989      */
9990     tpl : false,
9991     /**
9992      * @cfg {String} dataName the named area of the template to use as the data area
9993      *                          Works with domtemplates roo-name="name"
9994      */
9995     dataName: false,
9996     /**
9997      * @cfg {String} selectedClass The css class to add to selected nodes
9998      */
9999     selectedClass : "x-view-selected",
10000      /**
10001      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10002      */
10003     emptyText : "",
10004     
10005     /**
10006      * @cfg {String} text to display on mask (default Loading)
10007      */
10008     mask : false,
10009     /**
10010      * @cfg {Boolean} multiSelect Allow multiple selection
10011      */
10012     multiSelect : false,
10013     /**
10014      * @cfg {Boolean} singleSelect Allow single selection
10015      */
10016     singleSelect:  false,
10017     
10018     /**
10019      * @cfg {Boolean} toggleSelect - selecting 
10020      */
10021     toggleSelect : false,
10022     
10023     /**
10024      * Returns the element this view is bound to.
10025      * @return {Roo.Element}
10026      */
10027     getEl : function(){
10028         return this.wrapEl;
10029     },
10030     
10031     
10032
10033     /**
10034      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10035      */
10036     refresh : function(){
10037         Roo.log('refresh');
10038         var t = this.tpl;
10039         
10040         // if we are using something like 'domtemplate', then
10041         // the what gets used is:
10042         // t.applySubtemplate(NAME, data, wrapping data..)
10043         // the outer template then get' applied with
10044         //     the store 'extra data'
10045         // and the body get's added to the
10046         //      roo-name="data" node?
10047         //      <span class='roo-tpl-{name}'></span> ?????
10048         
10049         
10050         
10051         this.clearSelections();
10052         this.el.update("");
10053         var html = [];
10054         var records = this.store.getRange();
10055         if(records.length < 1) {
10056             
10057             // is this valid??  = should it render a template??
10058             
10059             this.el.update(this.emptyText);
10060             return;
10061         }
10062         var el = this.el;
10063         if (this.dataName) {
10064             this.el.update(t.apply(this.store.meta)); //????
10065             el = this.el.child('.roo-tpl-' + this.dataName);
10066         }
10067         
10068         for(var i = 0, len = records.length; i < len; i++){
10069             var data = this.prepareData(records[i].data, i, records[i]);
10070             this.fireEvent("preparedata", this, data, i, records[i]);
10071             html[html.length] = Roo.util.Format.trim(
10072                 this.dataName ?
10073                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10074                     t.apply(data)
10075             );
10076         }
10077         
10078         
10079         
10080         el.update(html.join(""));
10081         this.nodes = el.dom.childNodes;
10082         this.updateIndexes(0);
10083     },
10084     
10085
10086     /**
10087      * Function to override to reformat the data that is sent to
10088      * the template for each node.
10089      * DEPRICATED - use the preparedata event handler.
10090      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10091      * a JSON object for an UpdateManager bound view).
10092      */
10093     prepareData : function(data, index, record)
10094     {
10095         this.fireEvent("preparedata", this, data, index, record);
10096         return data;
10097     },
10098
10099     onUpdate : function(ds, record){
10100          Roo.log('on update');   
10101         this.clearSelections();
10102         var index = this.store.indexOf(record);
10103         var n = this.nodes[index];
10104         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10105         n.parentNode.removeChild(n);
10106         this.updateIndexes(index, index);
10107     },
10108
10109     
10110     
10111 // --------- FIXME     
10112     onAdd : function(ds, records, index)
10113     {
10114         Roo.log(['on Add', ds, records, index] );        
10115         this.clearSelections();
10116         if(this.nodes.length == 0){
10117             this.refresh();
10118             return;
10119         }
10120         var n = this.nodes[index];
10121         for(var i = 0, len = records.length; i < len; i++){
10122             var d = this.prepareData(records[i].data, i, records[i]);
10123             if(n){
10124                 this.tpl.insertBefore(n, d);
10125             }else{
10126                 
10127                 this.tpl.append(this.el, d);
10128             }
10129         }
10130         this.updateIndexes(index);
10131     },
10132
10133     onRemove : function(ds, record, index){
10134         Roo.log('onRemove');
10135         this.clearSelections();
10136         var el = this.dataName  ?
10137             this.el.child('.roo-tpl-' + this.dataName) :
10138             this.el; 
10139         
10140         el.dom.removeChild(this.nodes[index]);
10141         this.updateIndexes(index);
10142     },
10143
10144     /**
10145      * Refresh an individual node.
10146      * @param {Number} index
10147      */
10148     refreshNode : function(index){
10149         this.onUpdate(this.store, this.store.getAt(index));
10150     },
10151
10152     updateIndexes : function(startIndex, endIndex){
10153         var ns = this.nodes;
10154         startIndex = startIndex || 0;
10155         endIndex = endIndex || ns.length - 1;
10156         for(var i = startIndex; i <= endIndex; i++){
10157             ns[i].nodeIndex = i;
10158         }
10159     },
10160
10161     /**
10162      * Changes the data store this view uses and refresh the view.
10163      * @param {Store} store
10164      */
10165     setStore : function(store, initial){
10166         if(!initial && this.store){
10167             this.store.un("datachanged", this.refresh);
10168             this.store.un("add", this.onAdd);
10169             this.store.un("remove", this.onRemove);
10170             this.store.un("update", this.onUpdate);
10171             this.store.un("clear", this.refresh);
10172             this.store.un("beforeload", this.onBeforeLoad);
10173             this.store.un("load", this.onLoad);
10174             this.store.un("loadexception", this.onLoad);
10175         }
10176         if(store){
10177           
10178             store.on("datachanged", this.refresh, this);
10179             store.on("add", this.onAdd, this);
10180             store.on("remove", this.onRemove, this);
10181             store.on("update", this.onUpdate, this);
10182             store.on("clear", this.refresh, this);
10183             store.on("beforeload", this.onBeforeLoad, this);
10184             store.on("load", this.onLoad, this);
10185             store.on("loadexception", this.onLoad, this);
10186         }
10187         
10188         if(store){
10189             this.refresh();
10190         }
10191     },
10192     /**
10193      * onbeforeLoad - masks the loading area.
10194      *
10195      */
10196     onBeforeLoad : function(store,opts)
10197     {
10198          Roo.log('onBeforeLoad');   
10199         if (!opts.add) {
10200             this.el.update("");
10201         }
10202         this.el.mask(this.mask ? this.mask : "Loading" ); 
10203     },
10204     onLoad : function ()
10205     {
10206         this.el.unmask();
10207     },
10208     
10209
10210     /**
10211      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10212      * @param {HTMLElement} node
10213      * @return {HTMLElement} The template node
10214      */
10215     findItemFromChild : function(node){
10216         var el = this.dataName  ?
10217             this.el.child('.roo-tpl-' + this.dataName,true) :
10218             this.el.dom; 
10219         
10220         if(!node || node.parentNode == el){
10221                     return node;
10222             }
10223             var p = node.parentNode;
10224             while(p && p != el){
10225             if(p.parentNode == el){
10226                 return p;
10227             }
10228             p = p.parentNode;
10229         }
10230             return null;
10231     },
10232
10233     /** @ignore */
10234     onClick : function(e){
10235         var item = this.findItemFromChild(e.getTarget());
10236         if(item){
10237             var index = this.indexOf(item);
10238             if(this.onItemClick(item, index, e) !== false){
10239                 this.fireEvent("click", this, index, item, e);
10240             }
10241         }else{
10242             this.clearSelections();
10243         }
10244     },
10245
10246     /** @ignore */
10247     onContextMenu : function(e){
10248         var item = this.findItemFromChild(e.getTarget());
10249         if(item){
10250             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10251         }
10252     },
10253
10254     /** @ignore */
10255     onDblClick : function(e){
10256         var item = this.findItemFromChild(e.getTarget());
10257         if(item){
10258             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10259         }
10260     },
10261
10262     onItemClick : function(item, index, e)
10263     {
10264         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10265             return false;
10266         }
10267         if (this.toggleSelect) {
10268             var m = this.isSelected(item) ? 'unselect' : 'select';
10269             Roo.log(m);
10270             var _t = this;
10271             _t[m](item, true, false);
10272             return true;
10273         }
10274         if(this.multiSelect || this.singleSelect){
10275             if(this.multiSelect && e.shiftKey && this.lastSelection){
10276                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10277             }else{
10278                 this.select(item, this.multiSelect && e.ctrlKey);
10279                 this.lastSelection = item;
10280             }
10281             e.preventDefault();
10282         }
10283         return true;
10284     },
10285
10286     /**
10287      * Get the number of selected nodes.
10288      * @return {Number}
10289      */
10290     getSelectionCount : function(){
10291         return this.selections.length;
10292     },
10293
10294     /**
10295      * Get the currently selected nodes.
10296      * @return {Array} An array of HTMLElements
10297      */
10298     getSelectedNodes : function(){
10299         return this.selections;
10300     },
10301
10302     /**
10303      * Get the indexes of the selected nodes.
10304      * @return {Array}
10305      */
10306     getSelectedIndexes : function(){
10307         var indexes = [], s = this.selections;
10308         for(var i = 0, len = s.length; i < len; i++){
10309             indexes.push(s[i].nodeIndex);
10310         }
10311         return indexes;
10312     },
10313
10314     /**
10315      * Clear all selections
10316      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10317      */
10318     clearSelections : function(suppressEvent){
10319         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10320             this.cmp.elements = this.selections;
10321             this.cmp.removeClass(this.selectedClass);
10322             this.selections = [];
10323             if(!suppressEvent){
10324                 this.fireEvent("selectionchange", this, this.selections);
10325             }
10326         }
10327     },
10328
10329     /**
10330      * Returns true if the passed node is selected
10331      * @param {HTMLElement/Number} node The node or node index
10332      * @return {Boolean}
10333      */
10334     isSelected : function(node){
10335         var s = this.selections;
10336         if(s.length < 1){
10337             return false;
10338         }
10339         node = this.getNode(node);
10340         return s.indexOf(node) !== -1;
10341     },
10342
10343     /**
10344      * Selects nodes.
10345      * @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
10346      * @param {Boolean} keepExisting (optional) true to keep existing selections
10347      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10348      */
10349     select : function(nodeInfo, keepExisting, suppressEvent){
10350         if(nodeInfo instanceof Array){
10351             if(!keepExisting){
10352                 this.clearSelections(true);
10353             }
10354             for(var i = 0, len = nodeInfo.length; i < len; i++){
10355                 this.select(nodeInfo[i], true, true);
10356             }
10357             return;
10358         } 
10359         var node = this.getNode(nodeInfo);
10360         if(!node || this.isSelected(node)){
10361             return; // already selected.
10362         }
10363         if(!keepExisting){
10364             this.clearSelections(true);
10365         }
10366         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10367             Roo.fly(node).addClass(this.selectedClass);
10368             this.selections.push(node);
10369             if(!suppressEvent){
10370                 this.fireEvent("selectionchange", this, this.selections);
10371             }
10372         }
10373         
10374         
10375     },
10376       /**
10377      * Unselects nodes.
10378      * @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
10379      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10380      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10381      */
10382     unselect : function(nodeInfo, keepExisting, suppressEvent)
10383     {
10384         if(nodeInfo instanceof Array){
10385             Roo.each(this.selections, function(s) {
10386                 this.unselect(s, nodeInfo);
10387             }, this);
10388             return;
10389         }
10390         var node = this.getNode(nodeInfo);
10391         if(!node || !this.isSelected(node)){
10392             Roo.log("not selected");
10393             return; // not selected.
10394         }
10395         // fireevent???
10396         var ns = [];
10397         Roo.each(this.selections, function(s) {
10398             if (s == node ) {
10399                 Roo.fly(node).removeClass(this.selectedClass);
10400
10401                 return;
10402             }
10403             ns.push(s);
10404         },this);
10405         
10406         this.selections= ns;
10407         this.fireEvent("selectionchange", this, this.selections);
10408     },
10409
10410     /**
10411      * Gets a template node.
10412      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10413      * @return {HTMLElement} The node or null if it wasn't found
10414      */
10415     getNode : function(nodeInfo){
10416         if(typeof nodeInfo == "string"){
10417             return document.getElementById(nodeInfo);
10418         }else if(typeof nodeInfo == "number"){
10419             return this.nodes[nodeInfo];
10420         }
10421         return nodeInfo;
10422     },
10423
10424     /**
10425      * Gets a range template nodes.
10426      * @param {Number} startIndex
10427      * @param {Number} endIndex
10428      * @return {Array} An array of nodes
10429      */
10430     getNodes : function(start, end){
10431         var ns = this.nodes;
10432         start = start || 0;
10433         end = typeof end == "undefined" ? ns.length - 1 : end;
10434         var nodes = [];
10435         if(start <= end){
10436             for(var i = start; i <= end; i++){
10437                 nodes.push(ns[i]);
10438             }
10439         } else{
10440             for(var i = start; i >= end; i--){
10441                 nodes.push(ns[i]);
10442             }
10443         }
10444         return nodes;
10445     },
10446
10447     /**
10448      * Finds the index of the passed node
10449      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10450      * @return {Number} The index of the node or -1
10451      */
10452     indexOf : function(node){
10453         node = this.getNode(node);
10454         if(typeof node.nodeIndex == "number"){
10455             return node.nodeIndex;
10456         }
10457         var ns = this.nodes;
10458         for(var i = 0, len = ns.length; i < len; i++){
10459             if(ns[i] == node){
10460                 return i;
10461             }
10462         }
10463         return -1;
10464     }
10465 });
10466 /*
10467  * - LGPL
10468  *
10469  * based on jquery fullcalendar
10470  * 
10471  */
10472
10473 Roo.bootstrap = Roo.bootstrap || {};
10474 /**
10475  * @class Roo.bootstrap.Calendar
10476  * @extends Roo.bootstrap.Component
10477  * Bootstrap Calendar class
10478  * @cfg {Boolean} loadMask (true|false) default false
10479  * @cfg {Object} header generate the user specific header of the calendar, default false
10480
10481  * @constructor
10482  * Create a new Container
10483  * @param {Object} config The config object
10484  */
10485
10486
10487
10488 Roo.bootstrap.Calendar = function(config){
10489     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10490      this.addEvents({
10491         /**
10492              * @event select
10493              * Fires when a date is selected
10494              * @param {DatePicker} this
10495              * @param {Date} date The selected date
10496              */
10497         'select': true,
10498         /**
10499              * @event monthchange
10500              * Fires when the displayed month changes 
10501              * @param {DatePicker} this
10502              * @param {Date} date The selected month
10503              */
10504         'monthchange': true,
10505         /**
10506              * @event evententer
10507              * Fires when mouse over an event
10508              * @param {Calendar} this
10509              * @param {event} Event
10510              */
10511         'evententer': true,
10512         /**
10513              * @event eventleave
10514              * Fires when the mouse leaves an
10515              * @param {Calendar} this
10516              * @param {event}
10517              */
10518         'eventleave': true,
10519         /**
10520              * @event eventclick
10521              * Fires when the mouse click an
10522              * @param {Calendar} this
10523              * @param {event}
10524              */
10525         'eventclick': true
10526         
10527     });
10528
10529 };
10530
10531 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10532     
10533      /**
10534      * @cfg {Number} startDay
10535      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10536      */
10537     startDay : 0,
10538     
10539     loadMask : false,
10540     
10541     header : false,
10542       
10543     getAutoCreate : function(){
10544         
10545         
10546         var fc_button = function(name, corner, style, content ) {
10547             return Roo.apply({},{
10548                 tag : 'span',
10549                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10550                          (corner.length ?
10551                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10552                             ''
10553                         ),
10554                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10555                 unselectable: 'on'
10556             });
10557         };
10558         
10559         var header = {};
10560         
10561         if(!this.header){
10562             header = {
10563                 tag : 'table',
10564                 cls : 'fc-header',
10565                 style : 'width:100%',
10566                 cn : [
10567                     {
10568                         tag: 'tr',
10569                         cn : [
10570                             {
10571                                 tag : 'td',
10572                                 cls : 'fc-header-left',
10573                                 cn : [
10574                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
10575                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
10576                                     { tag: 'span', cls: 'fc-header-space' },
10577                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
10578
10579
10580                                 ]
10581                             },
10582
10583                             {
10584                                 tag : 'td',
10585                                 cls : 'fc-header-center',
10586                                 cn : [
10587                                     {
10588                                         tag: 'span',
10589                                         cls: 'fc-header-title',
10590                                         cn : {
10591                                             tag: 'H2',
10592                                             html : 'month / year'
10593                                         }
10594                                     }
10595
10596                                 ]
10597                             },
10598                             {
10599                                 tag : 'td',
10600                                 cls : 'fc-header-right',
10601                                 cn : [
10602                               /*      fc_button('month', 'left', '', 'month' ),
10603                                     fc_button('week', '', '', 'week' ),
10604                                     fc_button('day', 'right', '', 'day' )
10605                                 */    
10606
10607                                 ]
10608                             }
10609
10610                         ]
10611                     }
10612                 ]
10613             };
10614         }
10615         
10616         header = this.header;
10617         
10618        
10619         var cal_heads = function() {
10620             var ret = [];
10621             // fixme - handle this.
10622             
10623             for (var i =0; i < Date.dayNames.length; i++) {
10624                 var d = Date.dayNames[i];
10625                 ret.push({
10626                     tag: 'th',
10627                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10628                     html : d.substring(0,3)
10629                 });
10630                 
10631             }
10632             ret[0].cls += ' fc-first';
10633             ret[6].cls += ' fc-last';
10634             return ret;
10635         };
10636         var cal_cell = function(n) {
10637             return  {
10638                 tag: 'td',
10639                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10640                 cn : [
10641                     {
10642                         cn : [
10643                             {
10644                                 cls: 'fc-day-number',
10645                                 html: 'D'
10646                             },
10647                             {
10648                                 cls: 'fc-day-content',
10649                              
10650                                 cn : [
10651                                      {
10652                                         style: 'position: relative;' // height: 17px;
10653                                     }
10654                                 ]
10655                             }
10656                             
10657                             
10658                         ]
10659                     }
10660                 ]
10661                 
10662             }
10663         };
10664         var cal_rows = function() {
10665             
10666             var ret = []
10667             for (var r = 0; r < 6; r++) {
10668                 var row= {
10669                     tag : 'tr',
10670                     cls : 'fc-week',
10671                     cn : []
10672                 };
10673                 
10674                 for (var i =0; i < Date.dayNames.length; i++) {
10675                     var d = Date.dayNames[i];
10676                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10677
10678                 }
10679                 row.cn[0].cls+=' fc-first';
10680                 row.cn[0].cn[0].style = 'min-height:90px';
10681                 row.cn[6].cls+=' fc-last';
10682                 ret.push(row);
10683                 
10684             }
10685             ret[0].cls += ' fc-first';
10686             ret[4].cls += ' fc-prev-last';
10687             ret[5].cls += ' fc-last';
10688             return ret;
10689             
10690         };
10691         
10692         var cal_table = {
10693             tag: 'table',
10694             cls: 'fc-border-separate',
10695             style : 'width:100%',
10696             cellspacing  : 0,
10697             cn : [
10698                 { 
10699                     tag: 'thead',
10700                     cn : [
10701                         { 
10702                             tag: 'tr',
10703                             cls : 'fc-first fc-last',
10704                             cn : cal_heads()
10705                         }
10706                     ]
10707                 },
10708                 { 
10709                     tag: 'tbody',
10710                     cn : cal_rows()
10711                 }
10712                   
10713             ]
10714         };
10715          
10716          var cfg = {
10717             cls : 'fc fc-ltr',
10718             cn : [
10719                 header,
10720                 {
10721                     cls : 'fc-content',
10722                     style : "position: relative;",
10723                     cn : [
10724                         {
10725                             cls : 'fc-view fc-view-month fc-grid',
10726                             style : 'position: relative',
10727                             unselectable : 'on',
10728                             cn : [
10729                                 {
10730                                     cls : 'fc-event-container',
10731                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10732                                 },
10733                                 cal_table
10734                             ]
10735                         }
10736                     ]
10737     
10738                 }
10739            ] 
10740             
10741         };
10742         
10743          
10744         
10745         return cfg;
10746     },
10747     
10748     
10749     initEvents : function()
10750     {
10751         if(!this.store){
10752             throw "can not find store for calendar";
10753         }
10754         
10755         var mark = {
10756             tag: "div",
10757             cls:"x-dlg-mask",
10758             style: "text-align:center",
10759             cn: [
10760                 {
10761                     tag: "div",
10762                     style: "background-color:white;width:50%;margin:250 auto",
10763                     cn: [
10764                         {
10765                             tag: "img",
10766                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10767                         },
10768                         {
10769                             tag: "span",
10770                             html: "Loading"
10771                         }
10772                         
10773                     ]
10774                 }
10775             ]
10776         }
10777         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10778         
10779         var size = this.el.select('.fc-content', true).first().getSize();
10780         this.maskEl.setSize(size.width, size.height);
10781         this.maskEl.enableDisplayMode("block");
10782         if(!this.loadMask){
10783             this.maskEl.hide();
10784         }
10785         
10786         this.store = Roo.factory(this.store, Roo.data);
10787         this.store.on('load', this.onLoad, this);
10788         this.store.on('beforeload', this.onBeforeLoad, this);
10789         
10790         this.resize();
10791         
10792         this.cells = this.el.select('.fc-day',true);
10793         //Roo.log(this.cells);
10794         this.textNodes = this.el.query('.fc-day-number');
10795         this.cells.addClassOnOver('fc-state-hover');
10796         
10797         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10798         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10799         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10800         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10801         
10802         this.on('monthchange', this.onMonthChange, this);
10803         
10804         this.update(new Date().clearTime());
10805     },
10806     
10807     resize : function() {
10808         var sz  = this.el.getSize();
10809         
10810         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10811         this.el.select('.fc-day-content div',true).setHeight(34);
10812     },
10813     
10814     
10815     // private
10816     showPrevMonth : function(e){
10817         this.update(this.activeDate.add("mo", -1));
10818     },
10819     showToday : function(e){
10820         this.update(new Date().clearTime());
10821     },
10822     // private
10823     showNextMonth : function(e){
10824         this.update(this.activeDate.add("mo", 1));
10825     },
10826
10827     // private
10828     showPrevYear : function(){
10829         this.update(this.activeDate.add("y", -1));
10830     },
10831
10832     // private
10833     showNextYear : function(){
10834         this.update(this.activeDate.add("y", 1));
10835     },
10836
10837     
10838    // private
10839     update : function(date)
10840     {
10841         var vd = this.activeDate;
10842         this.activeDate = date;
10843 //        if(vd && this.el){
10844 //            var t = date.getTime();
10845 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10846 //                Roo.log('using add remove');
10847 //                
10848 //                this.fireEvent('monthchange', this, date);
10849 //                
10850 //                this.cells.removeClass("fc-state-highlight");
10851 //                this.cells.each(function(c){
10852 //                   if(c.dateValue == t){
10853 //                       c.addClass("fc-state-highlight");
10854 //                       setTimeout(function(){
10855 //                            try{c.dom.firstChild.focus();}catch(e){}
10856 //                       }, 50);
10857 //                       return false;
10858 //                   }
10859 //                   return true;
10860 //                });
10861 //                return;
10862 //            }
10863 //        }
10864         
10865         var days = date.getDaysInMonth();
10866         
10867         var firstOfMonth = date.getFirstDateOfMonth();
10868         var startingPos = firstOfMonth.getDay()-this.startDay;
10869         
10870         if(startingPos < this.startDay){
10871             startingPos += 7;
10872         }
10873         
10874         var pm = date.add(Date.MONTH, -1);
10875         var prevStart = pm.getDaysInMonth()-startingPos;
10876 //        
10877         this.cells = this.el.select('.fc-day',true);
10878         this.textNodes = this.el.query('.fc-day-number');
10879         this.cells.addClassOnOver('fc-state-hover');
10880         
10881         var cells = this.cells.elements;
10882         var textEls = this.textNodes;
10883         
10884         Roo.each(cells, function(cell){
10885             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10886         });
10887         
10888         days += startingPos;
10889
10890         // convert everything to numbers so it's fast
10891         var day = 86400000;
10892         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10893         //Roo.log(d);
10894         //Roo.log(pm);
10895         //Roo.log(prevStart);
10896         
10897         var today = new Date().clearTime().getTime();
10898         var sel = date.clearTime().getTime();
10899         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10900         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10901         var ddMatch = this.disabledDatesRE;
10902         var ddText = this.disabledDatesText;
10903         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10904         var ddaysText = this.disabledDaysText;
10905         var format = this.format;
10906         
10907         var setCellClass = function(cal, cell){
10908             
10909             //Roo.log('set Cell Class');
10910             cell.title = "";
10911             var t = d.getTime();
10912             
10913             //Roo.log(d);
10914             
10915             cell.dateValue = t;
10916             if(t == today){
10917                 cell.className += " fc-today";
10918                 cell.className += " fc-state-highlight";
10919                 cell.title = cal.todayText;
10920             }
10921             if(t == sel){
10922                 // disable highlight in other month..
10923                 //cell.className += " fc-state-highlight";
10924                 
10925             }
10926             // disabling
10927             if(t < min) {
10928                 cell.className = " fc-state-disabled";
10929                 cell.title = cal.minText;
10930                 return;
10931             }
10932             if(t > max) {
10933                 cell.className = " fc-state-disabled";
10934                 cell.title = cal.maxText;
10935                 return;
10936             }
10937             if(ddays){
10938                 if(ddays.indexOf(d.getDay()) != -1){
10939                     cell.title = ddaysText;
10940                     cell.className = " fc-state-disabled";
10941                 }
10942             }
10943             if(ddMatch && format){
10944                 var fvalue = d.dateFormat(format);
10945                 if(ddMatch.test(fvalue)){
10946                     cell.title = ddText.replace("%0", fvalue);
10947                     cell.className = " fc-state-disabled";
10948                 }
10949             }
10950             
10951             if (!cell.initialClassName) {
10952                 cell.initialClassName = cell.dom.className;
10953             }
10954             
10955             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10956         };
10957
10958         var i = 0;
10959         
10960         for(; i < startingPos; i++) {
10961             textEls[i].innerHTML = (++prevStart);
10962             d.setDate(d.getDate()+1);
10963             
10964             cells[i].className = "fc-past fc-other-month";
10965             setCellClass(this, cells[i]);
10966         }
10967         
10968         var intDay = 0;
10969         
10970         for(; i < days; i++){
10971             intDay = i - startingPos + 1;
10972             textEls[i].innerHTML = (intDay);
10973             d.setDate(d.getDate()+1);
10974             
10975             cells[i].className = ''; // "x-date-active";
10976             setCellClass(this, cells[i]);
10977         }
10978         var extraDays = 0;
10979         
10980         for(; i < 42; i++) {
10981             textEls[i].innerHTML = (++extraDays);
10982             d.setDate(d.getDate()+1);
10983             
10984             cells[i].className = "fc-future fc-other-month";
10985             setCellClass(this, cells[i]);
10986         }
10987         
10988         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10989         
10990         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10991         
10992         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10993         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10994         
10995         if(totalRows != 6){
10996             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10997             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10998         }
10999         
11000         this.fireEvent('monthchange', this, date);
11001         
11002         
11003         /*
11004         if(!this.internalRender){
11005             var main = this.el.dom.firstChild;
11006             var w = main.offsetWidth;
11007             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11008             Roo.fly(main).setWidth(w);
11009             this.internalRender = true;
11010             // opera does not respect the auto grow header center column
11011             // then, after it gets a width opera refuses to recalculate
11012             // without a second pass
11013             if(Roo.isOpera && !this.secondPass){
11014                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11015                 this.secondPass = true;
11016                 this.update.defer(10, this, [date]);
11017             }
11018         }
11019         */
11020         
11021     },
11022     
11023     findCell : function(dt) {
11024         dt = dt.clearTime().getTime();
11025         var ret = false;
11026         this.cells.each(function(c){
11027             //Roo.log("check " +c.dateValue + '?=' + dt);
11028             if(c.dateValue == dt){
11029                 ret = c;
11030                 return false;
11031             }
11032             return true;
11033         });
11034         
11035         return ret;
11036     },
11037     
11038     findCells : function(ev) {
11039         var s = ev.start.clone().clearTime().getTime();
11040        // Roo.log(s);
11041         var e= ev.end.clone().clearTime().getTime();
11042        // Roo.log(e);
11043         var ret = [];
11044         this.cells.each(function(c){
11045              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11046             
11047             if(c.dateValue > e){
11048                 return ;
11049             }
11050             if(c.dateValue < s){
11051                 return ;
11052             }
11053             ret.push(c);
11054         });
11055         
11056         return ret;    
11057     },
11058     
11059     findBestRow: function(cells)
11060     {
11061         var ret = 0;
11062         
11063         for (var i =0 ; i < cells.length;i++) {
11064             ret  = Math.max(cells[i].rows || 0,ret);
11065         }
11066         return ret;
11067         
11068     },
11069     
11070     
11071     addItem : function(ev)
11072     {
11073         // look for vertical location slot in
11074         var cells = this.findCells(ev);
11075         
11076         ev.row = this.findBestRow(cells);
11077         
11078         // work out the location.
11079         
11080         var crow = false;
11081         var rows = [];
11082         for(var i =0; i < cells.length; i++) {
11083             if (!crow) {
11084                 crow = {
11085                     start : cells[i],
11086                     end :  cells[i]
11087                 };
11088                 continue;
11089             }
11090             if (crow.start.getY() == cells[i].getY()) {
11091                 // on same row.
11092                 crow.end = cells[i];
11093                 continue;
11094             }
11095             // different row.
11096             rows.push(crow);
11097             crow = {
11098                 start: cells[i],
11099                 end : cells[i]
11100             };
11101             
11102         }
11103         
11104         rows.push(crow);
11105         ev.els = [];
11106         ev.rows = rows;
11107         ev.cells = cells;
11108         for (var i = 0; i < cells.length;i++) {
11109             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11110             
11111         }
11112         
11113         this.calevents.push(ev);
11114     },
11115     
11116     clearEvents: function() {
11117         
11118         if(!this.calevents){
11119             return;
11120         }
11121         
11122         Roo.each(this.cells.elements, function(c){
11123             c.rows = 0;
11124         });
11125         
11126         Roo.each(this.calevents, function(e) {
11127             Roo.each(e.els, function(el) {
11128                 el.un('mouseenter' ,this.onEventEnter, this);
11129                 el.un('mouseleave' ,this.onEventLeave, this);
11130                 el.remove();
11131             },this);
11132         },this);
11133         
11134     },
11135     
11136     renderEvents: function()
11137     {   
11138         // first make sure there is enough space..
11139         
11140         this.cells.each(function(c) {
11141         
11142             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11143         });
11144         
11145         for (var e = 0; e < this.calevents.length; e++) {
11146             var ev = this.calevents[e];
11147             var cells = ev.cells;
11148             var rows = ev.rows;
11149             
11150             for(var i =0; i < rows.length; i++) {
11151                 
11152                  
11153                 // how many rows should it span..
11154                 
11155                 var  cfg = {
11156                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11157                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11158                     
11159                     unselectable : "on",
11160                     cn : [
11161                         {
11162                             cls: 'fc-event-inner',
11163                             cn : [
11164 //                                {
11165 //                                  tag:'span',
11166 //                                  cls: 'fc-event-time',
11167 //                                  html : cells.length > 1 ? '' : ev.time
11168 //                                },
11169                                 {
11170                                   tag:'span',
11171                                   cls: 'fc-event-title',
11172                                   html : String.format('{0}', ev.title)
11173                                 }
11174                                 
11175                                 
11176                             ]
11177                         },
11178                         {
11179                             cls: 'ui-resizable-handle ui-resizable-e',
11180                             html : '&nbsp;&nbsp;&nbsp'
11181                         }
11182                         
11183                     ]
11184                 };
11185                 if (i == 0) {
11186                     cfg.cls += ' fc-event-start';
11187                 }
11188                 if ((i+1) == rows.length) {
11189                     cfg.cls += ' fc-event-end';
11190                 }
11191                 
11192                 var ctr = this.el.select('.fc-event-container',true).first();
11193                 var cg = ctr.createChild(cfg);
11194                 
11195                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11196                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11197                 cg.on('click', this.onEventClick, this, ev);
11198                 
11199                 ev.els.push(cg);
11200                 
11201                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11202                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11203                 //Roo.log(cg);
11204                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11205                 cg.setWidth(ebox.right - sbox.x -2);
11206             }
11207             
11208             
11209         }
11210         
11211     },
11212     
11213     onEventEnter: function (e, el,event,d) {
11214         this.fireEvent('evententer', this, el, event);
11215     },
11216     
11217     onEventLeave: function (e, el,event,d) {
11218         this.fireEvent('eventleave', this, el, event);
11219     },
11220     
11221     onEventClick: function (e, el,event,d) {
11222         this.fireEvent('eventclick', this, el, event);
11223     },
11224     
11225     onMonthChange: function () {
11226         this.store.load();
11227     },
11228     
11229     onLoad: function () 
11230     {   
11231         this.calevents = [];
11232         var cal = this;
11233         
11234         if(this.store.getCount() > 0){
11235             this.store.data.each(function(d){
11236                cal.addItem({
11237                     id : d.data.id,
11238                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11239                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11240                     time : d.data.start_time,
11241                     title : d.data.title,
11242                     description : d.data.description,
11243                     venue : d.data.venue
11244                 });
11245             });
11246         }
11247         
11248         this.renderEvents();
11249         
11250         if(this.loadMask){
11251             this.maskEl.hide();
11252         }
11253     },
11254     
11255     onBeforeLoad: function()
11256     {
11257         this.clearEvents();
11258         
11259         if(this.loadMask){
11260             this.maskEl.show();
11261         }
11262     }
11263 });
11264
11265  
11266  /*
11267  * - LGPL
11268  *
11269  * element
11270  * 
11271  */
11272
11273 /**
11274  * @class Roo.bootstrap.Popover
11275  * @extends Roo.bootstrap.Component
11276  * Bootstrap Popover class
11277  * @cfg {String} html contents of the popover   (or false to use children..)
11278  * @cfg {String} title of popover (or false to hide)
11279  * @cfg {String} placement how it is placed
11280  * @cfg {String} trigger click || hover (or false to trigger manually)
11281  * @cfg {String} over what (parent or false to trigger manually.)
11282  * 
11283  * @constructor
11284  * Create a new Popover
11285  * @param {Object} config The config object
11286  */
11287
11288 Roo.bootstrap.Popover = function(config){
11289     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11290 };
11291
11292 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11293     
11294     title: 'Fill in a title',
11295     html: false,
11296     
11297     placement : 'right',
11298     trigger : 'hover', // hover
11299     
11300     over: 'parent',
11301     
11302     can_build_overlaid : false,
11303     
11304     getChildContainer : function()
11305     {
11306         return this.el.select('.popover-content',true).first();
11307     },
11308     
11309     getAutoCreate : function(){
11310          Roo.log('make popover?');
11311         var cfg = {
11312            cls : 'popover roo-dynamic',
11313            style: 'display:block',
11314            cn : [
11315                 {
11316                     cls : 'arrow'
11317                 },
11318                 {
11319                     cls : 'popover-inner',
11320                     cn : [
11321                         {
11322                             tag: 'h3',
11323                             cls: 'popover-title',
11324                             html : this.title
11325                         },
11326                         {
11327                             cls : 'popover-content',
11328                             html : this.html
11329                         }
11330                     ]
11331                     
11332                 }
11333            ]
11334         };
11335         
11336         return cfg;
11337     },
11338     setTitle: function(str)
11339     {
11340         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11341     },
11342     setContent: function(str)
11343     {
11344         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11345     },
11346     // as it get's added to the bottom of the page.
11347     onRender : function(ct, position)
11348     {
11349         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11350         if(!this.el){
11351             var cfg = Roo.apply({},  this.getAutoCreate());
11352             cfg.id = Roo.id();
11353             
11354             if (this.cls) {
11355                 cfg.cls += ' ' + this.cls;
11356             }
11357             if (this.style) {
11358                 cfg.style = this.style;
11359             }
11360             Roo.log("adding to ")
11361             this.el = Roo.get(document.body).createChild(cfg, position);
11362             Roo.log(this.el);
11363         }
11364         this.initEvents();
11365     },
11366     
11367     initEvents : function()
11368     {
11369         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11370         this.el.enableDisplayMode('block');
11371         this.el.hide();
11372         if (this.over === false) {
11373             return; 
11374         }
11375         if (this.triggers === false) {
11376             return;
11377         }
11378         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11379         var triggers = this.trigger ? this.trigger.split(' ') : [];
11380         Roo.each(triggers, function(trigger) {
11381         
11382             if (trigger == 'click') {
11383                 on_el.on('click', this.toggle, this);
11384             } else if (trigger != 'manual') {
11385                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11386                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11387       
11388                 on_el.on(eventIn  ,this.enter, this);
11389                 on_el.on(eventOut, this.leave, this);
11390             }
11391         }, this);
11392         
11393     },
11394     
11395     
11396     // private
11397     timeout : null,
11398     hoverState : null,
11399     
11400     toggle : function () {
11401         this.hoverState == 'in' ? this.leave() : this.enter();
11402     },
11403     
11404     enter : function () {
11405        
11406     
11407         clearTimeout(this.timeout);
11408     
11409         this.hoverState = 'in'
11410     
11411         if (!this.delay || !this.delay.show) {
11412             this.show();
11413             return 
11414         }
11415         var _t = this;
11416         this.timeout = setTimeout(function () {
11417             if (_t.hoverState == 'in') {
11418                 _t.show();
11419             }
11420         }, this.delay.show)
11421     },
11422     leave : function() {
11423         clearTimeout(this.timeout);
11424     
11425         this.hoverState = 'out'
11426     
11427         if (!this.delay || !this.delay.hide) {
11428             this.hide();
11429             return 
11430         }
11431         var _t = this;
11432         this.timeout = setTimeout(function () {
11433             if (_t.hoverState == 'out') {
11434                 _t.hide();
11435             }
11436         }, this.delay.hide)
11437     },
11438     
11439     show : function (on_el)
11440     {
11441         if (!on_el) {
11442             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11443         }
11444         // set content.
11445         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11446         if (this.html !== false) {
11447             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11448         }
11449         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11450         if (!this.title.length) {
11451             this.el.select('.popover-title',true).hide();
11452         }
11453         
11454         var placement = typeof this.placement == 'function' ?
11455             this.placement.call(this, this.el, on_el) :
11456             this.placement;
11457             
11458         var autoToken = /\s?auto?\s?/i;
11459         var autoPlace = autoToken.test(placement);
11460         if (autoPlace) {
11461             placement = placement.replace(autoToken, '') || 'top';
11462         }
11463         
11464         //this.el.detach()
11465         //this.el.setXY([0,0]);
11466         this.el.show();
11467         this.el.dom.style.display='block';
11468         this.el.addClass(placement);
11469         
11470         //this.el.appendTo(on_el);
11471         
11472         var p = this.getPosition();
11473         var box = this.el.getBox();
11474         
11475         if (autoPlace) {
11476             // fixme..
11477         }
11478         var align = Roo.bootstrap.Popover.alignment[placement]
11479         this.el.alignTo(on_el, align[0],align[1]);
11480         //var arrow = this.el.select('.arrow',true).first();
11481         //arrow.set(align[2], 
11482         
11483         this.el.addClass('in');
11484         this.hoverState = null;
11485         
11486         if (this.el.hasClass('fade')) {
11487             // fade it?
11488         }
11489         
11490     },
11491     hide : function()
11492     {
11493         this.el.setXY([0,0]);
11494         this.el.removeClass('in');
11495         this.el.hide();
11496         
11497     }
11498     
11499 });
11500
11501 Roo.bootstrap.Popover.alignment = {
11502     'left' : ['r-l', [-10,0], 'right'],
11503     'right' : ['l-r', [10,0], 'left'],
11504     'bottom' : ['t-b', [0,10], 'top'],
11505     'top' : [ 'b-t', [0,-10], 'bottom']
11506 };
11507
11508  /*
11509  * - LGPL
11510  *
11511  * Progress
11512  * 
11513  */
11514
11515 /**
11516  * @class Roo.bootstrap.Progress
11517  * @extends Roo.bootstrap.Component
11518  * Bootstrap Progress class
11519  * @cfg {Boolean} striped striped of the progress bar
11520  * @cfg {Boolean} active animated of the progress bar
11521  * 
11522  * 
11523  * @constructor
11524  * Create a new Progress
11525  * @param {Object} config The config object
11526  */
11527
11528 Roo.bootstrap.Progress = function(config){
11529     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11530 };
11531
11532 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
11533     
11534     striped : false,
11535     active: false,
11536     
11537     getAutoCreate : function(){
11538         var cfg = {
11539             tag: 'div',
11540             cls: 'progress'
11541         };
11542         
11543         
11544         if(this.striped){
11545             cfg.cls += ' progress-striped';
11546         }
11547       
11548         if(this.active){
11549             cfg.cls += ' active';
11550         }
11551         
11552         
11553         return cfg;
11554     }
11555    
11556 });
11557
11558  
11559
11560  /*
11561  * - LGPL
11562  *
11563  * ProgressBar
11564  * 
11565  */
11566
11567 /**
11568  * @class Roo.bootstrap.ProgressBar
11569  * @extends Roo.bootstrap.Component
11570  * Bootstrap ProgressBar class
11571  * @cfg {Number} aria_valuenow aria-value now
11572  * @cfg {Number} aria_valuemin aria-value min
11573  * @cfg {Number} aria_valuemax aria-value max
11574  * @cfg {String} label label for the progress bar
11575  * @cfg {String} panel (success | info | warning | danger )
11576  * @cfg {String} role role of the progress bar
11577  * @cfg {String} sr_only text
11578  * 
11579  * 
11580  * @constructor
11581  * Create a new ProgressBar
11582  * @param {Object} config The config object
11583  */
11584
11585 Roo.bootstrap.ProgressBar = function(config){
11586     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11587 };
11588
11589 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
11590     
11591     aria_valuenow : 0,
11592     aria_valuemin : 0,
11593     aria_valuemax : 100,
11594     label : false,
11595     panel : false,
11596     role : false,
11597     sr_only: false,
11598     
11599     getAutoCreate : function()
11600     {
11601         
11602         var cfg = {
11603             tag: 'div',
11604             cls: 'progress-bar',
11605             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11606         };
11607         
11608         if(this.sr_only){
11609             cfg.cn = {
11610                 tag: 'span',
11611                 cls: 'sr-only',
11612                 html: this.sr_only
11613             }
11614         }
11615         
11616         if(this.role){
11617             cfg.role = this.role;
11618         }
11619         
11620         if(this.aria_valuenow){
11621             cfg['aria-valuenow'] = this.aria_valuenow;
11622         }
11623         
11624         if(this.aria_valuemin){
11625             cfg['aria-valuemin'] = this.aria_valuemin;
11626         }
11627         
11628         if(this.aria_valuemax){
11629             cfg['aria-valuemax'] = this.aria_valuemax;
11630         }
11631         
11632         if(this.label && !this.sr_only){
11633             cfg.html = this.label;
11634         }
11635         
11636         if(this.panel){
11637             cfg.cls += ' progress-bar-' + this.panel;
11638         }
11639         
11640         return cfg;
11641     },
11642     
11643     update : function(aria_valuenow)
11644     {
11645         this.aria_valuenow = aria_valuenow;
11646         
11647         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11648     }
11649    
11650 });
11651
11652  
11653
11654  /*
11655  * - LGPL
11656  *
11657  * TabPanel
11658  * 
11659  */
11660
11661 /**
11662  * @class Roo.bootstrap.TabPanel
11663  * @extends Roo.bootstrap.Component
11664  * Bootstrap TabPanel class
11665  * @cfg {Boolean} active panel active
11666  * @cfg {String} html panel content
11667  * @cfg {String} tabId tab relate id
11668  * @cfg {String} navId The navbar which triggers show hide
11669  * 
11670  * 
11671  * @constructor
11672  * Create a new TabPanel
11673  * @param {Object} config The config object
11674  */
11675
11676 Roo.bootstrap.TabPanel = function(config){
11677     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11678      this.addEvents({
11679         /**
11680              * @event changed
11681              * Fires when the active status changes
11682              * @param {Roo.bootstrap.TabPanel} this
11683              * @param {Boolean} state the new state
11684             
11685          */
11686         'changed': true
11687      });
11688 };
11689
11690 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11691     
11692     active: false,
11693     html: false,
11694     tabId: false,
11695     navId : false,
11696     
11697     getAutoCreate : function(){
11698         var cfg = {
11699             tag: 'div',
11700             cls: 'tab-pane',
11701             html: this.html || ''
11702         };
11703         
11704         if(this.active){
11705             cfg.cls += ' active';
11706         }
11707         
11708         if(this.tabId){
11709             cfg.tabId = this.tabId;
11710         }
11711         
11712         return cfg;
11713     },
11714     onRender : function(ct, position)
11715     {
11716        // Roo.log("Call onRender: " + this.xtype);
11717         
11718         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11719         
11720         if (this.navId && this.tabId) {
11721             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11722             if (!item) {
11723                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11724             } else {
11725                 item.on('changed', function(item, state) {
11726                     this.setActive(state);
11727                 }, this);
11728             }
11729         }
11730         
11731     },
11732     setActive: function(state)
11733     {
11734         Roo.log("panel - set active " + this.tabId + "=" + state);
11735         
11736         this.active = state;
11737         if (!state) {
11738             this.el.removeClass('active');
11739             
11740         } else  if (!this.el.hasClass('active')) {
11741             this.el.addClass('active');
11742         }
11743         this.fireEvent('changed', this, state);
11744     }
11745     
11746     
11747 });
11748  
11749
11750  
11751
11752  /*
11753  * - LGPL
11754  *
11755  * DateField
11756  * 
11757  */
11758
11759 /**
11760  * @class Roo.bootstrap.DateField
11761  * @extends Roo.bootstrap.Input
11762  * Bootstrap DateField class
11763  * @cfg {Number} weekStart default 0
11764  * @cfg {Number} weekStart default 0
11765  * @cfg {Number} viewMode default empty, (months|years)
11766  * @cfg {Number} minViewMode default empty, (months|years)
11767  * @cfg {Number} startDate default -Infinity
11768  * @cfg {Number} endDate default Infinity
11769  * @cfg {Boolean} todayHighlight default false
11770  * @cfg {Boolean} todayBtn default false
11771  * @cfg {Boolean} calendarWeeks default false
11772  * @cfg {Object} daysOfWeekDisabled default empty
11773  * 
11774  * @cfg {Boolean} keyboardNavigation default true
11775  * @cfg {String} language default en
11776  * 
11777  * @constructor
11778  * Create a new DateField
11779  * @param {Object} config The config object
11780  */
11781
11782 Roo.bootstrap.DateField = function(config){
11783     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11784      this.addEvents({
11785             /**
11786              * @event show
11787              * Fires when this field show.
11788              * @param {Roo.bootstrap.DateField} this
11789              * @param {Mixed} date The date value
11790              */
11791             show : true,
11792             /**
11793              * @event show
11794              * Fires when this field hide.
11795              * @param {Roo.bootstrap.DateField} this
11796              * @param {Mixed} date The date value
11797              */
11798             hide : true,
11799             /**
11800              * @event select
11801              * Fires when select a date.
11802              * @param {Roo.bootstrap.DateField} this
11803              * @param {Mixed} date The date value
11804              */
11805             select : true
11806         });
11807 };
11808
11809 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11810     
11811     /**
11812      * @cfg {String} format
11813      * The default date format string which can be overriden for localization support.  The format must be
11814      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11815      */
11816     format : "m/d/y",
11817     /**
11818      * @cfg {String} altFormats
11819      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11820      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11821      */
11822     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11823     
11824     weekStart : 0,
11825     
11826     viewMode : '',
11827     
11828     minViewMode : '',
11829     
11830     todayHighlight : false,
11831     
11832     todayBtn: false,
11833     
11834     language: 'en',
11835     
11836     keyboardNavigation: true,
11837     
11838     calendarWeeks: false,
11839     
11840     startDate: -Infinity,
11841     
11842     endDate: Infinity,
11843     
11844     daysOfWeekDisabled: [],
11845     
11846     _events: [],
11847     
11848     UTCDate: function()
11849     {
11850         return new Date(Date.UTC.apply(Date, arguments));
11851     },
11852     
11853     UTCToday: function()
11854     {
11855         var today = new Date();
11856         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11857     },
11858     
11859     getDate: function() {
11860             var d = this.getUTCDate();
11861             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11862     },
11863     
11864     getUTCDate: function() {
11865             return this.date;
11866     },
11867     
11868     setDate: function(d) {
11869             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11870     },
11871     
11872     setUTCDate: function(d) {
11873             this.date = d;
11874             this.setValue(this.formatDate(this.date));
11875     },
11876         
11877     onRender: function(ct, position)
11878     {
11879         
11880         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11881         
11882         this.language = this.language || 'en';
11883         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11884         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11885         
11886         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11887         this.format = this.format || 'm/d/y';
11888         this.isInline = false;
11889         this.isInput = true;
11890         this.component = this.el.select('.add-on', true).first() || false;
11891         this.component = (this.component && this.component.length === 0) ? false : this.component;
11892         this.hasInput = this.component && this.inputEL().length;
11893         
11894         if (typeof(this.minViewMode === 'string')) {
11895             switch (this.minViewMode) {
11896                 case 'months':
11897                     this.minViewMode = 1;
11898                     break;
11899                 case 'years':
11900                     this.minViewMode = 2;
11901                     break;
11902                 default:
11903                     this.minViewMode = 0;
11904                     break;
11905             }
11906         }
11907         
11908         if (typeof(this.viewMode === 'string')) {
11909             switch (this.viewMode) {
11910                 case 'months':
11911                     this.viewMode = 1;
11912                     break;
11913                 case 'years':
11914                     this.viewMode = 2;
11915                     break;
11916                 default:
11917                     this.viewMode = 0;
11918                     break;
11919             }
11920         }
11921                 
11922         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11923         
11924         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11925         
11926         this.picker().on('mousedown', this.onMousedown, this);
11927         this.picker().on('click', this.onClick, this);
11928         
11929         this.picker().addClass('datepicker-dropdown');
11930         
11931         this.startViewMode = this.viewMode;
11932         
11933         
11934         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11935             if(!this.calendarWeeks){
11936                 v.remove();
11937                 return;
11938             };
11939             
11940             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11941             v.attr('colspan', function(i, val){
11942                 return parseInt(val) + 1;
11943             });
11944         })
11945                         
11946         
11947         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11948         
11949         this.setStartDate(this.startDate);
11950         this.setEndDate(this.endDate);
11951         
11952         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11953         
11954         this.fillDow();
11955         this.fillMonths();
11956         this.update();
11957         this.showMode();
11958         
11959         if(this.isInline) {
11960             this.show();
11961         }
11962     },
11963     
11964     picker : function()
11965     {
11966         return this.el.select('.datepicker', true).first();
11967     },
11968     
11969     fillDow: function()
11970     {
11971         var dowCnt = this.weekStart;
11972         
11973         var dow = {
11974             tag: 'tr',
11975             cn: [
11976                 
11977             ]
11978         };
11979         
11980         if(this.calendarWeeks){
11981             dow.cn.push({
11982                 tag: 'th',
11983                 cls: 'cw',
11984                 html: '&nbsp;'
11985             })
11986         }
11987         
11988         while (dowCnt < this.weekStart + 7) {
11989             dow.cn.push({
11990                 tag: 'th',
11991                 cls: 'dow',
11992                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11993             });
11994         }
11995         
11996         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11997     },
11998     
11999     fillMonths: function()
12000     {    
12001         var i = 0
12002         var months = this.picker().select('>.datepicker-months td', true).first();
12003         
12004         months.dom.innerHTML = '';
12005         
12006         while (i < 12) {
12007             var month = {
12008                 tag: 'span',
12009                 cls: 'month',
12010                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12011             }
12012             
12013             months.createChild(month);
12014         }
12015         
12016     },
12017     
12018     update: function(){
12019         
12020         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12021         
12022         if (this.date < this.startDate) {
12023             this.viewDate = new Date(this.startDate);
12024         } else if (this.date > this.endDate) {
12025             this.viewDate = new Date(this.endDate);
12026         } else {
12027             this.viewDate = new Date(this.date);
12028         }
12029         
12030         this.fill();
12031     },
12032     
12033     fill: function() {
12034         var d = new Date(this.viewDate),
12035                 year = d.getUTCFullYear(),
12036                 month = d.getUTCMonth(),
12037                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12038                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12039                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12040                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12041                 currentDate = this.date && this.date.valueOf(),
12042                 today = this.UTCToday();
12043         
12044         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12045         
12046 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12047         
12048 //        this.picker.select('>tfoot th.today').
12049 //                                              .text(dates[this.language].today)
12050 //                                              .toggle(this.todayBtn !== false);
12051     
12052         this.updateNavArrows();
12053         this.fillMonths();
12054                                                 
12055         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12056         
12057         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12058          
12059         prevMonth.setUTCDate(day);
12060         
12061         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12062         
12063         var nextMonth = new Date(prevMonth);
12064         
12065         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12066         
12067         nextMonth = nextMonth.valueOf();
12068         
12069         var fillMonths = false;
12070         
12071         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12072         
12073         while(prevMonth.valueOf() < nextMonth) {
12074             var clsName = '';
12075             
12076             if (prevMonth.getUTCDay() === this.weekStart) {
12077                 if(fillMonths){
12078                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12079                 }
12080                     
12081                 fillMonths = {
12082                     tag: 'tr',
12083                     cn: []
12084                 };
12085                 
12086                 if(this.calendarWeeks){
12087                     // ISO 8601: First week contains first thursday.
12088                     // ISO also states week starts on Monday, but we can be more abstract here.
12089                     var
12090                     // Start of current week: based on weekstart/current date
12091                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12092                     // Thursday of this week
12093                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12094                     // First Thursday of year, year from thursday
12095                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12096                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12097                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12098                     
12099                     fillMonths.cn.push({
12100                         tag: 'td',
12101                         cls: 'cw',
12102                         html: calWeek
12103                     });
12104                 }
12105             }
12106             
12107             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12108                 clsName += ' old';
12109             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12110                 clsName += ' new';
12111             }
12112             if (this.todayHighlight &&
12113                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12114                 prevMonth.getUTCMonth() == today.getMonth() &&
12115                 prevMonth.getUTCDate() == today.getDate()) {
12116                 clsName += ' today';
12117             }
12118             
12119             if (currentDate && prevMonth.valueOf() === currentDate) {
12120                 clsName += ' active';
12121             }
12122             
12123             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12124                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12125                     clsName += ' disabled';
12126             }
12127             
12128             fillMonths.cn.push({
12129                 tag: 'td',
12130                 cls: 'day ' + clsName,
12131                 html: prevMonth.getDate()
12132             })
12133             
12134             prevMonth.setDate(prevMonth.getDate()+1);
12135         }
12136           
12137         var currentYear = this.date && this.date.getUTCFullYear();
12138         var currentMonth = this.date && this.date.getUTCMonth();
12139         
12140         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12141         
12142         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12143             v.removeClass('active');
12144             
12145             if(currentYear === year && k === currentMonth){
12146                 v.addClass('active');
12147             }
12148             
12149             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12150                 v.addClass('disabled');
12151             }
12152             
12153         });
12154         
12155         
12156         year = parseInt(year/10, 10) * 10;
12157         
12158         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12159         
12160         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12161         
12162         year -= 1;
12163         for (var i = -1; i < 11; i++) {
12164             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12165                 tag: 'span',
12166                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12167                 html: year
12168             })
12169             
12170             year += 1;
12171         }
12172     },
12173     
12174     showMode: function(dir) {
12175         if (dir) {
12176             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12177         }
12178         Roo.each(this.picker().select('>div',true).elements, function(v){
12179             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12180             v.hide();
12181         });
12182         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12183     },
12184     
12185     place: function()
12186     {
12187         if(this.isInline) return;
12188         
12189         this.picker().removeClass(['bottom', 'top']);
12190         
12191         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12192             /*
12193              * place to the top of element!
12194              *
12195              */
12196             
12197             this.picker().addClass('top');
12198             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12199             
12200             return;
12201         }
12202         
12203         this.picker().addClass('bottom');
12204         
12205         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12206     },
12207     
12208     parseDate : function(value){
12209         if(!value || value instanceof Date){
12210             return value;
12211         }
12212         var v = Date.parseDate(value, this.format);
12213         if (!v && this.useIso) {
12214             v = Date.parseDate(value, 'Y-m-d');
12215         }
12216         if(!v && this.altFormats){
12217             if(!this.altFormatsArray){
12218                 this.altFormatsArray = this.altFormats.split("|");
12219             }
12220             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12221                 v = Date.parseDate(value, this.altFormatsArray[i]);
12222             }
12223         }
12224         return v;
12225     },
12226     
12227     formatDate : function(date, fmt){
12228         return (!date || !(date instanceof Date)) ?
12229         date : date.dateFormat(fmt || this.format);
12230     },
12231     
12232     onFocus : function()
12233     {
12234         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12235         this.show();
12236     },
12237     
12238     onBlur : function()
12239     {
12240         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12241         this.hide();
12242     },
12243     
12244     show : function()
12245     {
12246         this.picker().show();
12247         this.update();
12248         this.place();
12249         
12250         this.fireEvent('show', this, this.date);
12251     },
12252     
12253     hide : function()
12254     {
12255         if(this.isInline) return;
12256         this.picker().hide();
12257         this.viewMode = this.startViewMode;
12258         this.showMode();
12259         
12260         this.fireEvent('hide', this, this.date);
12261         
12262     },
12263     
12264     onMousedown: function(e){
12265         e.stopPropagation();
12266         e.preventDefault();
12267     },
12268     
12269     keyup: function(e){
12270         Roo.bootstrap.DateField.superclass.keyup.call(this);
12271         this.update();
12272         
12273     },
12274
12275     setValue: function(v){
12276         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12277         
12278         this.fireEvent('select', this, this.date);
12279         
12280     },
12281     
12282     fireKey: function(e){
12283         if (!this.picker().isVisible()){
12284             if (e.keyCode == 27) // allow escape to hide and re-show picker
12285                 this.show();
12286             return;
12287         }
12288         var dateChanged = false,
12289         dir, day, month,
12290         newDate, newViewDate;
12291         switch(e.keyCode){
12292             case 27: // escape
12293                 this.hide();
12294                 e.preventDefault();
12295                 break;
12296             case 37: // left
12297             case 39: // right
12298                 if (!this.keyboardNavigation) break;
12299                 dir = e.keyCode == 37 ? -1 : 1;
12300                 
12301                 if (e.ctrlKey){
12302                     newDate = this.moveYear(this.date, dir);
12303                     newViewDate = this.moveYear(this.viewDate, dir);
12304                 } else if (e.shiftKey){
12305                     newDate = this.moveMonth(this.date, dir);
12306                     newViewDate = this.moveMonth(this.viewDate, dir);
12307                 } else {
12308                     newDate = new Date(this.date);
12309                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12310                     newViewDate = new Date(this.viewDate);
12311                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12312                 }
12313                 if (this.dateWithinRange(newDate)){
12314                     this.date = newDate;
12315                     this.viewDate = newViewDate;
12316                     this.setValue(this.formatDate(this.date));
12317                     this.update();
12318                     e.preventDefault();
12319                     dateChanged = true;
12320                 }
12321                 break;
12322             case 38: // up
12323             case 40: // down
12324                 if (!this.keyboardNavigation) break;
12325                 dir = e.keyCode == 38 ? -1 : 1;
12326                 if (e.ctrlKey){
12327                     newDate = this.moveYear(this.date, dir);
12328                     newViewDate = this.moveYear(this.viewDate, dir);
12329                 } else if (e.shiftKey){
12330                     newDate = this.moveMonth(this.date, dir);
12331                     newViewDate = this.moveMonth(this.viewDate, dir);
12332                 } else {
12333                     newDate = new Date(this.date);
12334                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12335                     newViewDate = new Date(this.viewDate);
12336                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12337                 }
12338                 if (this.dateWithinRange(newDate)){
12339                     this.date = newDate;
12340                     this.viewDate = newViewDate;
12341                     this.setValue(this.formatDate(this.date));
12342                     this.update();
12343                     e.preventDefault();
12344                     dateChanged = true;
12345                 }
12346                 break;
12347             case 13: // enter
12348                 this.setValue(this.formatDate(this.date));
12349                 this.hide();
12350                 e.preventDefault();
12351                 break;
12352             case 9: // tab
12353                 this.setValue(this.formatDate(this.date));
12354                 this.hide();
12355                 break;
12356         }
12357     },
12358     
12359     
12360     onClick: function(e) {
12361         e.stopPropagation();
12362         e.preventDefault();
12363         
12364         var target = e.getTarget();
12365         
12366         if(target.nodeName.toLowerCase() === 'i'){
12367             target = Roo.get(target).dom.parentNode;
12368         }
12369         
12370         var nodeName = target.nodeName;
12371         var className = target.className;
12372         var html = target.innerHTML;
12373         
12374         switch(nodeName.toLowerCase()) {
12375             case 'th':
12376                 switch(className) {
12377                     case 'switch':
12378                         this.showMode(1);
12379                         break;
12380                     case 'prev':
12381                     case 'next':
12382                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12383                         switch(this.viewMode){
12384                                 case 0:
12385                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12386                                         break;
12387                                 case 1:
12388                                 case 2:
12389                                         this.viewDate = this.moveYear(this.viewDate, dir);
12390                                         break;
12391                         }
12392                         this.fill();
12393                         break;
12394                     case 'today':
12395                         var date = new Date();
12396                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12397                         this.fill()
12398                         this.setValue(this.formatDate(this.date));
12399                         this.hide();
12400                         break;
12401                 }
12402                 break;
12403             case 'span':
12404                 if (className.indexOf('disabled') === -1) {
12405                     this.viewDate.setUTCDate(1);
12406                     if (className.indexOf('month') !== -1) {
12407                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12408                     } else {
12409                         var year = parseInt(html, 10) || 0;
12410                         this.viewDate.setUTCFullYear(year);
12411                         
12412                     }
12413                     this.showMode(-1);
12414                     this.fill();
12415                 }
12416                 break;
12417                 
12418             case 'td':
12419                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12420                     var day = parseInt(html, 10) || 1;
12421                     var year = this.viewDate.getUTCFullYear(),
12422                         month = this.viewDate.getUTCMonth();
12423
12424                     if (className.indexOf('old') !== -1) {
12425                         if(month === 0 ){
12426                             month = 11;
12427                             year -= 1;
12428                         }else{
12429                             month -= 1;
12430                         }
12431                     } else if (className.indexOf('new') !== -1) {
12432                         if (month == 11) {
12433                             month = 0;
12434                             year += 1;
12435                         } else {
12436                             month += 1;
12437                         }
12438                     }
12439                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12440                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12441                     this.fill();
12442                     this.setValue(this.formatDate(this.date));
12443                     this.hide();
12444                 }
12445                 break;
12446         }
12447     },
12448     
12449     setStartDate: function(startDate){
12450         this.startDate = startDate || -Infinity;
12451         if (this.startDate !== -Infinity) {
12452             this.startDate = this.parseDate(this.startDate);
12453         }
12454         this.update();
12455         this.updateNavArrows();
12456     },
12457
12458     setEndDate: function(endDate){
12459         this.endDate = endDate || Infinity;
12460         if (this.endDate !== Infinity) {
12461             this.endDate = this.parseDate(this.endDate);
12462         }
12463         this.update();
12464         this.updateNavArrows();
12465     },
12466     
12467     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12468         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12469         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12470             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12471         }
12472         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12473             return parseInt(d, 10);
12474         });
12475         this.update();
12476         this.updateNavArrows();
12477     },
12478     
12479     updateNavArrows: function() {
12480         var d = new Date(this.viewDate),
12481         year = d.getUTCFullYear(),
12482         month = d.getUTCMonth();
12483         
12484         Roo.each(this.picker().select('.prev', true).elements, function(v){
12485             v.show();
12486             switch (this.viewMode) {
12487                 case 0:
12488
12489                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12490                         v.hide();
12491                     }
12492                     break;
12493                 case 1:
12494                 case 2:
12495                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12496                         v.hide();
12497                     }
12498                     break;
12499             }
12500         });
12501         
12502         Roo.each(this.picker().select('.next', true).elements, function(v){
12503             v.show();
12504             switch (this.viewMode) {
12505                 case 0:
12506
12507                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12508                         v.hide();
12509                     }
12510                     break;
12511                 case 1:
12512                 case 2:
12513                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12514                         v.hide();
12515                     }
12516                     break;
12517             }
12518         })
12519     },
12520     
12521     moveMonth: function(date, dir){
12522         if (!dir) return date;
12523         var new_date = new Date(date.valueOf()),
12524         day = new_date.getUTCDate(),
12525         month = new_date.getUTCMonth(),
12526         mag = Math.abs(dir),
12527         new_month, test;
12528         dir = dir > 0 ? 1 : -1;
12529         if (mag == 1){
12530             test = dir == -1
12531             // If going back one month, make sure month is not current month
12532             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12533             ? function(){
12534                 return new_date.getUTCMonth() == month;
12535             }
12536             // If going forward one month, make sure month is as expected
12537             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12538             : function(){
12539                 return new_date.getUTCMonth() != new_month;
12540             };
12541             new_month = month + dir;
12542             new_date.setUTCMonth(new_month);
12543             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12544             if (new_month < 0 || new_month > 11)
12545                 new_month = (new_month + 12) % 12;
12546         } else {
12547             // For magnitudes >1, move one month at a time...
12548             for (var i=0; i<mag; i++)
12549                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12550                 new_date = this.moveMonth(new_date, dir);
12551             // ...then reset the day, keeping it in the new month
12552             new_month = new_date.getUTCMonth();
12553             new_date.setUTCDate(day);
12554             test = function(){
12555                 return new_month != new_date.getUTCMonth();
12556             };
12557         }
12558         // Common date-resetting loop -- if date is beyond end of month, make it
12559         // end of month
12560         while (test()){
12561             new_date.setUTCDate(--day);
12562             new_date.setUTCMonth(new_month);
12563         }
12564         return new_date;
12565     },
12566
12567     moveYear: function(date, dir){
12568         return this.moveMonth(date, dir*12);
12569     },
12570
12571     dateWithinRange: function(date){
12572         return date >= this.startDate && date <= this.endDate;
12573     },
12574
12575     
12576     remove: function() {
12577         this.picker().remove();
12578     }
12579    
12580 });
12581
12582 Roo.apply(Roo.bootstrap.DateField,  {
12583     
12584     head : {
12585         tag: 'thead',
12586         cn: [
12587         {
12588             tag: 'tr',
12589             cn: [
12590             {
12591                 tag: 'th',
12592                 cls: 'prev',
12593                 html: '<i class="icon-arrow-left"/>'
12594             },
12595             {
12596                 tag: 'th',
12597                 cls: 'switch',
12598                 colspan: '5'
12599             },
12600             {
12601                 tag: 'th',
12602                 cls: 'next',
12603                 html: '<i class="icon-arrow-right"/>'
12604             }
12605
12606             ]
12607         }
12608         ]
12609     },
12610     
12611     content : {
12612         tag: 'tbody',
12613         cn: [
12614         {
12615             tag: 'tr',
12616             cn: [
12617             {
12618                 tag: 'td',
12619                 colspan: '7'
12620             }
12621             ]
12622         }
12623         ]
12624     },
12625     
12626     footer : {
12627         tag: 'tfoot',
12628         cn: [
12629         {
12630             tag: 'tr',
12631             cn: [
12632             {
12633                 tag: 'th',
12634                 colspan: '7',
12635                 cls: 'today'
12636             }
12637                     
12638             ]
12639         }
12640         ]
12641     },
12642     
12643     dates:{
12644         en: {
12645             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12646             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12647             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12648             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12649             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12650             today: "Today"
12651         }
12652     },
12653     
12654     modes: [
12655     {
12656         clsName: 'days',
12657         navFnc: 'Month',
12658         navStep: 1
12659     },
12660     {
12661         clsName: 'months',
12662         navFnc: 'FullYear',
12663         navStep: 1
12664     },
12665     {
12666         clsName: 'years',
12667         navFnc: 'FullYear',
12668         navStep: 10
12669     }]
12670 });
12671
12672 Roo.apply(Roo.bootstrap.DateField,  {
12673   
12674     template : {
12675         tag: 'div',
12676         cls: 'datepicker dropdown-menu',
12677         cn: [
12678         {
12679             tag: 'div',
12680             cls: 'datepicker-days',
12681             cn: [
12682             {
12683                 tag: 'table',
12684                 cls: 'table-condensed',
12685                 cn:[
12686                 Roo.bootstrap.DateField.head,
12687                 {
12688                     tag: 'tbody'
12689                 },
12690                 Roo.bootstrap.DateField.footer
12691                 ]
12692             }
12693             ]
12694         },
12695         {
12696             tag: 'div',
12697             cls: 'datepicker-months',
12698             cn: [
12699             {
12700                 tag: 'table',
12701                 cls: 'table-condensed',
12702                 cn:[
12703                 Roo.bootstrap.DateField.head,
12704                 Roo.bootstrap.DateField.content,
12705                 Roo.bootstrap.DateField.footer
12706                 ]
12707             }
12708             ]
12709         },
12710         {
12711             tag: 'div',
12712             cls: 'datepicker-years',
12713             cn: [
12714             {
12715                 tag: 'table',
12716                 cls: 'table-condensed',
12717                 cn:[
12718                 Roo.bootstrap.DateField.head,
12719                 Roo.bootstrap.DateField.content,
12720                 Roo.bootstrap.DateField.footer
12721                 ]
12722             }
12723             ]
12724         }
12725         ]
12726     }
12727 });
12728
12729  
12730
12731  /*
12732  * - LGPL
12733  *
12734  * TimeField
12735  * 
12736  */
12737
12738 /**
12739  * @class Roo.bootstrap.TimeField
12740  * @extends Roo.bootstrap.Input
12741  * Bootstrap DateField class
12742  * 
12743  * 
12744  * @constructor
12745  * Create a new TimeField
12746  * @param {Object} config The config object
12747  */
12748
12749 Roo.bootstrap.TimeField = function(config){
12750     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12751     this.addEvents({
12752             /**
12753              * @event show
12754              * Fires when this field show.
12755              * @param {Roo.bootstrap.DateField} this
12756              * @param {Mixed} date The date value
12757              */
12758             show : true,
12759             /**
12760              * @event show
12761              * Fires when this field hide.
12762              * @param {Roo.bootstrap.DateField} this
12763              * @param {Mixed} date The date value
12764              */
12765             hide : true,
12766             /**
12767              * @event select
12768              * Fires when select a date.
12769              * @param {Roo.bootstrap.DateField} this
12770              * @param {Mixed} date The date value
12771              */
12772             select : true
12773         });
12774 };
12775
12776 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12777     
12778     /**
12779      * @cfg {String} format
12780      * The default time format string which can be overriden for localization support.  The format must be
12781      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12782      */
12783     format : "H:i",
12784        
12785     onRender: function(ct, position)
12786     {
12787         
12788         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12789                 
12790         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12791         
12792         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12793         
12794         this.pop = this.picker().select('>.datepicker-time',true).first();
12795         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12796         
12797         this.picker().on('mousedown', this.onMousedown, this);
12798         this.picker().on('click', this.onClick, this);
12799         
12800         this.picker().addClass('datepicker-dropdown');
12801     
12802         this.fillTime();
12803         this.update();
12804             
12805         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12806         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12807         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12808         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12809         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12810         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12811
12812     },
12813     
12814     fireKey: function(e){
12815         if (!this.picker().isVisible()){
12816             if (e.keyCode == 27) // allow escape to hide and re-show picker
12817                 this.show();
12818             return;
12819         }
12820
12821         e.preventDefault();
12822         
12823         switch(e.keyCode){
12824             case 27: // escape
12825                 this.hide();
12826                 break;
12827             case 37: // left
12828             case 39: // right
12829                 this.onTogglePeriod();
12830                 break;
12831             case 38: // up
12832                 this.onIncrementMinutes();
12833                 break;
12834             case 40: // down
12835                 this.onDecrementMinutes();
12836                 break;
12837             case 13: // enter
12838             case 9: // tab
12839                 this.setTime();
12840                 break;
12841         }
12842     },
12843     
12844     onClick: function(e) {
12845         e.stopPropagation();
12846         e.preventDefault();
12847     },
12848     
12849     picker : function()
12850     {
12851         return this.el.select('.datepicker', true).first();
12852     },
12853     
12854     fillTime: function()
12855     {    
12856         var time = this.pop.select('tbody', true).first();
12857         
12858         time.dom.innerHTML = '';
12859         
12860         time.createChild({
12861             tag: 'tr',
12862             cn: [
12863                 {
12864                     tag: 'td',
12865                     cn: [
12866                         {
12867                             tag: 'a',
12868                             href: '#',
12869                             cls: 'btn',
12870                             cn: [
12871                                 {
12872                                     tag: 'span',
12873                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12874                                 }
12875                             ]
12876                         } 
12877                     ]
12878                 },
12879                 {
12880                     tag: 'td',
12881                     cls: 'separator'
12882                 },
12883                 {
12884                     tag: 'td',
12885                     cn: [
12886                         {
12887                             tag: 'a',
12888                             href: '#',
12889                             cls: 'btn',
12890                             cn: [
12891                                 {
12892                                     tag: 'span',
12893                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12894                                 }
12895                             ]
12896                         }
12897                     ]
12898                 },
12899                 {
12900                     tag: 'td',
12901                     cls: 'separator'
12902                 }
12903             ]
12904         });
12905         
12906         time.createChild({
12907             tag: 'tr',
12908             cn: [
12909                 {
12910                     tag: 'td',
12911                     cn: [
12912                         {
12913                             tag: 'span',
12914                             cls: 'timepicker-hour',
12915                             html: '00'
12916                         }  
12917                     ]
12918                 },
12919                 {
12920                     tag: 'td',
12921                     cls: 'separator',
12922                     html: ':'
12923                 },
12924                 {
12925                     tag: 'td',
12926                     cn: [
12927                         {
12928                             tag: 'span',
12929                             cls: 'timepicker-minute',
12930                             html: '00'
12931                         }  
12932                     ]
12933                 },
12934                 {
12935                     tag: 'td',
12936                     cls: 'separator'
12937                 },
12938                 {
12939                     tag: 'td',
12940                     cn: [
12941                         {
12942                             tag: 'button',
12943                             type: 'button',
12944                             cls: 'btn btn-primary period',
12945                             html: 'AM'
12946                             
12947                         }
12948                     ]
12949                 }
12950             ]
12951         });
12952         
12953         time.createChild({
12954             tag: 'tr',
12955             cn: [
12956                 {
12957                     tag: 'td',
12958                     cn: [
12959                         {
12960                             tag: 'a',
12961                             href: '#',
12962                             cls: 'btn',
12963                             cn: [
12964                                 {
12965                                     tag: 'span',
12966                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12967                                 }
12968                             ]
12969                         }
12970                     ]
12971                 },
12972                 {
12973                     tag: 'td',
12974                     cls: 'separator'
12975                 },
12976                 {
12977                     tag: 'td',
12978                     cn: [
12979                         {
12980                             tag: 'a',
12981                             href: '#',
12982                             cls: 'btn',
12983                             cn: [
12984                                 {
12985                                     tag: 'span',
12986                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12987                                 }
12988                             ]
12989                         }
12990                     ]
12991                 },
12992                 {
12993                     tag: 'td',
12994                     cls: 'separator'
12995                 }
12996             ]
12997         });
12998         
12999     },
13000     
13001     update: function()
13002     {
13003         
13004         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13005         
13006         this.fill();
13007     },
13008     
13009     fill: function() 
13010     {
13011         var hours = this.time.getHours();
13012         var minutes = this.time.getMinutes();
13013         var period = 'AM';
13014         
13015         if(hours > 11){
13016             period = 'PM';
13017         }
13018         
13019         if(hours == 0){
13020             hours = 12;
13021         }
13022         
13023         
13024         if(hours > 12){
13025             hours = hours - 12;
13026         }
13027         
13028         if(hours < 10){
13029             hours = '0' + hours;
13030         }
13031         
13032         if(minutes < 10){
13033             minutes = '0' + minutes;
13034         }
13035         
13036         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13037         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13038         this.pop.select('button', true).first().dom.innerHTML = period;
13039         
13040     },
13041     
13042     place: function()
13043     {   
13044         this.picker().removeClass(['bottom', 'top']);
13045         
13046         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13047             /*
13048              * place to the top of element!
13049              *
13050              */
13051             
13052             this.picker().addClass('top');
13053             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13054             
13055             return;
13056         }
13057         
13058         this.picker().addClass('bottom');
13059         
13060         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13061     },
13062   
13063     onFocus : function()
13064     {
13065         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13066         this.show();
13067     },
13068     
13069     onBlur : function()
13070     {
13071         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13072         this.hide();
13073     },
13074     
13075     show : function()
13076     {
13077         this.picker().show();
13078         this.pop.show();
13079         this.update();
13080         this.place();
13081         
13082         this.fireEvent('show', this, this.date);
13083     },
13084     
13085     hide : function()
13086     {
13087         this.picker().hide();
13088         this.pop.hide();
13089         
13090         this.fireEvent('hide', this, this.date);
13091     },
13092     
13093     setTime : function()
13094     {
13095         this.hide();
13096         this.setValue(this.time.format(this.format));
13097         
13098         this.fireEvent('select', this, this.date);
13099         
13100         
13101     },
13102     
13103     onMousedown: function(e){
13104         e.stopPropagation();
13105         e.preventDefault();
13106     },
13107     
13108     onIncrementHours: function()
13109     {
13110         Roo.log('onIncrementHours');
13111         this.time = this.time.add(Date.HOUR, 1);
13112         this.update();
13113         
13114     },
13115     
13116     onDecrementHours: function()
13117     {
13118         Roo.log('onDecrementHours');
13119         this.time = this.time.add(Date.HOUR, -1);
13120         this.update();
13121     },
13122     
13123     onIncrementMinutes: function()
13124     {
13125         Roo.log('onIncrementMinutes');
13126         this.time = this.time.add(Date.MINUTE, 1);
13127         this.update();
13128     },
13129     
13130     onDecrementMinutes: function()
13131     {
13132         Roo.log('onDecrementMinutes');
13133         this.time = this.time.add(Date.MINUTE, -1);
13134         this.update();
13135     },
13136     
13137     onTogglePeriod: function()
13138     {
13139         Roo.log('onTogglePeriod');
13140         this.time = this.time.add(Date.HOUR, 12);
13141         this.update();
13142     }
13143     
13144    
13145 });
13146
13147 Roo.apply(Roo.bootstrap.TimeField,  {
13148     
13149     content : {
13150         tag: 'tbody',
13151         cn: [
13152             {
13153                 tag: 'tr',
13154                 cn: [
13155                 {
13156                     tag: 'td',
13157                     colspan: '7'
13158                 }
13159                 ]
13160             }
13161         ]
13162     },
13163     
13164     footer : {
13165         tag: 'tfoot',
13166         cn: [
13167             {
13168                 tag: 'tr',
13169                 cn: [
13170                 {
13171                     tag: 'th',
13172                     colspan: '7',
13173                     cls: '',
13174                     cn: [
13175                         {
13176                             tag: 'button',
13177                             cls: 'btn btn-info ok',
13178                             html: 'OK'
13179                         }
13180                     ]
13181                 }
13182
13183                 ]
13184             }
13185         ]
13186     }
13187 });
13188
13189 Roo.apply(Roo.bootstrap.TimeField,  {
13190   
13191     template : {
13192         tag: 'div',
13193         cls: 'datepicker dropdown-menu',
13194         cn: [
13195             {
13196                 tag: 'div',
13197                 cls: 'datepicker-time',
13198                 cn: [
13199                 {
13200                     tag: 'table',
13201                     cls: 'table-condensed',
13202                     cn:[
13203                     Roo.bootstrap.TimeField.content,
13204                     Roo.bootstrap.TimeField.footer
13205                     ]
13206                 }
13207                 ]
13208             }
13209         ]
13210     }
13211 });
13212
13213  
13214
13215  /*
13216  * - LGPL
13217  *
13218  * CheckBox
13219  * 
13220  */
13221
13222 /**
13223  * @class Roo.bootstrap.CheckBox
13224  * @extends Roo.bootstrap.Input
13225  * Bootstrap CheckBox class
13226  * 
13227  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13228  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13229  * @cfg {String} boxLabel The text that appears beside the checkbox
13230  * @cfg {Boolean} checked initnal the element
13231  * 
13232  * @constructor
13233  * Create a new CheckBox
13234  * @param {Object} config The config object
13235  */
13236
13237 Roo.bootstrap.CheckBox = function(config){
13238     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13239    
13240         this.addEvents({
13241             /**
13242             * @event check
13243             * Fires when the element is checked or unchecked.
13244             * @param {Roo.bootstrap.CheckBox} this This input
13245             * @param {Boolean} checked The new checked value
13246             */
13247            check : true
13248         });
13249 };
13250
13251 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13252     
13253     inputType: 'checkbox',
13254     inputValue: 1,
13255     valueOff: 0,
13256     boxLabel: false,
13257     checked: false,
13258     
13259     getAutoCreate : function()
13260     {
13261         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13262         
13263         var id = Roo.id();
13264         
13265         var cfg = {};
13266         
13267         cfg.cls = 'form-group' //input-group
13268         
13269         var input =  {
13270             tag: 'input',
13271             id : id,
13272             type : this.inputType,
13273             value : (!this.checked) ? this.valueOff : this.inputValue,
13274             cls : 'form-box',
13275             placeholder : this.placeholder || ''
13276             
13277         };
13278         
13279         if (this.disabled) {
13280             input.disabled=true;
13281         }
13282         
13283         if(this.checked){
13284             input.checked = this.checked;
13285         }
13286         
13287         if (this.name) {
13288             input.name = this.name;
13289         }
13290         
13291         if (this.size) {
13292             input.cls += ' input-' + this.size;
13293         }
13294         
13295         var settings=this;
13296         ['xs','sm','md','lg'].map(function(size){
13297             if (settings[size]) {
13298                 cfg.cls += ' col-' + size + '-' + settings[size];
13299             }
13300         });
13301         
13302         var inputblock = input;
13303         
13304         if (this.before || this.after) {
13305             
13306             inputblock = {
13307                 cls : 'input-group',
13308                 cn :  [] 
13309             };
13310             if (this.before) {
13311                 inputblock.cn.push({
13312                     tag :'span',
13313                     cls : 'input-group-addon',
13314                     html : this.before
13315                 });
13316             }
13317             inputblock.cn.push(input);
13318             if (this.after) {
13319                 inputblock.cn.push({
13320                     tag :'span',
13321                     cls : 'input-group-addon',
13322                     html : this.after
13323                 });
13324             }
13325             
13326         };
13327         
13328         if (align ==='left' && this.fieldLabel.length) {
13329                 Roo.log("left and has label");
13330                 cfg.cn = [
13331                     
13332                     {
13333                         tag: 'label',
13334                         'for' :  id,
13335                         cls : 'control-label col-md-' + this.labelWidth,
13336                         html : this.fieldLabel
13337                         
13338                     },
13339                     {
13340                         cls : "col-md-" + (12 - this.labelWidth), 
13341                         cn: [
13342                             inputblock
13343                         ]
13344                     }
13345                     
13346                 ];
13347         } else if ( this.fieldLabel.length) {
13348                 Roo.log(" label");
13349                 cfg.cn = [
13350                    
13351                     {
13352                         tag: this.boxLabel ? 'span' : 'label',
13353                         'for': id,
13354                         cls: 'control-label box-input-label',
13355                         //cls : 'input-group-addon',
13356                         html : this.fieldLabel
13357                         
13358                     },
13359                     
13360                     inputblock
13361                     
13362                 ];
13363
13364         } else {
13365             
13366                    Roo.log(" no label && no align");
13367                 cfg.cn = [
13368                     
13369                         inputblock
13370                     
13371                 ];
13372                 
13373                 
13374         };
13375         
13376         if(this.boxLabel){
13377             cfg.cn.push({
13378                 tag: 'label',
13379                 'for': id,
13380                 cls: 'box-label',
13381                 html: this.boxLabel
13382             })
13383         }
13384         
13385         return cfg;
13386         
13387     },
13388     
13389     /**
13390      * return the real input element.
13391      */
13392     inputEl: function ()
13393     {
13394         return this.el.select('input.form-box',true).first();
13395     },
13396     
13397     initEvents : function()
13398     {
13399 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13400         
13401         this.inputEl().on('click', this.onClick,  this);
13402         
13403     },
13404     
13405     onClick : function()
13406     {   
13407         this.setChecked(!this.checked);
13408     },
13409     
13410     setChecked : function(state,suppressEvent)
13411     {
13412         this.checked = state;
13413         
13414         this.inputEl().dom.checked = state;
13415         
13416         if(suppressEvent !== true){
13417             this.fireEvent('check', this, state);
13418         }
13419         
13420         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13421         
13422     },
13423     
13424     setValue : function(v,suppressEvent)
13425     {
13426         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13427     }
13428     
13429 });
13430
13431  
13432 /*
13433  * - LGPL
13434  *
13435  * Radio
13436  * 
13437  */
13438
13439 /**
13440  * @class Roo.bootstrap.Radio
13441  * @extends Roo.bootstrap.CheckBox
13442  * Bootstrap Radio class
13443
13444  * @constructor
13445  * Create a new Radio
13446  * @param {Object} config The config object
13447  */
13448
13449 Roo.bootstrap.Radio = function(config){
13450     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13451    
13452 };
13453
13454 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13455     
13456     inputType: 'radio',
13457     inputValue: '',
13458     valueOff: '',
13459     
13460     getAutoCreate : function()
13461     {
13462         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13463         
13464         var id = Roo.id();
13465         
13466         var cfg = {};
13467         
13468         cfg.cls = 'form-group' //input-group
13469         
13470         var input =  {
13471             tag: 'input',
13472             id : id,
13473             type : this.inputType,
13474             value : (!this.checked) ? this.valueOff : this.inputValue,
13475             cls : 'form-box',
13476             placeholder : this.placeholder || ''
13477             
13478         };
13479         
13480         if (this.disabled) {
13481             input.disabled=true;
13482         }
13483         
13484         if(this.checked){
13485             input.checked = this.checked;
13486         }
13487         
13488         if (this.name) {
13489             input.name = this.name;
13490         }
13491         
13492         if (this.size) {
13493             input.cls += ' input-' + this.size;
13494         }
13495         
13496         var settings=this;
13497         ['xs','sm','md','lg'].map(function(size){
13498             if (settings[size]) {
13499                 cfg.cls += ' col-' + size + '-' + settings[size];
13500             }
13501         });
13502         
13503         var inputblock = input;
13504         
13505         if (this.before || this.after) {
13506             
13507             inputblock = {
13508                 cls : 'input-group',
13509                 cn :  [] 
13510             };
13511             if (this.before) {
13512                 inputblock.cn.push({
13513                     tag :'span',
13514                     cls : 'input-group-addon',
13515                     html : this.before
13516                 });
13517             }
13518             inputblock.cn.push(input);
13519             if (this.after) {
13520                 inputblock.cn.push({
13521                     tag :'span',
13522                     cls : 'input-group-addon',
13523                     html : this.after
13524                 });
13525             }
13526             
13527         };
13528         
13529         if (align ==='left' && this.fieldLabel.length) {
13530                 Roo.log("left and has label");
13531                 cfg.cn = [
13532                     
13533                     {
13534                         tag: 'label',
13535                         'for' :  id,
13536                         cls : 'control-label col-md-' + this.labelWidth,
13537                         html : this.fieldLabel
13538                         
13539                     },
13540                     {
13541                         cls : "col-md-" + (12 - this.labelWidth), 
13542                         cn: [
13543                             inputblock
13544                         ]
13545                     }
13546                     
13547                 ];
13548         } else if ( this.fieldLabel.length) {
13549                 Roo.log(" label");
13550                  cfg.cn = [
13551                    
13552                     {
13553                         tag: 'label',
13554                         'for': id,
13555                         cls: 'control-label box-input-label',
13556                         //cls : 'input-group-addon',
13557                         html : this.fieldLabel
13558                         
13559                     },
13560                     
13561                     inputblock
13562                     
13563                 ];
13564
13565         } else {
13566             
13567                    Roo.log(" no label && no align");
13568                 cfg.cn = [
13569                     
13570                         inputblock
13571                     
13572                 ];
13573                 
13574                 
13575         };
13576         
13577         if(this.boxLabel){
13578             cfg.cn.push({
13579                 tag: 'label',
13580                 'for': id,
13581                 cls: 'box-label',
13582                 html: this.boxLabel
13583             })
13584         }
13585         
13586         return cfg;
13587         
13588     },
13589    
13590     onClick : function()
13591     {   
13592         this.setChecked(true);
13593     },
13594     
13595     setChecked : function(state,suppressEvent)
13596     {
13597         if(state){
13598             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13599                 v.dom.checked = false;
13600             });
13601         }
13602         
13603         this.checked = state;
13604         this.inputEl().dom.checked = state;
13605         
13606         if(suppressEvent !== true){
13607             this.fireEvent('check', this, state);
13608         }
13609         
13610         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13611         
13612     },
13613     
13614     getGroupValue : function()
13615     {
13616         var value = ''
13617         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13618             if(v.dom.checked == true){
13619                 value = v.dom.value;
13620             }
13621         });
13622         
13623         return value;
13624     },
13625     
13626     /**
13627      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13628      * @return {Mixed} value The field value
13629      */
13630     getValue : function(){
13631         return this.getGroupValue();
13632     }
13633     
13634 });
13635
13636  
13637 //<script type="text/javascript">
13638
13639 /*
13640  * Based  Ext JS Library 1.1.1
13641  * Copyright(c) 2006-2007, Ext JS, LLC.
13642  * LGPL
13643  *
13644  */
13645  
13646 /**
13647  * @class Roo.HtmlEditorCore
13648  * @extends Roo.Component
13649  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13650  *
13651  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13652  */
13653
13654 Roo.HtmlEditorCore = function(config){
13655     
13656     
13657     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13658     this.addEvents({
13659         /**
13660          * @event initialize
13661          * Fires when the editor is fully initialized (including the iframe)
13662          * @param {Roo.HtmlEditorCore} this
13663          */
13664         initialize: true,
13665         /**
13666          * @event activate
13667          * Fires when the editor is first receives the focus. Any insertion must wait
13668          * until after this event.
13669          * @param {Roo.HtmlEditorCore} this
13670          */
13671         activate: true,
13672          /**
13673          * @event beforesync
13674          * Fires before the textarea is updated with content from the editor iframe. Return false
13675          * to cancel the sync.
13676          * @param {Roo.HtmlEditorCore} this
13677          * @param {String} html
13678          */
13679         beforesync: true,
13680          /**
13681          * @event beforepush
13682          * Fires before the iframe editor is updated with content from the textarea. Return false
13683          * to cancel the push.
13684          * @param {Roo.HtmlEditorCore} this
13685          * @param {String} html
13686          */
13687         beforepush: true,
13688          /**
13689          * @event sync
13690          * Fires when the textarea is updated with content from the editor iframe.
13691          * @param {Roo.HtmlEditorCore} this
13692          * @param {String} html
13693          */
13694         sync: true,
13695          /**
13696          * @event push
13697          * Fires when the iframe editor is updated with content from the textarea.
13698          * @param {Roo.HtmlEditorCore} this
13699          * @param {String} html
13700          */
13701         push: true,
13702         
13703         /**
13704          * @event editorevent
13705          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13706          * @param {Roo.HtmlEditorCore} this
13707          */
13708         editorevent: true
13709     });
13710      
13711 };
13712
13713
13714 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13715
13716
13717      /**
13718      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13719      */
13720     
13721     owner : false,
13722     
13723      /**
13724      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13725      *                        Roo.resizable.
13726      */
13727     resizable : false,
13728      /**
13729      * @cfg {Number} height (in pixels)
13730      */   
13731     height: 300,
13732    /**
13733      * @cfg {Number} width (in pixels)
13734      */   
13735     width: 500,
13736     
13737     /**
13738      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13739      * 
13740      */
13741     stylesheets: false,
13742     
13743     // id of frame..
13744     frameId: false,
13745     
13746     // private properties
13747     validationEvent : false,
13748     deferHeight: true,
13749     initialized : false,
13750     activated : false,
13751     sourceEditMode : false,
13752     onFocus : Roo.emptyFn,
13753     iframePad:3,
13754     hideMode:'offsets',
13755     
13756     clearUp: true,
13757     
13758      
13759     
13760
13761     /**
13762      * Protected method that will not generally be called directly. It
13763      * is called when the editor initializes the iframe with HTML contents. Override this method if you
13764      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13765      */
13766     getDocMarkup : function(){
13767         // body styles..
13768         var st = '';
13769         Roo.log(this.stylesheets);
13770         
13771         // inherit styels from page...?? 
13772         if (this.stylesheets === false) {
13773             
13774             Roo.get(document.head).select('style').each(function(node) {
13775                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13776             });
13777             
13778             Roo.get(document.head).select('link').each(function(node) { 
13779                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13780             });
13781             
13782         } else if (!this.stylesheets.length) {
13783                 // simple..
13784                 st = '<style type="text/css">' +
13785                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13786                    '</style>';
13787         } else {
13788             Roo.each(this.stylesheets, function(s) {
13789                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13790             });
13791             
13792         }
13793         
13794         st +=  '<style type="text/css">' +
13795             'IMG { cursor: pointer } ' +
13796         '</style>';
13797
13798         
13799         return '<html><head>' + st  +
13800             //<style type="text/css">' +
13801             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13802             //'</style>' +
13803             ' </head><body class="roo-htmleditor-body"></body></html>';
13804     },
13805
13806     // private
13807     onRender : function(ct, position)
13808     {
13809         var _t = this;
13810         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13811         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13812         
13813         
13814         this.el.dom.style.border = '0 none';
13815         this.el.dom.setAttribute('tabIndex', -1);
13816         this.el.addClass('x-hidden hide');
13817         
13818         
13819         
13820         if(Roo.isIE){ // fix IE 1px bogus margin
13821             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13822         }
13823        
13824         
13825         this.frameId = Roo.id();
13826         
13827          
13828         
13829         var iframe = this.owner.wrap.createChild({
13830             tag: 'iframe',
13831             cls: 'form-control', // bootstrap..
13832             id: this.frameId,
13833             name: this.frameId,
13834             frameBorder : 'no',
13835             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13836         }, this.el
13837         );
13838         
13839         
13840         this.iframe = iframe.dom;
13841
13842          this.assignDocWin();
13843         
13844         this.doc.designMode = 'on';
13845        
13846         this.doc.open();
13847         this.doc.write(this.getDocMarkup());
13848         this.doc.close();
13849
13850         
13851         var task = { // must defer to wait for browser to be ready
13852             run : function(){
13853                 //console.log("run task?" + this.doc.readyState);
13854                 this.assignDocWin();
13855                 if(this.doc.body || this.doc.readyState == 'complete'){
13856                     try {
13857                         this.doc.designMode="on";
13858                     } catch (e) {
13859                         return;
13860                     }
13861                     Roo.TaskMgr.stop(task);
13862                     this.initEditor.defer(10, this);
13863                 }
13864             },
13865             interval : 10,
13866             duration: 10000,
13867             scope: this
13868         };
13869         Roo.TaskMgr.start(task);
13870
13871         
13872          
13873     },
13874
13875     // private
13876     onResize : function(w, h)
13877     {
13878          Roo.log('resize: ' +w + ',' + h );
13879         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13880         if(!this.iframe){
13881             return;
13882         }
13883         if(typeof w == 'number'){
13884             
13885             this.iframe.style.width = w + 'px';
13886         }
13887         if(typeof h == 'number'){
13888             
13889             this.iframe.style.height = h + 'px';
13890             if(this.doc){
13891                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13892             }
13893         }
13894         
13895     },
13896
13897     /**
13898      * Toggles the editor between standard and source edit mode.
13899      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13900      */
13901     toggleSourceEdit : function(sourceEditMode){
13902         
13903         this.sourceEditMode = sourceEditMode === true;
13904         
13905         if(this.sourceEditMode){
13906  
13907             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13908             
13909         }else{
13910             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13911             //this.iframe.className = '';
13912             this.deferFocus();
13913         }
13914         //this.setSize(this.owner.wrap.getSize());
13915         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13916     },
13917
13918     
13919   
13920
13921     /**
13922      * Protected method that will not generally be called directly. If you need/want
13923      * custom HTML cleanup, this is the method you should override.
13924      * @param {String} html The HTML to be cleaned
13925      * return {String} The cleaned HTML
13926      */
13927     cleanHtml : function(html){
13928         html = String(html);
13929         if(html.length > 5){
13930             if(Roo.isSafari){ // strip safari nonsense
13931                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13932             }
13933         }
13934         if(html == '&nbsp;'){
13935             html = '';
13936         }
13937         return html;
13938     },
13939
13940     /**
13941      * HTML Editor -> Textarea
13942      * Protected method that will not generally be called directly. Syncs the contents
13943      * of the editor iframe with the textarea.
13944      */
13945     syncValue : function(){
13946         if(this.initialized){
13947             var bd = (this.doc.body || this.doc.documentElement);
13948             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13949             var html = bd.innerHTML;
13950             if(Roo.isSafari){
13951                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13952                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13953                 if(m && m[1]){
13954                     html = '<div style="'+m[0]+'">' + html + '</div>';
13955                 }
13956             }
13957             html = this.cleanHtml(html);
13958             // fix up the special chars.. normaly like back quotes in word...
13959             // however we do not want to do this with chinese..
13960             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13961                 var cc = b.charCodeAt();
13962                 if (
13963                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13964                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13965                     (cc >= 0xf900 && cc < 0xfb00 )
13966                 ) {
13967                         return b;
13968                 }
13969                 return "&#"+cc+";" 
13970             });
13971             if(this.owner.fireEvent('beforesync', this, html) !== false){
13972                 this.el.dom.value = html;
13973                 this.owner.fireEvent('sync', this, html);
13974             }
13975         }
13976     },
13977
13978     /**
13979      * Protected method that will not generally be called directly. Pushes the value of the textarea
13980      * into the iframe editor.
13981      */
13982     pushValue : function(){
13983         if(this.initialized){
13984             var v = this.el.dom.value.trim();
13985             
13986 //            if(v.length < 1){
13987 //                v = '&#160;';
13988 //            }
13989             
13990             if(this.owner.fireEvent('beforepush', this, v) !== false){
13991                 var d = (this.doc.body || this.doc.documentElement);
13992                 d.innerHTML = v;
13993                 this.cleanUpPaste();
13994                 this.el.dom.value = d.innerHTML;
13995                 this.owner.fireEvent('push', this, v);
13996             }
13997         }
13998     },
13999
14000     // private
14001     deferFocus : function(){
14002         this.focus.defer(10, this);
14003     },
14004
14005     // doc'ed in Field
14006     focus : function(){
14007         if(this.win && !this.sourceEditMode){
14008             this.win.focus();
14009         }else{
14010             this.el.focus();
14011         }
14012     },
14013     
14014     assignDocWin: function()
14015     {
14016         var iframe = this.iframe;
14017         
14018          if(Roo.isIE){
14019             this.doc = iframe.contentWindow.document;
14020             this.win = iframe.contentWindow;
14021         } else {
14022             if (!Roo.get(this.frameId)) {
14023                 return;
14024             }
14025             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14026             this.win = Roo.get(this.frameId).dom.contentWindow;
14027         }
14028     },
14029     
14030     // private
14031     initEditor : function(){
14032         //console.log("INIT EDITOR");
14033         this.assignDocWin();
14034         
14035         
14036         
14037         this.doc.designMode="on";
14038         this.doc.open();
14039         this.doc.write(this.getDocMarkup());
14040         this.doc.close();
14041         
14042         var dbody = (this.doc.body || this.doc.documentElement);
14043         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14044         // this copies styles from the containing element into thsi one..
14045         // not sure why we need all of this..
14046         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14047         ss['background-attachment'] = 'fixed'; // w3c
14048         dbody.bgProperties = 'fixed'; // ie
14049         Roo.DomHelper.applyStyles(dbody, ss);
14050         Roo.EventManager.on(this.doc, {
14051             //'mousedown': this.onEditorEvent,
14052             'mouseup': this.onEditorEvent,
14053             'dblclick': this.onEditorEvent,
14054             'click': this.onEditorEvent,
14055             'keyup': this.onEditorEvent,
14056             buffer:100,
14057             scope: this
14058         });
14059         if(Roo.isGecko){
14060             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14061         }
14062         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14063             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14064         }
14065         this.initialized = true;
14066
14067         this.owner.fireEvent('initialize', this);
14068         this.pushValue();
14069     },
14070
14071     // private
14072     onDestroy : function(){
14073         
14074         
14075         
14076         if(this.rendered){
14077             
14078             //for (var i =0; i < this.toolbars.length;i++) {
14079             //    // fixme - ask toolbars for heights?
14080             //    this.toolbars[i].onDestroy();
14081            // }
14082             
14083             //this.wrap.dom.innerHTML = '';
14084             //this.wrap.remove();
14085         }
14086     },
14087
14088     // private
14089     onFirstFocus : function(){
14090         
14091         this.assignDocWin();
14092         
14093         
14094         this.activated = true;
14095          
14096     
14097         if(Roo.isGecko){ // prevent silly gecko errors
14098             this.win.focus();
14099             var s = this.win.getSelection();
14100             if(!s.focusNode || s.focusNode.nodeType != 3){
14101                 var r = s.getRangeAt(0);
14102                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14103                 r.collapse(true);
14104                 this.deferFocus();
14105             }
14106             try{
14107                 this.execCmd('useCSS', true);
14108                 this.execCmd('styleWithCSS', false);
14109             }catch(e){}
14110         }
14111         this.owner.fireEvent('activate', this);
14112     },
14113
14114     // private
14115     adjustFont: function(btn){
14116         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14117         //if(Roo.isSafari){ // safari
14118         //    adjust *= 2;
14119        // }
14120         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14121         if(Roo.isSafari){ // safari
14122             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14123             v =  (v < 10) ? 10 : v;
14124             v =  (v > 48) ? 48 : v;
14125             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14126             
14127         }
14128         
14129         
14130         v = Math.max(1, v+adjust);
14131         
14132         this.execCmd('FontSize', v  );
14133     },
14134
14135     onEditorEvent : function(e){
14136         this.owner.fireEvent('editorevent', this, e);
14137       //  this.updateToolbar();
14138         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14139     },
14140
14141     insertTag : function(tg)
14142     {
14143         // could be a bit smarter... -> wrap the current selected tRoo..
14144         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14145             
14146             range = this.createRange(this.getSelection());
14147             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14148             wrappingNode.appendChild(range.extractContents());
14149             range.insertNode(wrappingNode);
14150
14151             return;
14152             
14153             
14154             
14155         }
14156         this.execCmd("formatblock",   tg);
14157         
14158     },
14159     
14160     insertText : function(txt)
14161     {
14162         
14163         
14164         var range = this.createRange();
14165         range.deleteContents();
14166                //alert(Sender.getAttribute('label'));
14167                
14168         range.insertNode(this.doc.createTextNode(txt));
14169     } ,
14170     
14171      
14172
14173     /**
14174      * Executes a Midas editor command on the editor document and performs necessary focus and
14175      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14176      * @param {String} cmd The Midas command
14177      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14178      */
14179     relayCmd : function(cmd, value){
14180         this.win.focus();
14181         this.execCmd(cmd, value);
14182         this.owner.fireEvent('editorevent', this);
14183         //this.updateToolbar();
14184         this.owner.deferFocus();
14185     },
14186
14187     /**
14188      * Executes a Midas editor command directly on the editor document.
14189      * For visual commands, you should use {@link #relayCmd} instead.
14190      * <b>This should only be called after the editor is initialized.</b>
14191      * @param {String} cmd The Midas command
14192      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14193      */
14194     execCmd : function(cmd, value){
14195         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14196         this.syncValue();
14197     },
14198  
14199  
14200    
14201     /**
14202      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14203      * to insert tRoo.
14204      * @param {String} text | dom node.. 
14205      */
14206     insertAtCursor : function(text)
14207     {
14208         
14209         
14210         
14211         if(!this.activated){
14212             return;
14213         }
14214         /*
14215         if(Roo.isIE){
14216             this.win.focus();
14217             var r = this.doc.selection.createRange();
14218             if(r){
14219                 r.collapse(true);
14220                 r.pasteHTML(text);
14221                 this.syncValue();
14222                 this.deferFocus();
14223             
14224             }
14225             return;
14226         }
14227         */
14228         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14229             this.win.focus();
14230             
14231             
14232             // from jquery ui (MIT licenced)
14233             var range, node;
14234             var win = this.win;
14235             
14236             if (win.getSelection && win.getSelection().getRangeAt) {
14237                 range = win.getSelection().getRangeAt(0);
14238                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14239                 range.insertNode(node);
14240             } else if (win.document.selection && win.document.selection.createRange) {
14241                 // no firefox support
14242                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14243                 win.document.selection.createRange().pasteHTML(txt);
14244             } else {
14245                 // no firefox support
14246                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14247                 this.execCmd('InsertHTML', txt);
14248             } 
14249             
14250             this.syncValue();
14251             
14252             this.deferFocus();
14253         }
14254     },
14255  // private
14256     mozKeyPress : function(e){
14257         if(e.ctrlKey){
14258             var c = e.getCharCode(), cmd;
14259           
14260             if(c > 0){
14261                 c = String.fromCharCode(c).toLowerCase();
14262                 switch(c){
14263                     case 'b':
14264                         cmd = 'bold';
14265                         break;
14266                     case 'i':
14267                         cmd = 'italic';
14268                         break;
14269                     
14270                     case 'u':
14271                         cmd = 'underline';
14272                         break;
14273                     
14274                     case 'v':
14275                         this.cleanUpPaste.defer(100, this);
14276                         return;
14277                         
14278                 }
14279                 if(cmd){
14280                     this.win.focus();
14281                     this.execCmd(cmd);
14282                     this.deferFocus();
14283                     e.preventDefault();
14284                 }
14285                 
14286             }
14287         }
14288     },
14289
14290     // private
14291     fixKeys : function(){ // load time branching for fastest keydown performance
14292         if(Roo.isIE){
14293             return function(e){
14294                 var k = e.getKey(), r;
14295                 if(k == e.TAB){
14296                     e.stopEvent();
14297                     r = this.doc.selection.createRange();
14298                     if(r){
14299                         r.collapse(true);
14300                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14301                         this.deferFocus();
14302                     }
14303                     return;
14304                 }
14305                 
14306                 if(k == e.ENTER){
14307                     r = this.doc.selection.createRange();
14308                     if(r){
14309                         var target = r.parentElement();
14310                         if(!target || target.tagName.toLowerCase() != 'li'){
14311                             e.stopEvent();
14312                             r.pasteHTML('<br />');
14313                             r.collapse(false);
14314                             r.select();
14315                         }
14316                     }
14317                 }
14318                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14319                     this.cleanUpPaste.defer(100, this);
14320                     return;
14321                 }
14322                 
14323                 
14324             };
14325         }else if(Roo.isOpera){
14326             return function(e){
14327                 var k = e.getKey();
14328                 if(k == e.TAB){
14329                     e.stopEvent();
14330                     this.win.focus();
14331                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14332                     this.deferFocus();
14333                 }
14334                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14335                     this.cleanUpPaste.defer(100, this);
14336                     return;
14337                 }
14338                 
14339             };
14340         }else if(Roo.isSafari){
14341             return function(e){
14342                 var k = e.getKey();
14343                 
14344                 if(k == e.TAB){
14345                     e.stopEvent();
14346                     this.execCmd('InsertText','\t');
14347                     this.deferFocus();
14348                     return;
14349                 }
14350                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14351                     this.cleanUpPaste.defer(100, this);
14352                     return;
14353                 }
14354                 
14355              };
14356         }
14357     }(),
14358     
14359     getAllAncestors: function()
14360     {
14361         var p = this.getSelectedNode();
14362         var a = [];
14363         if (!p) {
14364             a.push(p); // push blank onto stack..
14365             p = this.getParentElement();
14366         }
14367         
14368         
14369         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14370             a.push(p);
14371             p = p.parentNode;
14372         }
14373         a.push(this.doc.body);
14374         return a;
14375     },
14376     lastSel : false,
14377     lastSelNode : false,
14378     
14379     
14380     getSelection : function() 
14381     {
14382         this.assignDocWin();
14383         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14384     },
14385     
14386     getSelectedNode: function() 
14387     {
14388         // this may only work on Gecko!!!
14389         
14390         // should we cache this!!!!
14391         
14392         
14393         
14394          
14395         var range = this.createRange(this.getSelection()).cloneRange();
14396         
14397         if (Roo.isIE) {
14398             var parent = range.parentElement();
14399             while (true) {
14400                 var testRange = range.duplicate();
14401                 testRange.moveToElementText(parent);
14402                 if (testRange.inRange(range)) {
14403                     break;
14404                 }
14405                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14406                     break;
14407                 }
14408                 parent = parent.parentElement;
14409             }
14410             return parent;
14411         }
14412         
14413         // is ancestor a text element.
14414         var ac =  range.commonAncestorContainer;
14415         if (ac.nodeType == 3) {
14416             ac = ac.parentNode;
14417         }
14418         
14419         var ar = ac.childNodes;
14420          
14421         var nodes = [];
14422         var other_nodes = [];
14423         var has_other_nodes = false;
14424         for (var i=0;i<ar.length;i++) {
14425             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14426                 continue;
14427             }
14428             // fullly contained node.
14429             
14430             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14431                 nodes.push(ar[i]);
14432                 continue;
14433             }
14434             
14435             // probably selected..
14436             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14437                 other_nodes.push(ar[i]);
14438                 continue;
14439             }
14440             // outer..
14441             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14442                 continue;
14443             }
14444             
14445             
14446             has_other_nodes = true;
14447         }
14448         if (!nodes.length && other_nodes.length) {
14449             nodes= other_nodes;
14450         }
14451         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14452             return false;
14453         }
14454         
14455         return nodes[0];
14456     },
14457     createRange: function(sel)
14458     {
14459         // this has strange effects when using with 
14460         // top toolbar - not sure if it's a great idea.
14461         //this.editor.contentWindow.focus();
14462         if (typeof sel != "undefined") {
14463             try {
14464                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14465             } catch(e) {
14466                 return this.doc.createRange();
14467             }
14468         } else {
14469             return this.doc.createRange();
14470         }
14471     },
14472     getParentElement: function()
14473     {
14474         
14475         this.assignDocWin();
14476         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14477         
14478         var range = this.createRange(sel);
14479          
14480         try {
14481             var p = range.commonAncestorContainer;
14482             while (p.nodeType == 3) { // text node
14483                 p = p.parentNode;
14484             }
14485             return p;
14486         } catch (e) {
14487             return null;
14488         }
14489     
14490     },
14491     /***
14492      *
14493      * Range intersection.. the hard stuff...
14494      *  '-1' = before
14495      *  '0' = hits..
14496      *  '1' = after.
14497      *         [ -- selected range --- ]
14498      *   [fail]                        [fail]
14499      *
14500      *    basically..
14501      *      if end is before start or  hits it. fail.
14502      *      if start is after end or hits it fail.
14503      *
14504      *   if either hits (but other is outside. - then it's not 
14505      *   
14506      *    
14507      **/
14508     
14509     
14510     // @see http://www.thismuchiknow.co.uk/?p=64.
14511     rangeIntersectsNode : function(range, node)
14512     {
14513         var nodeRange = node.ownerDocument.createRange();
14514         try {
14515             nodeRange.selectNode(node);
14516         } catch (e) {
14517             nodeRange.selectNodeContents(node);
14518         }
14519     
14520         var rangeStartRange = range.cloneRange();
14521         rangeStartRange.collapse(true);
14522     
14523         var rangeEndRange = range.cloneRange();
14524         rangeEndRange.collapse(false);
14525     
14526         var nodeStartRange = nodeRange.cloneRange();
14527         nodeStartRange.collapse(true);
14528     
14529         var nodeEndRange = nodeRange.cloneRange();
14530         nodeEndRange.collapse(false);
14531     
14532         return rangeStartRange.compareBoundaryPoints(
14533                  Range.START_TO_START, nodeEndRange) == -1 &&
14534                rangeEndRange.compareBoundaryPoints(
14535                  Range.START_TO_START, nodeStartRange) == 1;
14536         
14537          
14538     },
14539     rangeCompareNode : function(range, node)
14540     {
14541         var nodeRange = node.ownerDocument.createRange();
14542         try {
14543             nodeRange.selectNode(node);
14544         } catch (e) {
14545             nodeRange.selectNodeContents(node);
14546         }
14547         
14548         
14549         range.collapse(true);
14550     
14551         nodeRange.collapse(true);
14552      
14553         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14554         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
14555          
14556         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14557         
14558         var nodeIsBefore   =  ss == 1;
14559         var nodeIsAfter    = ee == -1;
14560         
14561         if (nodeIsBefore && nodeIsAfter)
14562             return 0; // outer
14563         if (!nodeIsBefore && nodeIsAfter)
14564             return 1; //right trailed.
14565         
14566         if (nodeIsBefore && !nodeIsAfter)
14567             return 2;  // left trailed.
14568         // fully contined.
14569         return 3;
14570     },
14571
14572     // private? - in a new class?
14573     cleanUpPaste :  function()
14574     {
14575         // cleans up the whole document..
14576         Roo.log('cleanuppaste');
14577         
14578         this.cleanUpChildren(this.doc.body);
14579         var clean = this.cleanWordChars(this.doc.body.innerHTML);
14580         if (clean != this.doc.body.innerHTML) {
14581             this.doc.body.innerHTML = clean;
14582         }
14583         
14584     },
14585     
14586     cleanWordChars : function(input) {// change the chars to hex code
14587         var he = Roo.HtmlEditorCore;
14588         
14589         var output = input;
14590         Roo.each(he.swapCodes, function(sw) { 
14591             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14592             
14593             output = output.replace(swapper, sw[1]);
14594         });
14595         
14596         return output;
14597     },
14598     
14599     
14600     cleanUpChildren : function (n)
14601     {
14602         if (!n.childNodes.length) {
14603             return;
14604         }
14605         for (var i = n.childNodes.length-1; i > -1 ; i--) {
14606            this.cleanUpChild(n.childNodes[i]);
14607         }
14608     },
14609     
14610     
14611         
14612     
14613     cleanUpChild : function (node)
14614     {
14615         var ed = this;
14616         //console.log(node);
14617         if (node.nodeName == "#text") {
14618             // clean up silly Windows -- stuff?
14619             return; 
14620         }
14621         if (node.nodeName == "#comment") {
14622             node.parentNode.removeChild(node);
14623             // clean up silly Windows -- stuff?
14624             return; 
14625         }
14626         
14627         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14628             // remove node.
14629             node.parentNode.removeChild(node);
14630             return;
14631             
14632         }
14633         
14634         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14635         
14636         // remove <a name=....> as rendering on yahoo mailer is borked with this.
14637         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14638         
14639         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14640         //    remove_keep_children = true;
14641         //}
14642         
14643         if (remove_keep_children) {
14644             this.cleanUpChildren(node);
14645             // inserts everything just before this node...
14646             while (node.childNodes.length) {
14647                 var cn = node.childNodes[0];
14648                 node.removeChild(cn);
14649                 node.parentNode.insertBefore(cn, node);
14650             }
14651             node.parentNode.removeChild(node);
14652             return;
14653         }
14654         
14655         if (!node.attributes || !node.attributes.length) {
14656             this.cleanUpChildren(node);
14657             return;
14658         }
14659         
14660         function cleanAttr(n,v)
14661         {
14662             
14663             if (v.match(/^\./) || v.match(/^\//)) {
14664                 return;
14665             }
14666             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14667                 return;
14668             }
14669             if (v.match(/^#/)) {
14670                 return;
14671             }
14672 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14673             node.removeAttribute(n);
14674             
14675         }
14676         
14677         function cleanStyle(n,v)
14678         {
14679             if (v.match(/expression/)) { //XSS?? should we even bother..
14680                 node.removeAttribute(n);
14681                 return;
14682             }
14683             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14684             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14685             
14686             
14687             var parts = v.split(/;/);
14688             var clean = [];
14689             
14690             Roo.each(parts, function(p) {
14691                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14692                 if (!p.length) {
14693                     return true;
14694                 }
14695                 var l = p.split(':').shift().replace(/\s+/g,'');
14696                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14697                 
14698                 if ( cblack.indexOf(l) > -1) {
14699 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14700                     //node.removeAttribute(n);
14701                     return true;
14702                 }
14703                 //Roo.log()
14704                 // only allow 'c whitelisted system attributes'
14705                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14706 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14707                     //node.removeAttribute(n);
14708                     return true;
14709                 }
14710                 
14711                 
14712                  
14713                 
14714                 clean.push(p);
14715                 return true;
14716             });
14717             if (clean.length) { 
14718                 node.setAttribute(n, clean.join(';'));
14719             } else {
14720                 node.removeAttribute(n);
14721             }
14722             
14723         }
14724         
14725         
14726         for (var i = node.attributes.length-1; i > -1 ; i--) {
14727             var a = node.attributes[i];
14728             //console.log(a);
14729             
14730             if (a.name.toLowerCase().substr(0,2)=='on')  {
14731                 node.removeAttribute(a.name);
14732                 continue;
14733             }
14734             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14735                 node.removeAttribute(a.name);
14736                 continue;
14737             }
14738             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14739                 cleanAttr(a.name,a.value); // fixme..
14740                 continue;
14741             }
14742             if (a.name == 'style') {
14743                 cleanStyle(a.name,a.value);
14744                 continue;
14745             }
14746             /// clean up MS crap..
14747             // tecnically this should be a list of valid class'es..
14748             
14749             
14750             if (a.name == 'class') {
14751                 if (a.value.match(/^Mso/)) {
14752                     node.className = '';
14753                 }
14754                 
14755                 if (a.value.match(/body/)) {
14756                     node.className = '';
14757                 }
14758                 continue;
14759             }
14760             
14761             // style cleanup!?
14762             // class cleanup?
14763             
14764         }
14765         
14766         
14767         this.cleanUpChildren(node);
14768         
14769         
14770     }
14771     
14772     
14773     // hide stuff that is not compatible
14774     /**
14775      * @event blur
14776      * @hide
14777      */
14778     /**
14779      * @event change
14780      * @hide
14781      */
14782     /**
14783      * @event focus
14784      * @hide
14785      */
14786     /**
14787      * @event specialkey
14788      * @hide
14789      */
14790     /**
14791      * @cfg {String} fieldClass @hide
14792      */
14793     /**
14794      * @cfg {String} focusClass @hide
14795      */
14796     /**
14797      * @cfg {String} autoCreate @hide
14798      */
14799     /**
14800      * @cfg {String} inputType @hide
14801      */
14802     /**
14803      * @cfg {String} invalidClass @hide
14804      */
14805     /**
14806      * @cfg {String} invalidText @hide
14807      */
14808     /**
14809      * @cfg {String} msgFx @hide
14810      */
14811     /**
14812      * @cfg {String} validateOnBlur @hide
14813      */
14814 });
14815
14816 Roo.HtmlEditorCore.white = [
14817         'area', 'br', 'img', 'input', 'hr', 'wbr',
14818         
14819        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14820        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14821        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14822        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14823        'table',   'ul',         'xmp', 
14824        
14825        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14826       'thead',   'tr', 
14827      
14828       'dir', 'menu', 'ol', 'ul', 'dl',
14829        
14830       'embed',  'object'
14831 ];
14832
14833
14834 Roo.HtmlEditorCore.black = [
14835     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14836         'applet', // 
14837         'base',   'basefont', 'bgsound', 'blink',  'body', 
14838         'frame',  'frameset', 'head',    'html',   'ilayer', 
14839         'iframe', 'layer',  'link',     'meta',    'object',   
14840         'script', 'style' ,'title',  'xml' // clean later..
14841 ];
14842 Roo.HtmlEditorCore.clean = [
14843     'script', 'style', 'title', 'xml'
14844 ];
14845 Roo.HtmlEditorCore.remove = [
14846     'font'
14847 ];
14848 // attributes..
14849
14850 Roo.HtmlEditorCore.ablack = [
14851     'on'
14852 ];
14853     
14854 Roo.HtmlEditorCore.aclean = [ 
14855     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14856 ];
14857
14858 // protocols..
14859 Roo.HtmlEditorCore.pwhite= [
14860         'http',  'https',  'mailto'
14861 ];
14862
14863 // white listed style attributes.
14864 Roo.HtmlEditorCore.cwhite= [
14865       //  'text-align', /// default is to allow most things..
14866       
14867          
14868 //        'font-size'//??
14869 ];
14870
14871 // black listed style attributes.
14872 Roo.HtmlEditorCore.cblack= [
14873       //  'font-size' -- this can be set by the project 
14874 ];
14875
14876
14877 Roo.HtmlEditorCore.swapCodes   =[ 
14878     [    8211, "--" ], 
14879     [    8212, "--" ], 
14880     [    8216,  "'" ],  
14881     [    8217, "'" ],  
14882     [    8220, '"' ],  
14883     [    8221, '"' ],  
14884     [    8226, "*" ],  
14885     [    8230, "..." ]
14886 ]; 
14887
14888     /*
14889  * - LGPL
14890  *
14891  * HtmlEditor
14892  * 
14893  */
14894
14895 /**
14896  * @class Roo.bootstrap.HtmlEditor
14897  * @extends Roo.bootstrap.TextArea
14898  * Bootstrap HtmlEditor class
14899
14900  * @constructor
14901  * Create a new HtmlEditor
14902  * @param {Object} config The config object
14903  */
14904
14905 Roo.bootstrap.HtmlEditor = function(config){
14906     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14907     if (!this.toolbars) {
14908         this.toolbars = [];
14909     }
14910     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14911     this.addEvents({
14912             /**
14913              * @event initialize
14914              * Fires when the editor is fully initialized (including the iframe)
14915              * @param {HtmlEditor} this
14916              */
14917             initialize: true,
14918             /**
14919              * @event activate
14920              * Fires when the editor is first receives the focus. Any insertion must wait
14921              * until after this event.
14922              * @param {HtmlEditor} this
14923              */
14924             activate: true,
14925              /**
14926              * @event beforesync
14927              * Fires before the textarea is updated with content from the editor iframe. Return false
14928              * to cancel the sync.
14929              * @param {HtmlEditor} this
14930              * @param {String} html
14931              */
14932             beforesync: true,
14933              /**
14934              * @event beforepush
14935              * Fires before the iframe editor is updated with content from the textarea. Return false
14936              * to cancel the push.
14937              * @param {HtmlEditor} this
14938              * @param {String} html
14939              */
14940             beforepush: true,
14941              /**
14942              * @event sync
14943              * Fires when the textarea is updated with content from the editor iframe.
14944              * @param {HtmlEditor} this
14945              * @param {String} html
14946              */
14947             sync: true,
14948              /**
14949              * @event push
14950              * Fires when the iframe editor is updated with content from the textarea.
14951              * @param {HtmlEditor} this
14952              * @param {String} html
14953              */
14954             push: true,
14955              /**
14956              * @event editmodechange
14957              * Fires when the editor switches edit modes
14958              * @param {HtmlEditor} this
14959              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14960              */
14961             editmodechange: true,
14962             /**
14963              * @event editorevent
14964              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14965              * @param {HtmlEditor} this
14966              */
14967             editorevent: true,
14968             /**
14969              * @event firstfocus
14970              * Fires when on first focus - needed by toolbars..
14971              * @param {HtmlEditor} this
14972              */
14973             firstfocus: true,
14974             /**
14975              * @event autosave
14976              * Auto save the htmlEditor value as a file into Events
14977              * @param {HtmlEditor} this
14978              */
14979             autosave: true,
14980             /**
14981              * @event savedpreview
14982              * preview the saved version of htmlEditor
14983              * @param {HtmlEditor} this
14984              */
14985             savedpreview: true
14986         });
14987 };
14988
14989
14990 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14991     
14992     
14993       /**
14994      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14995      */
14996     toolbars : false,
14997    
14998      /**
14999      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15000      *                        Roo.resizable.
15001      */
15002     resizable : false,
15003      /**
15004      * @cfg {Number} height (in pixels)
15005      */   
15006     height: 300,
15007    /**
15008      * @cfg {Number} width (in pixels)
15009      */   
15010     width: false,
15011     
15012     /**
15013      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15014      * 
15015      */
15016     stylesheets: false,
15017     
15018     // id of frame..
15019     frameId: false,
15020     
15021     // private properties
15022     validationEvent : false,
15023     deferHeight: true,
15024     initialized : false,
15025     activated : false,
15026     
15027     onFocus : Roo.emptyFn,
15028     iframePad:3,
15029     hideMode:'offsets',
15030     
15031     
15032     tbContainer : false,
15033     
15034     toolbarContainer :function() {
15035         return this.wrap.select('.x-html-editor-tb',true).first();
15036     },
15037
15038     /**
15039      * Protected method that will not generally be called directly. It
15040      * is called when the editor creates its toolbar. Override this method if you need to
15041      * add custom toolbar buttons.
15042      * @param {HtmlEditor} editor
15043      */
15044     createToolbar : function(){
15045         
15046         Roo.log("create toolbars");
15047         
15048         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15049         this.toolbars[0].render(this.toolbarContainer());
15050         
15051         return;
15052         
15053 //        if (!editor.toolbars || !editor.toolbars.length) {
15054 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15055 //        }
15056 //        
15057 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15058 //            editor.toolbars[i] = Roo.factory(
15059 //                    typeof(editor.toolbars[i]) == 'string' ?
15060 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15061 //                Roo.bootstrap.HtmlEditor);
15062 //            editor.toolbars[i].init(editor);
15063 //        }
15064     },
15065
15066      
15067     // private
15068     onRender : function(ct, position)
15069     {
15070        // Roo.log("Call onRender: " + this.xtype);
15071         var _t = this;
15072         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15073       
15074         this.wrap = this.inputEl().wrap({
15075             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15076         });
15077         
15078         this.editorcore.onRender(ct, position);
15079          
15080         if (this.resizable) {
15081             this.resizeEl = new Roo.Resizable(this.wrap, {
15082                 pinned : true,
15083                 wrap: true,
15084                 dynamic : true,
15085                 minHeight : this.height,
15086                 height: this.height,
15087                 handles : this.resizable,
15088                 width: this.width,
15089                 listeners : {
15090                     resize : function(r, w, h) {
15091                         _t.onResize(w,h); // -something
15092                     }
15093                 }
15094             });
15095             
15096         }
15097         this.createToolbar(this);
15098        
15099         
15100         if(!this.width && this.resizable){
15101             this.setSize(this.wrap.getSize());
15102         }
15103         if (this.resizeEl) {
15104             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15105             // should trigger onReize..
15106         }
15107         
15108     },
15109
15110     // private
15111     onResize : function(w, h)
15112     {
15113         Roo.log('resize: ' +w + ',' + h );
15114         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15115         var ew = false;
15116         var eh = false;
15117         
15118         if(this.inputEl() ){
15119             if(typeof w == 'number'){
15120                 var aw = w - this.wrap.getFrameWidth('lr');
15121                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15122                 ew = aw;
15123             }
15124             if(typeof h == 'number'){
15125                  var tbh = -11;  // fixme it needs to tool bar size!
15126                 for (var i =0; i < this.toolbars.length;i++) {
15127                     // fixme - ask toolbars for heights?
15128                     tbh += this.toolbars[i].el.getHeight();
15129                     //if (this.toolbars[i].footer) {
15130                     //    tbh += this.toolbars[i].footer.el.getHeight();
15131                     //}
15132                 }
15133               
15134                 
15135                 
15136                 
15137                 
15138                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15139                 ah -= 5; // knock a few pixes off for look..
15140                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15141                 var eh = ah;
15142             }
15143         }
15144         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15145         this.editorcore.onResize(ew,eh);
15146         
15147     },
15148
15149     /**
15150      * Toggles the editor between standard and source edit mode.
15151      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15152      */
15153     toggleSourceEdit : function(sourceEditMode)
15154     {
15155         this.editorcore.toggleSourceEdit(sourceEditMode);
15156         
15157         if(this.editorcore.sourceEditMode){
15158             Roo.log('editor - showing textarea');
15159             
15160 //            Roo.log('in');
15161 //            Roo.log(this.syncValue());
15162             this.syncValue();
15163             this.inputEl().removeClass('hide');
15164             this.inputEl().dom.removeAttribute('tabIndex');
15165             this.inputEl().focus();
15166         }else{
15167             Roo.log('editor - hiding textarea');
15168 //            Roo.log('out')
15169 //            Roo.log(this.pushValue()); 
15170             this.pushValue();
15171             
15172             this.inputEl().addClass('hide');
15173             this.inputEl().dom.setAttribute('tabIndex', -1);
15174             //this.deferFocus();
15175         }
15176          
15177         if(this.resizable){
15178             this.setSize(this.wrap.getSize());
15179         }
15180         
15181         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15182     },
15183  
15184     // private (for BoxComponent)
15185     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15186
15187     // private (for BoxComponent)
15188     getResizeEl : function(){
15189         return this.wrap;
15190     },
15191
15192     // private (for BoxComponent)
15193     getPositionEl : function(){
15194         return this.wrap;
15195     },
15196
15197     // private
15198     initEvents : function(){
15199         this.originalValue = this.getValue();
15200     },
15201
15202 //    /**
15203 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15204 //     * @method
15205 //     */
15206 //    markInvalid : Roo.emptyFn,
15207 //    /**
15208 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15209 //     * @method
15210 //     */
15211 //    clearInvalid : Roo.emptyFn,
15212
15213     setValue : function(v){
15214         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15215         this.editorcore.pushValue();
15216     },
15217
15218      
15219     // private
15220     deferFocus : function(){
15221         this.focus.defer(10, this);
15222     },
15223
15224     // doc'ed in Field
15225     focus : function(){
15226         this.editorcore.focus();
15227         
15228     },
15229       
15230
15231     // private
15232     onDestroy : function(){
15233         
15234         
15235         
15236         if(this.rendered){
15237             
15238             for (var i =0; i < this.toolbars.length;i++) {
15239                 // fixme - ask toolbars for heights?
15240                 this.toolbars[i].onDestroy();
15241             }
15242             
15243             this.wrap.dom.innerHTML = '';
15244             this.wrap.remove();
15245         }
15246     },
15247
15248     // private
15249     onFirstFocus : function(){
15250         //Roo.log("onFirstFocus");
15251         this.editorcore.onFirstFocus();
15252          for (var i =0; i < this.toolbars.length;i++) {
15253             this.toolbars[i].onFirstFocus();
15254         }
15255         
15256     },
15257     
15258     // private
15259     syncValue : function()
15260     {   
15261         this.editorcore.syncValue();
15262     },
15263     
15264     pushValue : function()
15265     {   
15266         this.editorcore.pushValue();
15267     }
15268      
15269     
15270     // hide stuff that is not compatible
15271     /**
15272      * @event blur
15273      * @hide
15274      */
15275     /**
15276      * @event change
15277      * @hide
15278      */
15279     /**
15280      * @event focus
15281      * @hide
15282      */
15283     /**
15284      * @event specialkey
15285      * @hide
15286      */
15287     /**
15288      * @cfg {String} fieldClass @hide
15289      */
15290     /**
15291      * @cfg {String} focusClass @hide
15292      */
15293     /**
15294      * @cfg {String} autoCreate @hide
15295      */
15296     /**
15297      * @cfg {String} inputType @hide
15298      */
15299     /**
15300      * @cfg {String} invalidClass @hide
15301      */
15302     /**
15303      * @cfg {String} invalidText @hide
15304      */
15305     /**
15306      * @cfg {String} msgFx @hide
15307      */
15308     /**
15309      * @cfg {String} validateOnBlur @hide
15310      */
15311 });
15312  
15313     
15314    
15315    
15316    
15317       
15318
15319 /**
15320  * @class Roo.bootstrap.HtmlEditorToolbar1
15321  * Basic Toolbar
15322  * 
15323  * Usage:
15324  *
15325  new Roo.bootstrap.HtmlEditor({
15326     ....
15327     toolbars : [
15328         new Roo.bootstrap.HtmlEditorToolbar1({
15329             disable : { fonts: 1 , format: 1, ..., ... , ...],
15330             btns : [ .... ]
15331         })
15332     }
15333      
15334  * 
15335  * @cfg {Object} disable List of elements to disable..
15336  * @cfg {Array} btns List of additional buttons.
15337  * 
15338  * 
15339  * NEEDS Extra CSS? 
15340  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15341  */
15342  
15343 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15344 {
15345     
15346     Roo.apply(this, config);
15347     
15348     // default disabled, based on 'good practice'..
15349     this.disable = this.disable || {};
15350     Roo.applyIf(this.disable, {
15351         fontSize : true,
15352         colors : true,
15353         specialElements : true
15354     });
15355     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15356     
15357     this.editor = config.editor;
15358     this.editorcore = config.editor.editorcore;
15359     
15360     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15361     
15362     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15363     // dont call parent... till later.
15364 }
15365 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
15366     
15367     
15368     bar : true,
15369     
15370     editor : false,
15371     editorcore : false,
15372     
15373     
15374     formats : [
15375         "p" ,  
15376         "h1","h2","h3","h4","h5","h6", 
15377         "pre", "code", 
15378         "abbr", "acronym", "address", "cite", "samp", "var",
15379         'div','span'
15380     ],
15381     
15382     onRender : function(ct, position)
15383     {
15384        // Roo.log("Call onRender: " + this.xtype);
15385         
15386        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15387        Roo.log(this.el);
15388        this.el.dom.style.marginBottom = '0';
15389        var _this = this;
15390        var editorcore = this.editorcore;
15391        var editor= this.editor;
15392        
15393        var children = [];
15394        var btn = function(id,cmd , toggle, handler){
15395        
15396             var  event = toggle ? 'toggle' : 'click';
15397        
15398             var a = {
15399                 size : 'sm',
15400                 xtype: 'Button',
15401                 xns: Roo.bootstrap,
15402                 glyphicon : id,
15403                 cmd : id || cmd,
15404                 enableToggle:toggle !== false,
15405                 //html : 'submit'
15406                 pressed : toggle ? false : null,
15407                 listeners : {}
15408             }
15409             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15410                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15411             }
15412             children.push(a);
15413             return a;
15414        }
15415         
15416         var style = {
15417                 xtype: 'Button',
15418                 size : 'sm',
15419                 xns: Roo.bootstrap,
15420                 glyphicon : 'font',
15421                 //html : 'submit'
15422                 menu : {
15423                     xtype: 'Menu',
15424                     xns: Roo.bootstrap,
15425                     items:  []
15426                 }
15427         };
15428         Roo.each(this.formats, function(f) {
15429             style.menu.items.push({
15430                 xtype :'MenuItem',
15431                 xns: Roo.bootstrap,
15432                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15433                 tagname : f,
15434                 listeners : {
15435                     click : function()
15436                     {
15437                         editorcore.insertTag(this.tagname);
15438                         editor.focus();
15439                     }
15440                 }
15441                 
15442             });
15443         });
15444          children.push(style);   
15445             
15446             
15447         btn('bold',false,true);
15448         btn('italic',false,true);
15449         btn('align-left', 'justifyleft',true);
15450         btn('align-center', 'justifycenter',true);
15451         btn('align-right' , 'justifyright',true);
15452         btn('link', false, false, function(btn) {
15453             //Roo.log("create link?");
15454             var url = prompt(this.createLinkText, this.defaultLinkValue);
15455             if(url && url != 'http:/'+'/'){
15456                 this.editorcore.relayCmd('createlink', url);
15457             }
15458         }),
15459         btn('list','insertunorderedlist',true);
15460         btn('pencil', false,true, function(btn){
15461                 Roo.log(this);
15462                 
15463                 this.toggleSourceEdit(btn.pressed);
15464         });
15465         /*
15466         var cog = {
15467                 xtype: 'Button',
15468                 size : 'sm',
15469                 xns: Roo.bootstrap,
15470                 glyphicon : 'cog',
15471                 //html : 'submit'
15472                 menu : {
15473                     xtype: 'Menu',
15474                     xns: Roo.bootstrap,
15475                     items:  []
15476                 }
15477         };
15478         
15479         cog.menu.items.push({
15480             xtype :'MenuItem',
15481             xns: Roo.bootstrap,
15482             html : Clean styles,
15483             tagname : f,
15484             listeners : {
15485                 click : function()
15486                 {
15487                     editorcore.insertTag(this.tagname);
15488                     editor.focus();
15489                 }
15490             }
15491             
15492         });
15493        */
15494         
15495          
15496        this.xtype = 'Navbar';
15497         
15498         for(var i=0;i< children.length;i++) {
15499             
15500             this.buttons.add(this.addxtypeChild(children[i]));
15501             
15502         }
15503         
15504         editor.on('editorevent', this.updateToolbar, this);
15505     },
15506     onBtnClick : function(id)
15507     {
15508        this.editorcore.relayCmd(id);
15509        this.editorcore.focus();
15510     },
15511     
15512     /**
15513      * Protected method that will not generally be called directly. It triggers
15514      * a toolbar update by reading the markup state of the current selection in the editor.
15515      */
15516     updateToolbar: function(){
15517
15518         if(!this.editorcore.activated){
15519             this.editor.onFirstFocus(); // is this neeed?
15520             return;
15521         }
15522
15523         var btns = this.buttons; 
15524         var doc = this.editorcore.doc;
15525         btns.get('bold').setActive(doc.queryCommandState('bold'));
15526         btns.get('italic').setActive(doc.queryCommandState('italic'));
15527         //btns.get('underline').setActive(doc.queryCommandState('underline'));
15528         
15529         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15530         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15531         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15532         
15533         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15534         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15535          /*
15536         
15537         var ans = this.editorcore.getAllAncestors();
15538         if (this.formatCombo) {
15539             
15540             
15541             var store = this.formatCombo.store;
15542             this.formatCombo.setValue("");
15543             for (var i =0; i < ans.length;i++) {
15544                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15545                     // select it..
15546                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15547                     break;
15548                 }
15549             }
15550         }
15551         
15552         
15553         
15554         // hides menus... - so this cant be on a menu...
15555         Roo.bootstrap.MenuMgr.hideAll();
15556         */
15557         Roo.bootstrap.MenuMgr.hideAll();
15558         //this.editorsyncValue();
15559     },
15560     onFirstFocus: function() {
15561         this.buttons.each(function(item){
15562            item.enable();
15563         });
15564     },
15565     toggleSourceEdit : function(sourceEditMode){
15566         
15567           
15568         if(sourceEditMode){
15569             Roo.log("disabling buttons");
15570            this.buttons.each( function(item){
15571                 if(item.cmd != 'pencil'){
15572                     item.disable();
15573                 }
15574             });
15575           
15576         }else{
15577             Roo.log("enabling buttons");
15578             if(this.editorcore.initialized){
15579                 this.buttons.each( function(item){
15580                     item.enable();
15581                 });
15582             }
15583             
15584         }
15585         Roo.log("calling toggole on editor");
15586         // tell the editor that it's been pressed..
15587         this.editor.toggleSourceEdit(sourceEditMode);
15588        
15589     }
15590 });
15591
15592
15593
15594
15595
15596 /**
15597  * @class Roo.bootstrap.Table.AbstractSelectionModel
15598  * @extends Roo.util.Observable
15599  * Abstract base class for grid SelectionModels.  It provides the interface that should be
15600  * implemented by descendant classes.  This class should not be directly instantiated.
15601  * @constructor
15602  */
15603 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15604     this.locked = false;
15605     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15606 };
15607
15608
15609 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
15610     /** @ignore Called by the grid automatically. Do not call directly. */
15611     init : function(grid){
15612         this.grid = grid;
15613         this.initEvents();
15614     },
15615
15616     /**
15617      * Locks the selections.
15618      */
15619     lock : function(){
15620         this.locked = true;
15621     },
15622
15623     /**
15624      * Unlocks the selections.
15625      */
15626     unlock : function(){
15627         this.locked = false;
15628     },
15629
15630     /**
15631      * Returns true if the selections are locked.
15632      * @return {Boolean}
15633      */
15634     isLocked : function(){
15635         return this.locked;
15636     }
15637 });
15638 /**
15639  * @class Roo.bootstrap.Table.ColumnModel
15640  * @extends Roo.util.Observable
15641  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15642  * the columns in the table.
15643  
15644  * @constructor
15645  * @param {Object} config An Array of column config objects. See this class's
15646  * config objects for details.
15647 */
15648 Roo.bootstrap.Table.ColumnModel = function(config){
15649         /**
15650      * The config passed into the constructor
15651      */
15652     this.config = config;
15653     this.lookup = {};
15654
15655     // if no id, create one
15656     // if the column does not have a dataIndex mapping,
15657     // map it to the order it is in the config
15658     for(var i = 0, len = config.length; i < len; i++){
15659         var c = config[i];
15660         if(typeof c.dataIndex == "undefined"){
15661             c.dataIndex = i;
15662         }
15663         if(typeof c.renderer == "string"){
15664             c.renderer = Roo.util.Format[c.renderer];
15665         }
15666         if(typeof c.id == "undefined"){
15667             c.id = Roo.id();
15668         }
15669 //        if(c.editor && c.editor.xtype){
15670 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15671 //        }
15672 //        if(c.editor && c.editor.isFormField){
15673 //            c.editor = new Roo.grid.GridEditor(c.editor);
15674 //        }
15675
15676         this.lookup[c.id] = c;
15677     }
15678
15679     /**
15680      * The width of columns which have no width specified (defaults to 100)
15681      * @type Number
15682      */
15683     this.defaultWidth = 100;
15684
15685     /**
15686      * Default sortable of columns which have no sortable specified (defaults to false)
15687      * @type Boolean
15688      */
15689     this.defaultSortable = false;
15690
15691     this.addEvents({
15692         /**
15693              * @event widthchange
15694              * Fires when the width of a column changes.
15695              * @param {ColumnModel} this
15696              * @param {Number} columnIndex The column index
15697              * @param {Number} newWidth The new width
15698              */
15699             "widthchange": true,
15700         /**
15701              * @event headerchange
15702              * Fires when the text of a header changes.
15703              * @param {ColumnModel} this
15704              * @param {Number} columnIndex The column index
15705              * @param {Number} newText The new header text
15706              */
15707             "headerchange": true,
15708         /**
15709              * @event hiddenchange
15710              * Fires when a column is hidden or "unhidden".
15711              * @param {ColumnModel} this
15712              * @param {Number} columnIndex The column index
15713              * @param {Boolean} hidden true if hidden, false otherwise
15714              */
15715             "hiddenchange": true,
15716             /**
15717          * @event columnmoved
15718          * Fires when a column is moved.
15719          * @param {ColumnModel} this
15720          * @param {Number} oldIndex
15721          * @param {Number} newIndex
15722          */
15723         "columnmoved" : true,
15724         /**
15725          * @event columlockchange
15726          * Fires when a column's locked state is changed
15727          * @param {ColumnModel} this
15728          * @param {Number} colIndex
15729          * @param {Boolean} locked true if locked
15730          */
15731         "columnlockchange" : true
15732     });
15733     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15734 };
15735 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15736     /**
15737      * @cfg {String} header The header text to display in the Grid view.
15738      */
15739     /**
15740      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15741      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15742      * specified, the column's index is used as an index into the Record's data Array.
15743      */
15744     /**
15745      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15746      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15747      */
15748     /**
15749      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15750      * Defaults to the value of the {@link #defaultSortable} property.
15751      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15752      */
15753     /**
15754      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
15755      */
15756     /**
15757      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
15758      */
15759     /**
15760      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15761      */
15762     /**
15763      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15764      */
15765     /**
15766      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15767      * given the cell's data value. See {@link #setRenderer}. If not specified, the
15768      * default renderer uses the raw data value.
15769      */
15770     /**
15771      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
15772      */
15773
15774     /**
15775      * Returns the id of the column at the specified index.
15776      * @param {Number} index The column index
15777      * @return {String} the id
15778      */
15779     getColumnId : function(index){
15780         return this.config[index].id;
15781     },
15782
15783     /**
15784      * Returns the column for a specified id.
15785      * @param {String} id The column id
15786      * @return {Object} the column
15787      */
15788     getColumnById : function(id){
15789         return this.lookup[id];
15790     },
15791
15792     
15793     /**
15794      * Returns the column for a specified dataIndex.
15795      * @param {String} dataIndex The column dataIndex
15796      * @return {Object|Boolean} the column or false if not found
15797      */
15798     getColumnByDataIndex: function(dataIndex){
15799         var index = this.findColumnIndex(dataIndex);
15800         return index > -1 ? this.config[index] : false;
15801     },
15802     
15803     /**
15804      * Returns the index for a specified column id.
15805      * @param {String} id The column id
15806      * @return {Number} the index, or -1 if not found
15807      */
15808     getIndexById : function(id){
15809         for(var i = 0, len = this.config.length; i < len; i++){
15810             if(this.config[i].id == id){
15811                 return i;
15812             }
15813         }
15814         return -1;
15815     },
15816     
15817     /**
15818      * Returns the index for a specified column dataIndex.
15819      * @param {String} dataIndex The column dataIndex
15820      * @return {Number} the index, or -1 if not found
15821      */
15822     
15823     findColumnIndex : function(dataIndex){
15824         for(var i = 0, len = this.config.length; i < len; i++){
15825             if(this.config[i].dataIndex == dataIndex){
15826                 return i;
15827             }
15828         }
15829         return -1;
15830     },
15831     
15832     
15833     moveColumn : function(oldIndex, newIndex){
15834         var c = this.config[oldIndex];
15835         this.config.splice(oldIndex, 1);
15836         this.config.splice(newIndex, 0, c);
15837         this.dataMap = null;
15838         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15839     },
15840
15841     isLocked : function(colIndex){
15842         return this.config[colIndex].locked === true;
15843     },
15844
15845     setLocked : function(colIndex, value, suppressEvent){
15846         if(this.isLocked(colIndex) == value){
15847             return;
15848         }
15849         this.config[colIndex].locked = value;
15850         if(!suppressEvent){
15851             this.fireEvent("columnlockchange", this, colIndex, value);
15852         }
15853     },
15854
15855     getTotalLockedWidth : function(){
15856         var totalWidth = 0;
15857         for(var i = 0; i < this.config.length; i++){
15858             if(this.isLocked(i) && !this.isHidden(i)){
15859                 this.totalWidth += this.getColumnWidth(i);
15860             }
15861         }
15862         return totalWidth;
15863     },
15864
15865     getLockedCount : function(){
15866         for(var i = 0, len = this.config.length; i < len; i++){
15867             if(!this.isLocked(i)){
15868                 return i;
15869             }
15870         }
15871     },
15872
15873     /**
15874      * Returns the number of columns.
15875      * @return {Number}
15876      */
15877     getColumnCount : function(visibleOnly){
15878         if(visibleOnly === true){
15879             var c = 0;
15880             for(var i = 0, len = this.config.length; i < len; i++){
15881                 if(!this.isHidden(i)){
15882                     c++;
15883                 }
15884             }
15885             return c;
15886         }
15887         return this.config.length;
15888     },
15889
15890     /**
15891      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15892      * @param {Function} fn
15893      * @param {Object} scope (optional)
15894      * @return {Array} result
15895      */
15896     getColumnsBy : function(fn, scope){
15897         var r = [];
15898         for(var i = 0, len = this.config.length; i < len; i++){
15899             var c = this.config[i];
15900             if(fn.call(scope||this, c, i) === true){
15901                 r[r.length] = c;
15902             }
15903         }
15904         return r;
15905     },
15906
15907     /**
15908      * Returns true if the specified column is sortable.
15909      * @param {Number} col The column index
15910      * @return {Boolean}
15911      */
15912     isSortable : function(col){
15913         if(typeof this.config[col].sortable == "undefined"){
15914             return this.defaultSortable;
15915         }
15916         return this.config[col].sortable;
15917     },
15918
15919     /**
15920      * Returns the rendering (formatting) function defined for the column.
15921      * @param {Number} col The column index.
15922      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15923      */
15924     getRenderer : function(col){
15925         if(!this.config[col].renderer){
15926             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15927         }
15928         return this.config[col].renderer;
15929     },
15930
15931     /**
15932      * Sets the rendering (formatting) function for a column.
15933      * @param {Number} col The column index
15934      * @param {Function} fn The function to use to process the cell's raw data
15935      * to return HTML markup for the grid view. The render function is called with
15936      * the following parameters:<ul>
15937      * <li>Data value.</li>
15938      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15939      * <li>css A CSS style string to apply to the table cell.</li>
15940      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15941      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15942      * <li>Row index</li>
15943      * <li>Column index</li>
15944      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15945      */
15946     setRenderer : function(col, fn){
15947         this.config[col].renderer = fn;
15948     },
15949
15950     /**
15951      * Returns the width for the specified column.
15952      * @param {Number} col The column index
15953      * @return {Number}
15954      */
15955     getColumnWidth : function(col){
15956         return this.config[col].width * 1 || this.defaultWidth;
15957     },
15958
15959     /**
15960      * Sets the width for a column.
15961      * @param {Number} col The column index
15962      * @param {Number} width The new width
15963      */
15964     setColumnWidth : function(col, width, suppressEvent){
15965         this.config[col].width = width;
15966         this.totalWidth = null;
15967         if(!suppressEvent){
15968              this.fireEvent("widthchange", this, col, width);
15969         }
15970     },
15971
15972     /**
15973      * Returns the total width of all columns.
15974      * @param {Boolean} includeHidden True to include hidden column widths
15975      * @return {Number}
15976      */
15977     getTotalWidth : function(includeHidden){
15978         if(!this.totalWidth){
15979             this.totalWidth = 0;
15980             for(var i = 0, len = this.config.length; i < len; i++){
15981                 if(includeHidden || !this.isHidden(i)){
15982                     this.totalWidth += this.getColumnWidth(i);
15983                 }
15984             }
15985         }
15986         return this.totalWidth;
15987     },
15988
15989     /**
15990      * Returns the header for the specified column.
15991      * @param {Number} col The column index
15992      * @return {String}
15993      */
15994     getColumnHeader : function(col){
15995         return this.config[col].header;
15996     },
15997
15998     /**
15999      * Sets the header for a column.
16000      * @param {Number} col The column index
16001      * @param {String} header The new header
16002      */
16003     setColumnHeader : function(col, header){
16004         this.config[col].header = header;
16005         this.fireEvent("headerchange", this, col, header);
16006     },
16007
16008     /**
16009      * Returns the tooltip for the specified column.
16010      * @param {Number} col The column index
16011      * @return {String}
16012      */
16013     getColumnTooltip : function(col){
16014             return this.config[col].tooltip;
16015     },
16016     /**
16017      * Sets the tooltip for a column.
16018      * @param {Number} col The column index
16019      * @param {String} tooltip The new tooltip
16020      */
16021     setColumnTooltip : function(col, tooltip){
16022             this.config[col].tooltip = tooltip;
16023     },
16024
16025     /**
16026      * Returns the dataIndex for the specified column.
16027      * @param {Number} col The column index
16028      * @return {Number}
16029      */
16030     getDataIndex : function(col){
16031         return this.config[col].dataIndex;
16032     },
16033
16034     /**
16035      * Sets the dataIndex for a column.
16036      * @param {Number} col The column index
16037      * @param {Number} dataIndex The new dataIndex
16038      */
16039     setDataIndex : function(col, dataIndex){
16040         this.config[col].dataIndex = dataIndex;
16041     },
16042
16043     
16044     
16045     /**
16046      * Returns true if the cell is editable.
16047      * @param {Number} colIndex The column index
16048      * @param {Number} rowIndex The row index
16049      * @return {Boolean}
16050      */
16051     isCellEditable : function(colIndex, rowIndex){
16052         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16053     },
16054
16055     /**
16056      * Returns the editor defined for the cell/column.
16057      * return false or null to disable editing.
16058      * @param {Number} colIndex The column index
16059      * @param {Number} rowIndex The row index
16060      * @return {Object}
16061      */
16062     getCellEditor : function(colIndex, rowIndex){
16063         return this.config[colIndex].editor;
16064     },
16065
16066     /**
16067      * Sets if a column is editable.
16068      * @param {Number} col The column index
16069      * @param {Boolean} editable True if the column is editable
16070      */
16071     setEditable : function(col, editable){
16072         this.config[col].editable = editable;
16073     },
16074
16075
16076     /**
16077      * Returns true if the column is hidden.
16078      * @param {Number} colIndex The column index
16079      * @return {Boolean}
16080      */
16081     isHidden : function(colIndex){
16082         return this.config[colIndex].hidden;
16083     },
16084
16085
16086     /**
16087      * Returns true if the column width cannot be changed
16088      */
16089     isFixed : function(colIndex){
16090         return this.config[colIndex].fixed;
16091     },
16092
16093     /**
16094      * Returns true if the column can be resized
16095      * @return {Boolean}
16096      */
16097     isResizable : function(colIndex){
16098         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16099     },
16100     /**
16101      * Sets if a column is hidden.
16102      * @param {Number} colIndex The column index
16103      * @param {Boolean} hidden True if the column is hidden
16104      */
16105     setHidden : function(colIndex, hidden){
16106         this.config[colIndex].hidden = hidden;
16107         this.totalWidth = null;
16108         this.fireEvent("hiddenchange", this, colIndex, hidden);
16109     },
16110
16111     /**
16112      * Sets the editor for a column.
16113      * @param {Number} col The column index
16114      * @param {Object} editor The editor object
16115      */
16116     setEditor : function(col, editor){
16117         this.config[col].editor = editor;
16118     }
16119 });
16120
16121 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16122         if(typeof value == "string" && value.length < 1){
16123             return "&#160;";
16124         }
16125         return value;
16126 };
16127
16128 // Alias for backwards compatibility
16129 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16130
16131 /**
16132  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16133  * @class Roo.bootstrap.Table.RowSelectionModel
16134  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16135  * It supports multiple selections and keyboard selection/navigation. 
16136  * @constructor
16137  * @param {Object} config
16138  */
16139
16140 Roo.bootstrap.Table.RowSelectionModel = function(config){
16141     Roo.apply(this, config);
16142     this.selections = new Roo.util.MixedCollection(false, function(o){
16143         return o.id;
16144     });
16145
16146     this.last = false;
16147     this.lastActive = false;
16148
16149     this.addEvents({
16150         /**
16151              * @event selectionchange
16152              * Fires when the selection changes
16153              * @param {SelectionModel} this
16154              */
16155             "selectionchange" : true,
16156         /**
16157              * @event afterselectionchange
16158              * Fires after the selection changes (eg. by key press or clicking)
16159              * @param {SelectionModel} this
16160              */
16161             "afterselectionchange" : true,
16162         /**
16163              * @event beforerowselect
16164              * Fires when a row is selected being selected, return false to cancel.
16165              * @param {SelectionModel} this
16166              * @param {Number} rowIndex The selected index
16167              * @param {Boolean} keepExisting False if other selections will be cleared
16168              */
16169             "beforerowselect" : true,
16170         /**
16171              * @event rowselect
16172              * Fires when a row is selected.
16173              * @param {SelectionModel} this
16174              * @param {Number} rowIndex The selected index
16175              * @param {Roo.data.Record} r The record
16176              */
16177             "rowselect" : true,
16178         /**
16179              * @event rowdeselect
16180              * Fires when a row is deselected.
16181              * @param {SelectionModel} this
16182              * @param {Number} rowIndex The selected index
16183              */
16184         "rowdeselect" : true
16185     });
16186     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16187     this.locked = false;
16188 };
16189
16190 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16191     /**
16192      * @cfg {Boolean} singleSelect
16193      * True to allow selection of only one row at a time (defaults to false)
16194      */
16195     singleSelect : false,
16196
16197     // private
16198     initEvents : function(){
16199
16200         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16201             this.grid.on("mousedown", this.handleMouseDown, this);
16202         }else{ // allow click to work like normal
16203             this.grid.on("rowclick", this.handleDragableRowClick, this);
16204         }
16205
16206         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16207             "up" : function(e){
16208                 if(!e.shiftKey){
16209                     this.selectPrevious(e.shiftKey);
16210                 }else if(this.last !== false && this.lastActive !== false){
16211                     var last = this.last;
16212                     this.selectRange(this.last,  this.lastActive-1);
16213                     this.grid.getView().focusRow(this.lastActive);
16214                     if(last !== false){
16215                         this.last = last;
16216                     }
16217                 }else{
16218                     this.selectFirstRow();
16219                 }
16220                 this.fireEvent("afterselectionchange", this);
16221             },
16222             "down" : function(e){
16223                 if(!e.shiftKey){
16224                     this.selectNext(e.shiftKey);
16225                 }else if(this.last !== false && this.lastActive !== false){
16226                     var last = this.last;
16227                     this.selectRange(this.last,  this.lastActive+1);
16228                     this.grid.getView().focusRow(this.lastActive);
16229                     if(last !== false){
16230                         this.last = last;
16231                     }
16232                 }else{
16233                     this.selectFirstRow();
16234                 }
16235                 this.fireEvent("afterselectionchange", this);
16236             },
16237             scope: this
16238         });
16239
16240         var view = this.grid.view;
16241         view.on("refresh", this.onRefresh, this);
16242         view.on("rowupdated", this.onRowUpdated, this);
16243         view.on("rowremoved", this.onRemove, this);
16244     },
16245
16246     // private
16247     onRefresh : function(){
16248         var ds = this.grid.dataSource, i, v = this.grid.view;
16249         var s = this.selections;
16250         s.each(function(r){
16251             if((i = ds.indexOfId(r.id)) != -1){
16252                 v.onRowSelect(i);
16253             }else{
16254                 s.remove(r);
16255             }
16256         });
16257     },
16258
16259     // private
16260     onRemove : function(v, index, r){
16261         this.selections.remove(r);
16262     },
16263
16264     // private
16265     onRowUpdated : function(v, index, r){
16266         if(this.isSelected(r)){
16267             v.onRowSelect(index);
16268         }
16269     },
16270
16271     /**
16272      * Select records.
16273      * @param {Array} records The records to select
16274      * @param {Boolean} keepExisting (optional) True to keep existing selections
16275      */
16276     selectRecords : function(records, keepExisting){
16277         if(!keepExisting){
16278             this.clearSelections();
16279         }
16280         var ds = this.grid.dataSource;
16281         for(var i = 0, len = records.length; i < len; i++){
16282             this.selectRow(ds.indexOf(records[i]), true);
16283         }
16284     },
16285
16286     /**
16287      * Gets the number of selected rows.
16288      * @return {Number}
16289      */
16290     getCount : function(){
16291         return this.selections.length;
16292     },
16293
16294     /**
16295      * Selects the first row in the grid.
16296      */
16297     selectFirstRow : function(){
16298         this.selectRow(0);
16299     },
16300
16301     /**
16302      * Select the last row.
16303      * @param {Boolean} keepExisting (optional) True to keep existing selections
16304      */
16305     selectLastRow : function(keepExisting){
16306         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16307     },
16308
16309     /**
16310      * Selects the row immediately following the last selected row.
16311      * @param {Boolean} keepExisting (optional) True to keep existing selections
16312      */
16313     selectNext : function(keepExisting){
16314         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16315             this.selectRow(this.last+1, keepExisting);
16316             this.grid.getView().focusRow(this.last);
16317         }
16318     },
16319
16320     /**
16321      * Selects the row that precedes the last selected row.
16322      * @param {Boolean} keepExisting (optional) True to keep existing selections
16323      */
16324     selectPrevious : function(keepExisting){
16325         if(this.last){
16326             this.selectRow(this.last-1, keepExisting);
16327             this.grid.getView().focusRow(this.last);
16328         }
16329     },
16330
16331     /**
16332      * Returns the selected records
16333      * @return {Array} Array of selected records
16334      */
16335     getSelections : function(){
16336         return [].concat(this.selections.items);
16337     },
16338
16339     /**
16340      * Returns the first selected record.
16341      * @return {Record}
16342      */
16343     getSelected : function(){
16344         return this.selections.itemAt(0);
16345     },
16346
16347
16348     /**
16349      * Clears all selections.
16350      */
16351     clearSelections : function(fast){
16352         if(this.locked) return;
16353         if(fast !== true){
16354             var ds = this.grid.dataSource;
16355             var s = this.selections;
16356             s.each(function(r){
16357                 this.deselectRow(ds.indexOfId(r.id));
16358             }, this);
16359             s.clear();
16360         }else{
16361             this.selections.clear();
16362         }
16363         this.last = false;
16364     },
16365
16366
16367     /**
16368      * Selects all rows.
16369      */
16370     selectAll : function(){
16371         if(this.locked) return;
16372         this.selections.clear();
16373         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16374             this.selectRow(i, true);
16375         }
16376     },
16377
16378     /**
16379      * Returns True if there is a selection.
16380      * @return {Boolean}
16381      */
16382     hasSelection : function(){
16383         return this.selections.length > 0;
16384     },
16385
16386     /**
16387      * Returns True if the specified row is selected.
16388      * @param {Number/Record} record The record or index of the record to check
16389      * @return {Boolean}
16390      */
16391     isSelected : function(index){
16392         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16393         return (r && this.selections.key(r.id) ? true : false);
16394     },
16395
16396     /**
16397      * Returns True if the specified record id is selected.
16398      * @param {String} id The id of record to check
16399      * @return {Boolean}
16400      */
16401     isIdSelected : function(id){
16402         return (this.selections.key(id) ? true : false);
16403     },
16404
16405     // private
16406     handleMouseDown : function(e, t){
16407         var view = this.grid.getView(), rowIndex;
16408         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16409             return;
16410         };
16411         if(e.shiftKey && this.last !== false){
16412             var last = this.last;
16413             this.selectRange(last, rowIndex, e.ctrlKey);
16414             this.last = last; // reset the last
16415             view.focusRow(rowIndex);
16416         }else{
16417             var isSelected = this.isSelected(rowIndex);
16418             if(e.button !== 0 && isSelected){
16419                 view.focusRow(rowIndex);
16420             }else if(e.ctrlKey && isSelected){
16421                 this.deselectRow(rowIndex);
16422             }else if(!isSelected){
16423                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16424                 view.focusRow(rowIndex);
16425             }
16426         }
16427         this.fireEvent("afterselectionchange", this);
16428     },
16429     // private
16430     handleDragableRowClick :  function(grid, rowIndex, e) 
16431     {
16432         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16433             this.selectRow(rowIndex, false);
16434             grid.view.focusRow(rowIndex);
16435              this.fireEvent("afterselectionchange", this);
16436         }
16437     },
16438     
16439     /**
16440      * Selects multiple rows.
16441      * @param {Array} rows Array of the indexes of the row to select
16442      * @param {Boolean} keepExisting (optional) True to keep existing selections
16443      */
16444     selectRows : function(rows, keepExisting){
16445         if(!keepExisting){
16446             this.clearSelections();
16447         }
16448         for(var i = 0, len = rows.length; i < len; i++){
16449             this.selectRow(rows[i], true);
16450         }
16451     },
16452
16453     /**
16454      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16455      * @param {Number} startRow The index of the first row in the range
16456      * @param {Number} endRow The index of the last row in the range
16457      * @param {Boolean} keepExisting (optional) True to retain existing selections
16458      */
16459     selectRange : function(startRow, endRow, keepExisting){
16460         if(this.locked) return;
16461         if(!keepExisting){
16462             this.clearSelections();
16463         }
16464         if(startRow <= endRow){
16465             for(var i = startRow; i <= endRow; i++){
16466                 this.selectRow(i, true);
16467             }
16468         }else{
16469             for(var i = startRow; i >= endRow; i--){
16470                 this.selectRow(i, true);
16471             }
16472         }
16473     },
16474
16475     /**
16476      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16477      * @param {Number} startRow The index of the first row in the range
16478      * @param {Number} endRow The index of the last row in the range
16479      */
16480     deselectRange : function(startRow, endRow, preventViewNotify){
16481         if(this.locked) return;
16482         for(var i = startRow; i <= endRow; i++){
16483             this.deselectRow(i, preventViewNotify);
16484         }
16485     },
16486
16487     /**
16488      * Selects a row.
16489      * @param {Number} row The index of the row to select
16490      * @param {Boolean} keepExisting (optional) True to keep existing selections
16491      */
16492     selectRow : function(index, keepExisting, preventViewNotify){
16493         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16494         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16495             if(!keepExisting || this.singleSelect){
16496                 this.clearSelections();
16497             }
16498             var r = this.grid.dataSource.getAt(index);
16499             this.selections.add(r);
16500             this.last = this.lastActive = index;
16501             if(!preventViewNotify){
16502                 this.grid.getView().onRowSelect(index);
16503             }
16504             this.fireEvent("rowselect", this, index, r);
16505             this.fireEvent("selectionchange", this);
16506         }
16507     },
16508
16509     /**
16510      * Deselects a row.
16511      * @param {Number} row The index of the row to deselect
16512      */
16513     deselectRow : function(index, preventViewNotify){
16514         if(this.locked) return;
16515         if(this.last == index){
16516             this.last = false;
16517         }
16518         if(this.lastActive == index){
16519             this.lastActive = false;
16520         }
16521         var r = this.grid.dataSource.getAt(index);
16522         this.selections.remove(r);
16523         if(!preventViewNotify){
16524             this.grid.getView().onRowDeselect(index);
16525         }
16526         this.fireEvent("rowdeselect", this, index);
16527         this.fireEvent("selectionchange", this);
16528     },
16529
16530     // private
16531     restoreLast : function(){
16532         if(this._last){
16533             this.last = this._last;
16534         }
16535     },
16536
16537     // private
16538     acceptsNav : function(row, col, cm){
16539         return !cm.isHidden(col) && cm.isCellEditable(col, row);
16540     },
16541
16542     // private
16543     onEditorKey : function(field, e){
16544         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16545         if(k == e.TAB){
16546             e.stopEvent();
16547             ed.completeEdit();
16548             if(e.shiftKey){
16549                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16550             }else{
16551                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16552             }
16553         }else if(k == e.ENTER && !e.ctrlKey){
16554             e.stopEvent();
16555             ed.completeEdit();
16556             if(e.shiftKey){
16557                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16558             }else{
16559                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16560             }
16561         }else if(k == e.ESC){
16562             ed.cancelEdit();
16563         }
16564         if(newCell){
16565             g.startEditing(newCell[0], newCell[1]);
16566         }
16567     }
16568 });/*
16569  * - LGPL
16570  *
16571  * element
16572  * 
16573  */
16574
16575 /**
16576  * @class Roo.bootstrap.MessageBar
16577  * @extends Roo.bootstrap.Component
16578  * Bootstrap MessageBar class
16579  * @cfg {String} html contents of the MessageBar
16580  * @cfg {String} weight (info | success | warning | danger) default info
16581  * @cfg {String} beforeClass insert the bar before the given class
16582  * @cfg {Boolean} closable (true | false) default false
16583  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16584  * 
16585  * @constructor
16586  * Create a new Element
16587  * @param {Object} config The config object
16588  */
16589
16590 Roo.bootstrap.MessageBar = function(config){
16591     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16592 };
16593
16594 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
16595     
16596     html: '',
16597     weight: 'info',
16598     closable: false,
16599     fixed: false,
16600     beforeClass: 'bootstrap-sticky-wrap',
16601     
16602     getAutoCreate : function(){
16603         
16604         var cfg = {
16605             tag: 'div',
16606             cls: 'alert alert-dismissable alert-' + this.weight,
16607             cn: [
16608                 {
16609                     tag: 'span',
16610                     cls: 'message',
16611                     html: this.html || ''
16612                 }
16613             ]
16614         }
16615         
16616         if(this.fixed){
16617             cfg.cls += ' alert-messages-fixed';
16618         }
16619         
16620         if(this.closable){
16621             cfg.cn.push({
16622                 tag: 'button',
16623                 cls: 'close',
16624                 html: 'x'
16625             });
16626         }
16627         
16628         return cfg;
16629     },
16630     
16631     onRender : function(ct, position)
16632     {
16633         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16634         
16635         if(!this.el){
16636             var cfg = Roo.apply({},  this.getAutoCreate());
16637             cfg.id = Roo.id();
16638             
16639             if (this.cls) {
16640                 cfg.cls += ' ' + this.cls;
16641             }
16642             if (this.style) {
16643                 cfg.style = this.style;
16644             }
16645             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16646             
16647             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16648         }
16649         
16650         this.el.select('>button.close').on('click', this.hide, this);
16651         
16652     },
16653     
16654     show : function()
16655     {
16656         if (!this.rendered) {
16657             this.render();
16658         }
16659         
16660         this.el.show();
16661         
16662         this.fireEvent('show', this);
16663         
16664     },
16665     
16666     hide : function()
16667     {
16668         if (!this.rendered) {
16669             this.render();
16670         }
16671         
16672         this.el.hide();
16673         
16674         this.fireEvent('hide', this);
16675     },
16676     
16677     update : function()
16678     {
16679 //        var e = this.el.dom.firstChild;
16680 //        
16681 //        if(this.closable){
16682 //            e = e.nextSibling;
16683 //        }
16684 //        
16685 //        e.data = this.html || '';
16686
16687         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16688     }
16689    
16690 });
16691
16692  
16693
16694