Roo/bootstrap/Img.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-responsive1' : '',
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 {Array} buttons Array of buttons or standard button set..
1761  * 
1762  * @constructor
1763  * Create a new Modal Dialog
1764  * @param {Object} config The config object
1765  */
1766
1767 Roo.bootstrap.Modal = function(config){
1768     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1769     this.addEvents({
1770         // raw events
1771         /**
1772          * @event btnclick
1773          * The raw btnclick event for the button
1774          * @param {Roo.EventObject} e
1775          */
1776         "btnclick" : true
1777     });
1778     this.buttons = this.buttons || [];
1779 };
1780
1781 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1782     
1783     title : 'test dialog',
1784    
1785     buttons : false,
1786     
1787     // set on load...
1788     body:  false,
1789     
1790     onRender : function(ct, position)
1791     {
1792         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1793      
1794         if(!this.el){
1795             var cfg = Roo.apply({},  this.getAutoCreate());
1796             cfg.id = Roo.id();
1797             //if(!cfg.name){
1798             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1799             //}
1800             //if (!cfg.name.length) {
1801             //    delete cfg.name;
1802            // }
1803             if (this.cls) {
1804                 cfg.cls += ' ' + this.cls;
1805             }
1806             if (this.style) {
1807                 cfg.style = this.style;
1808             }
1809             this.el = Roo.get(document.body).createChild(cfg, position);
1810         }
1811         //var type = this.el.dom.type;
1812         
1813         if(this.tabIndex !== undefined){
1814             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1815         }
1816         
1817         
1818         
1819         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1820         this.maskEl.enableDisplayMode("block");
1821         this.maskEl.hide();
1822         //this.el.addClass("x-dlg-modal");
1823     
1824         if (this.buttons.length) {
1825             Roo.each(this.buttons, function(bb) {
1826                 b = Roo.apply({}, bb);
1827                 b.xns = b.xns || Roo.bootstrap;
1828                 b.xtype = b.xtype || 'Button';
1829                 if (typeof(b.listeners) == 'undefined') {
1830                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1831                 }
1832                 
1833                 var btn = Roo.factory(b);
1834                 
1835                 btn.onRender(this.el.select('.modal-footer').first());
1836                 
1837             },this);
1838         }
1839         // render the children.
1840         var nitems = [];
1841         
1842         if(typeof(this.items) != 'undefined'){
1843             var items = this.items;
1844             delete this.items;
1845
1846             for(var i =0;i < items.length;i++) {
1847                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1848             }
1849         }
1850         
1851         this.items = nitems;
1852         
1853         this.body = this.el.select('.modal-body',true).first();
1854         this.close = this.el.select('.modal-header .close', true).first();
1855         this.footer = this.el.select('.modal-footer',true).first();
1856         this.initEvents();
1857         //this.el.addClass([this.fieldClass, this.cls]);
1858         
1859     },
1860     getAutoCreate : function(){
1861         
1862         
1863         var bdy = {
1864                 cls : 'modal-body',
1865                 html : this.html || ''
1866         };
1867         
1868          
1869         return modal = {
1870             cls: "modal fade",
1871             style : 'display: none',
1872             cn : [
1873                 {
1874                     cls: "modal-dialog",
1875                     cn : [
1876                         {
1877                             cls : "modal-content",
1878                             cn : [
1879                                 {
1880                                     cls : 'modal-header',
1881                                     cn : [
1882                                         {
1883                                             tag: 'button',
1884                                             cls : 'close',
1885                                             html : '&times'
1886                                         },
1887                                         {
1888                                             tag: 'h4',
1889                                             cls : 'modal-title',
1890                                             html : this.title
1891                                         }
1892                                     
1893                                     ]
1894                                 },
1895                                 bdy,
1896                                 {
1897                                     cls : 'modal-footer' 
1898                                 }
1899                                 
1900                                 
1901                             ]
1902                             
1903                         }
1904                     ]
1905                         
1906                 }
1907             ]
1908             
1909             
1910         };
1911           
1912     },
1913     getChildContainer : function() {
1914          
1915          return this.el.select('.modal-body',true).first();
1916         
1917     },
1918     getButtonContainer : function() {
1919          return this.el.select('.modal-footer',true).first();
1920         
1921     },
1922     initEvents : function()
1923     {
1924         this.el.select('.modal-header .close').on('click', this.hide, this);
1925 //        
1926 //        this.addxtype(this);
1927     },
1928     show : function() {
1929         
1930         if (!this.rendered) {
1931             this.render();
1932         }
1933        
1934         this.el.addClass('on');
1935         this.el.removeClass('fade');
1936         this.el.setStyle('display', 'block');
1937         Roo.get(document.body).addClass("x-body-masked");
1938         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1939         this.maskEl.show();
1940         this.el.setStyle('zIndex', '10001');
1941         this.fireEvent('show', this);
1942         
1943         
1944     },
1945     hide : function()
1946     {
1947         Roo.log('Modal hide?!');
1948         this.maskEl.hide();
1949         Roo.get(document.body).removeClass("x-body-masked");
1950         this.el.removeClass('on');
1951         this.el.addClass('fade');
1952         this.el.setStyle('display', 'none');
1953         this.fireEvent('hide', this);
1954     },
1955     
1956     addButton : function(str, cb)
1957     {
1958          
1959         
1960         var b = Roo.apply({}, { html : str } );
1961         b.xns = b.xns || Roo.bootstrap;
1962         b.xtype = b.xtype || 'Button';
1963         if (typeof(b.listeners) == 'undefined') {
1964             b.listeners = { click : cb.createDelegate(this)  };
1965         }
1966         
1967         var btn = Roo.factory(b);
1968            
1969         btn.onRender(this.el.select('.modal-footer').first());
1970         
1971         return btn;   
1972        
1973     },
1974     
1975     setDefaultButton : function(btn)
1976     {
1977         //this.el.select('.modal-footer').()
1978     },
1979     resizeTo: function(w,h)
1980     {
1981         // skip..
1982     },
1983     setContentSize  : function(w, h)
1984     {
1985         
1986     },
1987     onButtonClick: function(btn,e)
1988     {
1989         //Roo.log([a,b,c]);
1990         this.fireEvent('btnclick', btn.name, e);
1991     },
1992     setTitle: function(str) {
1993         this.el.select('.modal-title',true).first().dom.innerHTML = str;
1994         
1995     }
1996 });
1997
1998
1999 Roo.apply(Roo.bootstrap.Modal,  {
2000     /**
2001          * Button config that displays a single OK button
2002          * @type Object
2003          */
2004         OK :  [{
2005             name : 'ok',
2006             weight : 'primary',
2007             html : 'OK'
2008         }], 
2009         /**
2010          * Button config that displays Yes and No buttons
2011          * @type Object
2012          */
2013         YESNO : [
2014             {
2015                 name  : 'no',
2016                 html : 'No'
2017             },
2018             {
2019                 name  :'yes',
2020                 weight : 'primary',
2021                 html : 'Yes'
2022             }
2023         ],
2024         
2025         /**
2026          * Button config that displays OK and Cancel buttons
2027          * @type Object
2028          */
2029         OKCANCEL : [
2030             {
2031                name : 'cancel',
2032                 html : 'Cancel'
2033             },
2034             {
2035                 name : 'ok',
2036                 weight : 'primary',
2037                 html : 'OK'
2038             }
2039         ],
2040         /**
2041          * Button config that displays Yes, No and Cancel buttons
2042          * @type Object
2043          */
2044         YESNOCANCEL : [
2045             {
2046                 name : 'yes',
2047                 weight : 'primary',
2048                 html : 'Yes'
2049             },
2050             {
2051                 name : 'no',
2052                 html : 'No'
2053             },
2054             {
2055                 name : 'cancel',
2056                 html : 'Cancel'
2057             }
2058         ]
2059 });
2060  /*
2061  * - LGPL
2062  *
2063  * messagebox - can be used as a replace
2064  * 
2065  */
2066 /**
2067  * @class Roo.MessageBox
2068  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2069  * Example usage:
2070  *<pre><code>
2071 // Basic alert:
2072 Roo.Msg.alert('Status', 'Changes saved successfully.');
2073
2074 // Prompt for user data:
2075 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2076     if (btn == 'ok'){
2077         // process text value...
2078     }
2079 });
2080
2081 // Show a dialog using config options:
2082 Roo.Msg.show({
2083    title:'Save Changes?',
2084    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2085    buttons: Roo.Msg.YESNOCANCEL,
2086    fn: processResult,
2087    animEl: 'elId'
2088 });
2089 </code></pre>
2090  * @singleton
2091  */
2092 Roo.bootstrap.MessageBox = function(){
2093     var dlg, opt, mask, waitTimer;
2094     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2095     var buttons, activeTextEl, bwidth;
2096
2097     
2098     // private
2099     var handleButton = function(button){
2100         dlg.hide();
2101         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2102     };
2103
2104     // private
2105     var handleHide = function(){
2106         if(opt && opt.cls){
2107             dlg.el.removeClass(opt.cls);
2108         }
2109         //if(waitTimer){
2110         //    Roo.TaskMgr.stop(waitTimer);
2111         //    waitTimer = null;
2112         //}
2113     };
2114
2115     // private
2116     var updateButtons = function(b){
2117         var width = 0;
2118         if(!b){
2119             buttons["ok"].hide();
2120             buttons["cancel"].hide();
2121             buttons["yes"].hide();
2122             buttons["no"].hide();
2123             //dlg.footer.dom.style.display = 'none';
2124             return width;
2125         }
2126         dlg.footer.dom.style.display = '';
2127         for(var k in buttons){
2128             if(typeof buttons[k] != "function"){
2129                 if(b[k]){
2130                     buttons[k].show();
2131                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2132                     width += buttons[k].el.getWidth()+15;
2133                 }else{
2134                     buttons[k].hide();
2135                 }
2136             }
2137         }
2138         return width;
2139     };
2140
2141     // private
2142     var handleEsc = function(d, k, e){
2143         if(opt && opt.closable !== false){
2144             dlg.hide();
2145         }
2146         if(e){
2147             e.stopEvent();
2148         }
2149     };
2150
2151     return {
2152         /**
2153          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2154          * @return {Roo.BasicDialog} The BasicDialog element
2155          */
2156         getDialog : function(){
2157            if(!dlg){
2158                 dlg = new Roo.bootstrap.Modal( {
2159                     //draggable: true,
2160                     //resizable:false,
2161                     //constraintoviewport:false,
2162                     //fixedcenter:true,
2163                     //collapsible : false,
2164                     //shim:true,
2165                     //modal: true,
2166                   //  width:400,
2167                   //  height:100,
2168                     //buttonAlign:"center",
2169                     closeClick : function(){
2170                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2171                             handleButton("no");
2172                         }else{
2173                             handleButton("cancel");
2174                         }
2175                     }
2176                 });
2177                 dlg.render();
2178                 dlg.on("hide", handleHide);
2179                 mask = dlg.mask;
2180                 //dlg.addKeyListener(27, handleEsc);
2181                 buttons = {};
2182                 this.buttons = buttons;
2183                 var bt = this.buttonText;
2184                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2185                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2186                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2187                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2188                 Roo.log(buttons)
2189                 bodyEl = dlg.body.createChild({
2190
2191                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2192                         '<textarea class="roo-mb-textarea"></textarea>' +
2193                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2194                 });
2195                 msgEl = bodyEl.dom.firstChild;
2196                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2197                 textboxEl.enableDisplayMode();
2198                 textboxEl.addKeyListener([10,13], function(){
2199                     if(dlg.isVisible() && opt && opt.buttons){
2200                         if(opt.buttons.ok){
2201                             handleButton("ok");
2202                         }else if(opt.buttons.yes){
2203                             handleButton("yes");
2204                         }
2205                     }
2206                 });
2207                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2208                 textareaEl.enableDisplayMode();
2209                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2210                 progressEl.enableDisplayMode();
2211                 var pf = progressEl.dom.firstChild;
2212                 if (pf) {
2213                     pp = Roo.get(pf.firstChild);
2214                     pp.setHeight(pf.offsetHeight);
2215                 }
2216                 
2217             }
2218             return dlg;
2219         },
2220
2221         /**
2222          * Updates the message box body text
2223          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2224          * the XHTML-compliant non-breaking space character '&amp;#160;')
2225          * @return {Roo.MessageBox} This message box
2226          */
2227         updateText : function(text){
2228             if(!dlg.isVisible() && !opt.width){
2229                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2230             }
2231             msgEl.innerHTML = text || '&#160;';
2232       
2233             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2234             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2235             var w = Math.max(
2236                     Math.min(opt.width || cw , this.maxWidth), 
2237                     Math.max(opt.minWidth || this.minWidth, bwidth)
2238             );
2239             if(opt.prompt){
2240                 activeTextEl.setWidth(w);
2241             }
2242             if(dlg.isVisible()){
2243                 dlg.fixedcenter = false;
2244             }
2245             // to big, make it scroll. = But as usual stupid IE does not support
2246             // !important..
2247             
2248             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2249                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2250                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2251             } else {
2252                 bodyEl.dom.style.height = '';
2253                 bodyEl.dom.style.overflowY = '';
2254             }
2255             if (cw > w) {
2256                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2257             } else {
2258                 bodyEl.dom.style.overflowX = '';
2259             }
2260             
2261             dlg.setContentSize(w, bodyEl.getHeight());
2262             if(dlg.isVisible()){
2263                 dlg.fixedcenter = true;
2264             }
2265             return this;
2266         },
2267
2268         /**
2269          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2270          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2271          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2272          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2273          * @return {Roo.MessageBox} This message box
2274          */
2275         updateProgress : function(value, text){
2276             if(text){
2277                 this.updateText(text);
2278             }
2279             if (pp) { // weird bug on my firefox - for some reason this is not defined
2280                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2281             }
2282             return this;
2283         },        
2284
2285         /**
2286          * Returns true if the message box is currently displayed
2287          * @return {Boolean} True if the message box is visible, else false
2288          */
2289         isVisible : function(){
2290             return dlg && dlg.isVisible();  
2291         },
2292
2293         /**
2294          * Hides the message box if it is displayed
2295          */
2296         hide : function(){
2297             if(this.isVisible()){
2298                 dlg.hide();
2299             }  
2300         },
2301
2302         /**
2303          * Displays a new message box, or reinitializes an existing message box, based on the config options
2304          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2305          * The following config object properties are supported:
2306          * <pre>
2307 Property    Type             Description
2308 ----------  ---------------  ------------------------------------------------------------------------------------
2309 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2310                                    closes (defaults to undefined)
2311 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2312                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2313 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2314                                    progress and wait dialogs will ignore this property and always hide the
2315                                    close button as they can only be closed programmatically.
2316 cls               String           A custom CSS class to apply to the message box element
2317 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2318                                    displayed (defaults to 75)
2319 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2320                                    function will be btn (the name of the button that was clicked, if applicable,
2321                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2322                                    Progress and wait dialogs will ignore this option since they do not respond to
2323                                    user actions and can only be closed programmatically, so any required function
2324                                    should be called by the same code after it closes the dialog.
2325 icon              String           A CSS class that provides a background image to be used as an icon for
2326                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2327 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2328 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2329 modal             Boolean          False to allow user interaction with the page while the message box is
2330                                    displayed (defaults to true)
2331 msg               String           A string that will replace the existing message box body text (defaults
2332                                    to the XHTML-compliant non-breaking space character '&#160;')
2333 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2334 progress          Boolean          True to display a progress bar (defaults to false)
2335 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2336 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2337 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2338 title             String           The title text
2339 value             String           The string value to set into the active textbox element if displayed
2340 wait              Boolean          True to display a progress bar (defaults to false)
2341 width             Number           The width of the dialog in pixels
2342 </pre>
2343          *
2344          * Example usage:
2345          * <pre><code>
2346 Roo.Msg.show({
2347    title: 'Address',
2348    msg: 'Please enter your address:',
2349    width: 300,
2350    buttons: Roo.MessageBox.OKCANCEL,
2351    multiline: true,
2352    fn: saveAddress,
2353    animEl: 'addAddressBtn'
2354 });
2355 </code></pre>
2356          * @param {Object} config Configuration options
2357          * @return {Roo.MessageBox} This message box
2358          */
2359         show : function(options)
2360         {
2361             
2362             // this causes nightmares if you show one dialog after another
2363             // especially on callbacks..
2364              
2365             if(this.isVisible()){
2366                 
2367                 this.hide();
2368                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2369                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2370                 Roo.log("New Dialog Message:" +  options.msg )
2371                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2372                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2373                 
2374             }
2375             var d = this.getDialog();
2376             opt = options;
2377             d.setTitle(opt.title || "&#160;");
2378             d.close.setDisplayed(opt.closable !== false);
2379             activeTextEl = textboxEl;
2380             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2381             if(opt.prompt){
2382                 if(opt.multiline){
2383                     textboxEl.hide();
2384                     textareaEl.show();
2385                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2386                         opt.multiline : this.defaultTextHeight);
2387                     activeTextEl = textareaEl;
2388                 }else{
2389                     textboxEl.show();
2390                     textareaEl.hide();
2391                 }
2392             }else{
2393                 textboxEl.hide();
2394                 textareaEl.hide();
2395             }
2396             progressEl.setDisplayed(opt.progress === true);
2397             this.updateProgress(0);
2398             activeTextEl.dom.value = opt.value || "";
2399             if(opt.prompt){
2400                 dlg.setDefaultButton(activeTextEl);
2401             }else{
2402                 var bs = opt.buttons;
2403                 var db = null;
2404                 if(bs && bs.ok){
2405                     db = buttons["ok"];
2406                 }else if(bs && bs.yes){
2407                     db = buttons["yes"];
2408                 }
2409                 dlg.setDefaultButton(db);
2410             }
2411             bwidth = updateButtons(opt.buttons);
2412             this.updateText(opt.msg);
2413             if(opt.cls){
2414                 d.el.addClass(opt.cls);
2415             }
2416             d.proxyDrag = opt.proxyDrag === true;
2417             d.modal = opt.modal !== false;
2418             d.mask = opt.modal !== false ? mask : false;
2419             if(!d.isVisible()){
2420                 // force it to the end of the z-index stack so it gets a cursor in FF
2421                 document.body.appendChild(dlg.el.dom);
2422                 d.animateTarget = null;
2423                 d.show(options.animEl);
2424             }
2425             return this;
2426         },
2427
2428         /**
2429          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2430          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2431          * and closing the message box when the process is complete.
2432          * @param {String} title The title bar text
2433          * @param {String} msg The message box body text
2434          * @return {Roo.MessageBox} This message box
2435          */
2436         progress : function(title, msg){
2437             this.show({
2438                 title : title,
2439                 msg : msg,
2440                 buttons: false,
2441                 progress:true,
2442                 closable:false,
2443                 minWidth: this.minProgressWidth,
2444                 modal : true
2445             });
2446             return this;
2447         },
2448
2449         /**
2450          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2451          * If a callback function is passed it will be called after the user clicks the button, and the
2452          * id of the button that was clicked will be passed as the only parameter to the callback
2453          * (could also be the top-right close button).
2454          * @param {String} title The title bar text
2455          * @param {String} msg The message box body text
2456          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2457          * @param {Object} scope (optional) The scope of the callback function
2458          * @return {Roo.MessageBox} This message box
2459          */
2460         alert : function(title, msg, fn, scope){
2461             this.show({
2462                 title : title,
2463                 msg : msg,
2464                 buttons: this.OK,
2465                 fn: fn,
2466                 scope : scope,
2467                 modal : true
2468             });
2469             return this;
2470         },
2471
2472         /**
2473          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2474          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2475          * You are responsible for closing the message box when the process is complete.
2476          * @param {String} msg The message box body text
2477          * @param {String} title (optional) The title bar text
2478          * @return {Roo.MessageBox} This message box
2479          */
2480         wait : function(msg, title){
2481             this.show({
2482                 title : title,
2483                 msg : msg,
2484                 buttons: false,
2485                 closable:false,
2486                 progress:true,
2487                 modal:true,
2488                 width:300,
2489                 wait:true
2490             });
2491             waitTimer = Roo.TaskMgr.start({
2492                 run: function(i){
2493                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2494                 },
2495                 interval: 1000
2496             });
2497             return this;
2498         },
2499
2500         /**
2501          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2502          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2503          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2504          * @param {String} title The title bar text
2505          * @param {String} msg The message box body text
2506          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2507          * @param {Object} scope (optional) The scope of the callback function
2508          * @return {Roo.MessageBox} This message box
2509          */
2510         confirm : function(title, msg, fn, scope){
2511             this.show({
2512                 title : title,
2513                 msg : msg,
2514                 buttons: this.YESNO,
2515                 fn: fn,
2516                 scope : scope,
2517                 modal : true
2518             });
2519             return this;
2520         },
2521
2522         /**
2523          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2524          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2525          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2526          * (could also be the top-right close button) and the text that was entered will be passed as the two
2527          * parameters to the callback.
2528          * @param {String} title The title bar text
2529          * @param {String} msg The message box body text
2530          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2531          * @param {Object} scope (optional) The scope of the callback function
2532          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2533          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2534          * @return {Roo.MessageBox} This message box
2535          */
2536         prompt : function(title, msg, fn, scope, multiline){
2537             this.show({
2538                 title : title,
2539                 msg : msg,
2540                 buttons: this.OKCANCEL,
2541                 fn: fn,
2542                 minWidth:250,
2543                 scope : scope,
2544                 prompt:true,
2545                 multiline: multiline,
2546                 modal : true
2547             });
2548             return this;
2549         },
2550
2551         /**
2552          * Button config that displays a single OK button
2553          * @type Object
2554          */
2555         OK : {ok:true},
2556         /**
2557          * Button config that displays Yes and No buttons
2558          * @type Object
2559          */
2560         YESNO : {yes:true, no:true},
2561         /**
2562          * Button config that displays OK and Cancel buttons
2563          * @type Object
2564          */
2565         OKCANCEL : {ok:true, cancel:true},
2566         /**
2567          * Button config that displays Yes, No and Cancel buttons
2568          * @type Object
2569          */
2570         YESNOCANCEL : {yes:true, no:true, cancel:true},
2571
2572         /**
2573          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2574          * @type Number
2575          */
2576         defaultTextHeight : 75,
2577         /**
2578          * The maximum width in pixels of the message box (defaults to 600)
2579          * @type Number
2580          */
2581         maxWidth : 600,
2582         /**
2583          * The minimum width in pixels of the message box (defaults to 100)
2584          * @type Number
2585          */
2586         minWidth : 100,
2587         /**
2588          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2589          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2590          * @type Number
2591          */
2592         minProgressWidth : 250,
2593         /**
2594          * An object containing the default button text strings that can be overriden for localized language support.
2595          * Supported properties are: ok, cancel, yes and no.
2596          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2597          * @type Object
2598          */
2599         buttonText : {
2600             ok : "OK",
2601             cancel : "Cancel",
2602             yes : "Yes",
2603             no : "No"
2604         }
2605     };
2606 }();
2607
2608 /**
2609  * Shorthand for {@link Roo.MessageBox}
2610  */
2611 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2612 Roo.Msg = Roo.Msg || Roo.MessageBox;
2613 /*
2614  * - LGPL
2615  *
2616  * navbar
2617  * 
2618  */
2619
2620 /**
2621  * @class Roo.bootstrap.Navbar
2622  * @extends Roo.bootstrap.Component
2623  * Bootstrap Navbar class
2624  * @cfg {Boolean} sidebar has side bar
2625  * @cfg {Boolean} bar is a bar?
2626  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2627  * @cfg {String} brand what is brand
2628  * @cfg {Boolean} inverse is inverted color
2629  * @cfg {String} type (nav | pills | tabs)
2630  * @cfg {Boolean} arrangement stacked | justified
2631  * @cfg {String} align (left | right) alignment
2632  * @cfg {String} brand_href href of the brand
2633  * @cfg {Boolean} main (true|false) main nav bar? default false
2634  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2635  *
2636  * 
2637  * @constructor
2638  * Create a new Navbar
2639  * @param {Object} config The config object
2640  */
2641
2642
2643 Roo.bootstrap.Navbar = function(config){
2644     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2645    
2646     
2647 };
2648
2649 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2650     
2651     sidebar: false,
2652     
2653     bar: false,
2654     brand: '',
2655     inverse: false,
2656     position: '',
2657     align : false,
2658     type: 'nav',
2659     arrangement: '',
2660     brand_href: false,
2661     main : false,
2662     loadMask : false,
2663     
2664     
2665     // private
2666     navItems : false,
2667     
2668     getAutoCreate : function(){
2669         var cfg = {
2670             cls : 'navbar'
2671         };
2672         
2673         if (this.sidebar === true) {
2674             cfg = {
2675                 tag: 'div',
2676                 cls: 'sidebar-nav'
2677             };
2678             return cfg;
2679         }
2680         
2681         if (this.bar === true) {
2682             cfg = {
2683                 tag: 'nav',
2684                 cls: 'navbar',
2685                 role: 'navigation',
2686                 cn: [
2687                     {
2688                         tag: 'div',
2689                         cls: 'navbar-header',
2690                         cn: [
2691                             {
2692                             tag: 'button',
2693                             type: 'button',
2694                             cls: 'navbar-toggle',
2695                             'data-toggle': 'collapse',
2696                             cn: [
2697                                 {
2698                                     tag: 'span',
2699                                     cls: 'sr-only',
2700                                     html: 'Toggle navigation'
2701                                 },
2702                                 {
2703                                     tag: 'span',
2704                                     cls: 'icon-bar'
2705                                 },
2706                                 {
2707                                     tag: 'span',
2708                                     cls: 'icon-bar'
2709                                 },
2710                                 {
2711                                     tag: 'span',
2712                                     cls: 'icon-bar'
2713                                 }
2714                             ]
2715                             }
2716                         ]
2717                     },
2718                     {
2719                     tag: 'div',
2720                     cls: 'collapse navbar-collapse'
2721                     }
2722                 ]
2723             };
2724             
2725             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2726             
2727             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2728                 cfg.cls += ' navbar-' + this.position;
2729                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2730             }
2731             
2732             if (this.brand !== '') {
2733                 cfg.cn[0].cn.push({
2734                     tag: 'a',
2735                     href: this.brand_href ? this.brand_href : '#',
2736                     cls: 'navbar-brand',
2737                     cn: [
2738                     this.brand
2739                     ]
2740                 });
2741             }
2742             
2743             if(this.main){
2744                 cfg.cls += ' main-nav';
2745             }
2746             
2747             
2748             return cfg;
2749         
2750         } else if (this.bar === false) {
2751             
2752         } else {
2753             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2754         }
2755         
2756         cfg.cn = [
2757             {
2758                 cls: 'nav',
2759                 tag : 'ul'
2760             }
2761         ];
2762         
2763         if (['tabs','pills'].indexOf(this.type)!==-1) {
2764             cfg.cn[0].cls += ' nav-' + this.type
2765         } else {
2766             if (this.type!=='nav') {
2767             Roo.log('nav type must be nav/tabs/pills')
2768             }
2769             cfg.cn[0].cls += ' navbar-nav'
2770         }
2771         
2772         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2773             cfg.cn[0].cls += ' nav-' + this.arrangement;
2774         }
2775         
2776         if (this.align === 'right') {
2777             cfg.cn[0].cls += ' navbar-right';
2778         }
2779         if (this.inverse) {
2780             cfg.cls += ' navbar-inverse';
2781             
2782         }
2783         
2784         
2785         return cfg;
2786     },
2787     
2788     initEvents :function ()
2789     {
2790         //Roo.log(this.el.select('.navbar-toggle',true));
2791         this.el.select('.navbar-toggle',true).on('click', function() {
2792            // Roo.log('click');
2793             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2794         }, this);
2795         
2796         var mark = {
2797             tag: "div",
2798             cls:"x-dlg-mask"
2799         }
2800         
2801         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2802         
2803         var size = this.el.getSize();
2804         this.maskEl.setSize(size.width, size.height);
2805         this.maskEl.enableDisplayMode("block");
2806         this.maskEl.hide();
2807         
2808         if(this.loadMask){
2809             this.maskEl.show();
2810         }
2811     },
2812     
2813     
2814     getChildContainer : function()
2815     {
2816         if (this.bar === true) {
2817             return this.el.select('.collapse',true).first();
2818         }
2819         
2820         return this.el;
2821     },
2822     
2823     mask : function()
2824     {
2825         this.maskEl.show();
2826     },
2827     
2828     unmask : function()
2829     {
2830         this.maskEl.hide();
2831     }
2832     
2833     
2834     
2835 });
2836
2837
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * nav group
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.NavGroup
2850  * @extends Roo.bootstrap.Component
2851  * Bootstrap NavGroup class
2852  * @cfg {String} align left | right
2853  * @cfg {Boolean} inverse false | true
2854  * @cfg {String} type (nav|pills|tab) default nav
2855  * @cfg {String} navId - reference Id for navbar.
2856
2857  * 
2858  * @constructor
2859  * Create a new nav group
2860  * @param {Object} config The config object
2861  */
2862
2863 Roo.bootstrap.NavGroup = function(config){
2864     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2865     this.navItems = [];
2866     Roo.bootstrap.NavGroup.register(this);
2867      this.addEvents({
2868         /**
2869              * @event changed
2870              * Fires when the active item changes
2871              * @param {Roo.bootstrap.NavGroup} this
2872              * @param {Roo.bootstrap.Navbar.Item} item The item selected
2873              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
2874          */
2875         'changed': true
2876      });
2877     
2878 };
2879
2880 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2881     
2882     align: '',
2883     inverse: false,
2884     form: false,
2885     type: 'nav',
2886     navId : '',
2887     // private
2888     
2889     navItems : false,
2890     
2891     getAutoCreate : function()
2892     {
2893         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2894         
2895         cfg = {
2896             tag : 'ul',
2897             cls: 'nav' 
2898         }
2899         
2900         if (['tabs','pills'].indexOf(this.type)!==-1) {
2901             cfg.cls += ' nav-' + this.type
2902         } else {
2903             if (this.type!=='nav') {
2904                 Roo.log('nav type must be nav/tabs/pills')
2905             }
2906             cfg.cls += ' navbar-nav'
2907         }
2908         
2909         if (this.parent().sidebar === true) {
2910             cfg = {
2911                 tag: 'ul',
2912                 cls: 'dashboard-menu'
2913             }
2914             
2915             return cfg;
2916         }
2917         
2918         if (this.form === true) {
2919             cfg = {
2920                 tag: 'form',
2921                 cls: 'navbar-form'
2922             }
2923             
2924             if (this.align === 'right') {
2925                 cfg.cls += ' navbar-right';
2926             } else {
2927                 cfg.cls += ' navbar-left';
2928             }
2929         }
2930         
2931         if (this.align === 'right') {
2932             cfg.cls += ' navbar-right';
2933         }
2934         
2935         if (this.inverse) {
2936             cfg.cls += ' navbar-inverse';
2937             
2938         }
2939         
2940         
2941         return cfg;
2942     },
2943     
2944     setActiveItem : function(item)
2945     {
2946         var prev = false;
2947         Roo.each(this.navItems, function(v){
2948             if (v == item) {
2949                 return ;
2950             }
2951             if (v.isActive()) {
2952                 v.setActive(false, true);
2953                 prev = v;
2954                 
2955             }
2956             
2957         });
2958
2959         item.setActive(true, true);
2960         this.fireEvent('changed', this, item, prev);
2961         
2962         
2963     },
2964     
2965     
2966     register : function(item)
2967     {
2968         this.navItems.push( item);
2969         item.navId = this.navId;
2970     
2971     },
2972     getNavItem: function(tabId)
2973     {
2974         var ret = false;
2975         Roo.each(this.navItems, function(e) {
2976             if (e.tabId == tabId) {
2977                ret =  e;
2978                return false;
2979             }
2980             return true;
2981             
2982         });
2983         return ret;
2984     }
2985 });
2986
2987  
2988 Roo.apply(Roo.bootstrap.NavGroup, {
2989     
2990     groups: {},
2991     
2992     register : function(navgrp)
2993     {
2994         this.groups[navgrp.navId] = navgrp;
2995         
2996     },
2997     get: function(navId) {
2998         return this.groups[navId];
2999     }
3000     
3001     
3002     
3003 });
3004
3005  /*
3006  * - LGPL
3007  *
3008  * row
3009  * 
3010  */
3011
3012 /**
3013  * @class Roo.bootstrap.Navbar.Item
3014  * @extends Roo.bootstrap.Component
3015  * Bootstrap Navbar.Button class
3016  * @cfg {String} href  link to
3017  * @cfg {String} html content of button
3018  * @cfg {String} badge text inside badge
3019  * @cfg {String} glyphicon name of glyphicon
3020  * @cfg {String} icon name of font awesome icon
3021  * @cfg {Boolean} active Is item active
3022  * @cfg {Boolean} preventDefault (true | false) default false
3023  * @cfg {String} tabId the tab that this item activates.
3024   
3025  * @constructor
3026  * Create a new Navbar Button
3027  * @param {Object} config The config object
3028  */
3029 Roo.bootstrap.Navbar.Item = function(config){
3030     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3031     this.addEvents({
3032         // raw events
3033         /**
3034          * @event click
3035          * The raw click event for the entire grid.
3036          * @param {Roo.EventObject} e
3037          */
3038         "click" : true,
3039          /**
3040             * @event changed
3041             * Fires when the active item active state changes
3042             * @param {Roo.bootstrap.Navbar.Item} this
3043             * @param {boolean} state the new state
3044              
3045          */
3046         'changed': true
3047     });
3048    
3049 };
3050
3051 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3052     
3053     href: false,
3054     html: '',
3055     badge: '',
3056     icon: false,
3057     glyphicon: false,
3058     active: false,
3059     preventDefault : false,
3060     tabId : false,
3061     
3062     getAutoCreate : function(){
3063         
3064         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3065         
3066         if (this.parent().parent().sidebar === true) {
3067             cfg = {
3068                 tag: 'li',
3069                 cls: '',
3070                 cn: [
3071                     {
3072                         tag: 'p',
3073                         cls: ''
3074                     }
3075                 ]
3076             }
3077             
3078             if (this.html) {
3079                 cfg.cn[0].html = this.html;
3080             }
3081             
3082             if (this.active) {
3083                 this.cls += ' active';
3084             }
3085             
3086             if (this.menu) {
3087                 cfg.cn[0].cls += ' dropdown-toggle';
3088                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3089             }
3090             
3091             if (this.href) {
3092                 cfg.cn[0].tag = 'a',
3093                 cfg.cn[0].href = this.href;
3094             }
3095             
3096             if (this.glyphicon) {
3097                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3098             }
3099             
3100             if (this.icon) {
3101                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3102             }
3103             
3104             return cfg;
3105         }
3106         
3107         cfg = {
3108             tag: 'li',
3109             cls: 'nav-item'
3110         }
3111         
3112         if (this.active) {
3113             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3114         }
3115             
3116         cfg.cn = [
3117             {
3118                 tag: 'p',
3119                 html: 'Text'
3120             }
3121         ];
3122         
3123         if (this.glyphicon) {
3124             if(cfg.html){cfg.html = ' ' + this.html};
3125             cfg.cn=[
3126                 {
3127                     tag: 'span',
3128                     cls: 'glyphicon glyphicon-' + this.glyphicon
3129                 }
3130             ];
3131         }
3132         
3133         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3134         
3135         if (this.menu) {
3136             cfg.cn[0].tag='a';
3137             cfg.cn[0].href='#';
3138             cfg.cn[0].html += " <span class='caret'></span>";
3139         //}else if (!this.href) {
3140         //    cfg.cn[0].tag='p';
3141         //    cfg.cn[0].cls='navbar-text';
3142         } else {
3143             cfg.cn[0].tag='a';
3144             cfg.cn[0].href=this.href||'#';
3145             cfg.cn[0].html=this.html;
3146         }
3147         
3148         if (this.badge !== '') {
3149             
3150             cfg.cn[0].cn=[
3151                 cfg.cn[0].html + ' ',
3152                 {
3153                     tag: 'span',
3154                     cls: 'badge',
3155                     html: this.badge
3156                 }
3157             ];
3158             cfg.cn[0].html=''
3159         }
3160          
3161         if (this.icon) {
3162             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3163         }
3164         
3165         return cfg;
3166     },
3167     initEvents: function() {
3168        // Roo.log('init events?');
3169        // Roo.log(this.el.dom);
3170         this.el.select('a',true).on('click', this.onClick, this);
3171         // at this point parent should be available..
3172         this.parent().register(this);
3173     },
3174     
3175     onClick : function(e)
3176     {
3177         if(this.preventDefault){
3178             e.preventDefault();
3179         }
3180         
3181         if(this.fireEvent('click', this, e) === false){
3182             return;
3183         };
3184         
3185         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3186              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3187                 this.parent().setActiveItem(this);
3188             }
3189             
3190             
3191             
3192         } 
3193     },
3194     
3195     isActive: function () {
3196         return this.active
3197     },
3198     setActive : function(state, fire)
3199     {
3200         this.active = state;
3201         if (!state ) {
3202             this.el.removeClass('active');
3203         } else if (!this.el.hasClass('active')) {
3204             this.el.addClass('active');
3205         }
3206         if (fire) {
3207             this.fireEvent('changed', this, state);
3208         }
3209         
3210         
3211     }
3212      // this should not be here...
3213  
3214 });
3215  
3216
3217  /*
3218  * - LGPL
3219  *
3220  * row
3221  * 
3222  */
3223
3224 /**
3225  * @class Roo.bootstrap.Row
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Row class (contains columns...)
3228  * 
3229  * @constructor
3230  * Create a new Row
3231  * @param {Object} config The config object
3232  */
3233
3234 Roo.bootstrap.Row = function(config){
3235     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3236 };
3237
3238 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3239     
3240     getAutoCreate : function(){
3241        return {
3242             cls: 'row clearfix'
3243        };
3244     }
3245     
3246     
3247 });
3248
3249  
3250
3251  /*
3252  * - LGPL
3253  *
3254  * element
3255  * 
3256  */
3257
3258 /**
3259  * @class Roo.bootstrap.Element
3260  * @extends Roo.bootstrap.Component
3261  * Bootstrap Element class
3262  * @cfg {String} html contents of the element
3263  * @cfg {String} tag tag of the element
3264  * @cfg {String} cls class of the element
3265  * 
3266  * @constructor
3267  * Create a new Element
3268  * @param {Object} config The config object
3269  */
3270
3271 Roo.bootstrap.Element = function(config){
3272     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3273 };
3274
3275 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3276     
3277     tag: 'div',
3278     cls: '',
3279     html: '',
3280      
3281     
3282     getAutoCreate : function(){
3283         
3284         var cfg = {
3285             tag: this.tag,
3286             cls: this.cls,
3287             html: this.html
3288         }
3289         
3290         
3291         
3292         return cfg;
3293     }
3294    
3295 });
3296
3297  
3298
3299  /*
3300  * - LGPL
3301  *
3302  * pagination
3303  * 
3304  */
3305
3306 /**
3307  * @class Roo.bootstrap.Pagination
3308  * @extends Roo.bootstrap.Component
3309  * Bootstrap Pagination class
3310  * @cfg {String} size xs | sm | md | lg
3311  * @cfg {Boolean} inverse false | true
3312  * 
3313  * @constructor
3314  * Create a new Pagination
3315  * @param {Object} config The config object
3316  */
3317
3318 Roo.bootstrap.Pagination = function(config){
3319     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3320 };
3321
3322 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3323     
3324     cls: false,
3325     size: false,
3326     inverse: false,
3327     
3328     getAutoCreate : function(){
3329         var cfg = {
3330             tag: 'ul',
3331                 cls: 'pagination'
3332         };
3333         if (this.inverse) {
3334             cfg.cls += ' inverse';
3335         }
3336         if (this.html) {
3337             cfg.html=this.html;
3338         }
3339         if (this.cls) {
3340             cfg.cls += " " + this.cls;
3341         }
3342         return cfg;
3343     }
3344    
3345 });
3346
3347  
3348
3349  /*
3350  * - LGPL
3351  *
3352  * Pagination item
3353  * 
3354  */
3355
3356
3357 /**
3358  * @class Roo.bootstrap.PaginationItem
3359  * @extends Roo.bootstrap.Component
3360  * Bootstrap PaginationItem class
3361  * @cfg {String} html text
3362  * @cfg {String} href the link
3363  * @cfg {Boolean} preventDefault (true | false) default true
3364  * @cfg {Boolean} active (true | false) default false
3365  * 
3366  * 
3367  * @constructor
3368  * Create a new PaginationItem
3369  * @param {Object} config The config object
3370  */
3371
3372
3373 Roo.bootstrap.PaginationItem = function(config){
3374     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3375     this.addEvents({
3376         // raw events
3377         /**
3378          * @event click
3379          * The raw click event for the entire grid.
3380          * @param {Roo.EventObject} e
3381          */
3382         "click" : true
3383     });
3384 };
3385
3386 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3387     
3388     href : false,
3389     html : false,
3390     preventDefault: true,
3391     active : false,
3392     cls : false,
3393     
3394     getAutoCreate : function(){
3395         var cfg= {
3396             tag: 'li',
3397             cn: [
3398                 {
3399                     tag : 'a',
3400                     href : this.href ? this.href : '#',
3401                     html : this.html ? this.html : ''
3402                 }
3403             ]
3404         };
3405         
3406         if(this.cls){
3407             cfg.cls = this.cls;
3408         }
3409         
3410         if(this.active){
3411             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3412         }
3413         
3414         return cfg;
3415     },
3416     
3417     initEvents: function() {
3418         
3419         this.el.on('click', this.onClick, this);
3420         
3421     },
3422     onClick : function(e)
3423     {
3424         Roo.log('PaginationItem on click ');
3425         if(this.preventDefault){
3426             e.preventDefault();
3427         }
3428         
3429         this.fireEvent('click', this, e);
3430     }
3431    
3432 });
3433
3434  
3435
3436  /*
3437  * - LGPL
3438  *
3439  * slider
3440  * 
3441  */
3442
3443
3444 /**
3445  * @class Roo.bootstrap.Slider
3446  * @extends Roo.bootstrap.Component
3447  * Bootstrap Slider class
3448  *    
3449  * @constructor
3450  * Create a new Slider
3451  * @param {Object} config The config object
3452  */
3453
3454 Roo.bootstrap.Slider = function(config){
3455     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3456 };
3457
3458 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3459     
3460     getAutoCreate : function(){
3461         
3462         var cfg = {
3463             tag: 'div',
3464             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3465             cn: [
3466                 {
3467                     tag: 'a',
3468                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3469                 }
3470             ]
3471         }
3472         
3473         return cfg;
3474     }
3475    
3476 });
3477
3478  /*
3479  * - LGPL
3480  *
3481  * table
3482  * 
3483  */
3484
3485 /**
3486  * @class Roo.bootstrap.Table
3487  * @extends Roo.bootstrap.Component
3488  * Bootstrap Table class
3489  * @cfg {String} cls table class
3490  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3491  * @cfg {String} bgcolor Specifies the background color for a table
3492  * @cfg {Number} border Specifies whether the table cells should have borders or not
3493  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3494  * @cfg {Number} cellspacing Specifies the space between cells
3495  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3496  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3497  * @cfg {String} sortable Specifies that the table should be sortable
3498  * @cfg {String} summary Specifies a summary of the content of a table
3499  * @cfg {Number} width Specifies the width of a table
3500  * 
3501  * @cfg {boolean} striped Should the rows be alternative striped
3502  * @cfg {boolean} bordered Add borders to the table
3503  * @cfg {boolean} hover Add hover highlighting
3504  * @cfg {boolean} condensed Format condensed
3505  * @cfg {boolean} responsive Format condensed
3506  *
3507  
3508  
3509  * 
3510  * @constructor
3511  * Create a new Table
3512  * @param {Object} config The config object
3513  */
3514
3515 Roo.bootstrap.Table = function(config){
3516     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3517     
3518     if (this.sm) {
3519         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3520         this.sm = this.selModel;
3521         this.sm.xmodule = this.xmodule || false;
3522     }
3523     if (this.cm && typeof(this.cm.config) == 'undefined') {
3524         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3525         this.cm = this.colModel;
3526         this.cm.xmodule = this.xmodule || false;
3527     }
3528     if (this.store) {
3529         this.store= Roo.factory(this.store, Roo.data);
3530         this.ds = this.store;
3531         this.ds.xmodule = this.xmodule || false;
3532          
3533     }
3534 };
3535
3536 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3537     
3538     cls: false,
3539     align: false,
3540     bgcolor: false,
3541     border: false,
3542     cellpadding: false,
3543     cellspacing: false,
3544     frame: false,
3545     rules: false,
3546     sortable: false,
3547     summary: false,
3548     width: false,
3549     striped : false,
3550     bordered: false,
3551     hover:  false,
3552     condensed : false,
3553     responsive : false,
3554     sm : false,
3555     cm : false,
3556     store : false,
3557     
3558     getAutoCreate : function(){
3559         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3560         
3561         cfg = {
3562             tag: 'table',
3563             cls : 'table',
3564             cn : []
3565         }
3566             
3567         if (this.striped) {
3568             cfg.cls += ' table-striped';
3569         }
3570         if (this.hover) {
3571             cfg.cls += ' table-hover';
3572         }
3573         if (this.bordered) {
3574             cfg.cls += ' table-bordered';
3575         }
3576         if (this.condensed) {
3577             cfg.cls += ' table-condensed';
3578         }
3579         if (this.responsive) {
3580             cfg.cls += ' table-responsive';
3581         }
3582         
3583           
3584         
3585         
3586         if (this.cls) {
3587             cfg.cls+=  ' ' +this.cls;
3588         }
3589         
3590         // this lot should be simplifed...
3591         
3592         if (this.align) {
3593             cfg.align=this.align;
3594         }
3595         if (this.bgcolor) {
3596             cfg.bgcolor=this.bgcolor;
3597         }
3598         if (this.border) {
3599             cfg.border=this.border;
3600         }
3601         if (this.cellpadding) {
3602             cfg.cellpadding=this.cellpadding;
3603         }
3604         if (this.cellspacing) {
3605             cfg.cellspacing=this.cellspacing;
3606         }
3607         if (this.frame) {
3608             cfg.frame=this.frame;
3609         }
3610         if (this.rules) {
3611             cfg.rules=this.rules;
3612         }
3613         if (this.sortable) {
3614             cfg.sortable=this.sortable;
3615         }
3616         if (this.summary) {
3617             cfg.summary=this.summary;
3618         }
3619         if (this.width) {
3620             cfg.width=this.width;
3621         }
3622         
3623         if(this.store || this.cm){
3624             cfg.cn.push(this.renderHeader());
3625             cfg.cn.push(this.renderBody());
3626             cfg.cn.push(this.renderFooter());
3627             
3628             cfg.cls+=  ' TableGrid';
3629         }
3630         
3631         return cfg;
3632     },
3633 //    
3634 //    initTableGrid : function()
3635 //    {
3636 //        var cfg = {};
3637 //        
3638 //        var header = {
3639 //            tag: 'thead',
3640 //            cn : []
3641 //        };
3642 //        
3643 //        var cm = this.cm;
3644 //        
3645 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3646 //            header.cn.push({
3647 //                tag: 'th',
3648 //                html: cm.getColumnHeader(i)
3649 //            })
3650 //        }
3651 //        
3652 //        cfg.push(header);
3653 //        
3654 //        return cfg;
3655 //        
3656 //        
3657 //    },
3658     
3659     initEvents : function()
3660     {   
3661         if(!this.store || !this.cm){
3662             return;
3663         }
3664         
3665         Roo.log('initEvents with ds!!!!');
3666         
3667         var _this = this;
3668         
3669         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3670             e.on('click', _this.sort, _this);
3671         });
3672 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3673 //        this.maskEl.enableDisplayMode("block");
3674 //        this.maskEl.show();
3675         
3676         this.store.on('load', this.onLoad, this);
3677         this.store.on('beforeload', this.onBeforeLoad, this);
3678         
3679         this.store.load();
3680         
3681         
3682         
3683     },
3684     
3685     sort : function(e,el)
3686     {
3687         var col = Roo.get(el)
3688         
3689         if(!col.hasClass('sortable')){
3690             return;
3691         }
3692         
3693         var sort = col.attr('sort');
3694         var dir = 'ASC';
3695         
3696         if(col.hasClass('glyphicon-arrow-up')){
3697             dir = 'DESC';
3698         }
3699         
3700         this.store.sortInfo = {field : sort, direction : dir};
3701         
3702         this.store.load();
3703     },
3704     
3705     renderHeader : function()
3706     {
3707         var header = {
3708             tag: 'thead',
3709             cn : []
3710         };
3711         
3712         var cm = this.cm;
3713         
3714         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3715             
3716             var config = cm.config[i];
3717             
3718             var c = {
3719                 tag: 'th',
3720                 html: cm.getColumnHeader(i)
3721             };
3722             
3723             if(typeof(config.dataIndex) != 'undefined'){
3724                 c.sort = config.dataIndex;
3725             }
3726             
3727             if(typeof(config.sortable) != 'undefined' && config.sortable){
3728                 c.cls = 'sortable';
3729             }
3730             
3731             if(typeof(config.width) != 'undefined'){
3732                 c.style = 'width:' + config.width + 'px';
3733             }
3734             
3735             header.cn.push(c)
3736         }
3737         
3738         return header;
3739     },
3740     
3741     renderBody : function()
3742     {
3743         var body = {
3744             tag: 'tbody',
3745             cn : []
3746         };
3747         
3748         return body;
3749     },
3750     
3751     renderFooter : function()
3752     {
3753         var footer = {
3754             tag: 'tfoot',
3755             cn : []
3756         };
3757         
3758         return footer;
3759     },
3760     
3761     onLoad : function()
3762     {
3763         Roo.log('ds onload');
3764         
3765         var _this = this;
3766         var cm = this.cm;
3767         
3768         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3769             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3770             
3771             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3772                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3773             }
3774             
3775             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3776                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3777             }
3778         });
3779         
3780         var tbody = this.el.select('tbody', true).first();
3781         
3782         var renders = [];
3783         
3784         if(this.store.getCount() > 0){
3785             this.store.data.each(function(d){
3786                 var row = {
3787                     tag : 'tr',
3788                     cn : []
3789                 };
3790                 
3791                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3792                     var renderer = cm.getRenderer(i);
3793                     var config = cm.config[i];
3794                     var value = '';
3795                     var id = Roo.id();
3796                     
3797                     if(typeof(renderer) !== 'undefined'){
3798                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3799                     }
3800                     
3801                     if(typeof(value) === 'object'){
3802                         renders.push({
3803                             id : id,
3804                             cfg : value 
3805                         })
3806                     }
3807                     
3808                     var td = {
3809                         tag: 'td',
3810                         id: id,
3811                         html: (typeof(value) === 'object') ? '' : value
3812                     };
3813                     
3814                     if(typeof(config.width) != 'undefined'){
3815                         td.style = 'width:' +  config.width + 'px';
3816                     }
3817                     
3818                     row.cn.push(td);
3819                    
3820                 }
3821                 
3822                 tbody.createChild(row);
3823                 
3824             });
3825         }
3826         
3827         
3828         if(renders.length){
3829             var _this = this;
3830             Roo.each(renders, function(r){
3831                 _this.renderColumn(r);
3832             })
3833         }
3834 //        
3835 //        if(this.loadMask){
3836 //            this.maskEl.hide();
3837 //        }
3838     },
3839     
3840     onBeforeLoad : function()
3841     {
3842         Roo.log('ds onBeforeLoad');
3843         
3844         this.clear();
3845         
3846 //        if(this.loadMask){
3847 //            this.maskEl.show();
3848 //        }
3849     },
3850     
3851     clear : function()
3852     {
3853         this.el.select('tbody', true).first().dom.innerHTML = '';
3854     },
3855     
3856     getSelectionModel : function(){
3857         if(!this.selModel){
3858             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3859         }
3860         return this.selModel;
3861     },
3862     
3863     renderColumn : function(r)
3864     {
3865         var _this = this;
3866         r.cfg.render(Roo.get(r.id));
3867         
3868         if(r.cfg.cn){
3869             Roo.each(r.cfg.cn, function(c){
3870                 var child = {
3871                     id: r.id,
3872                     cfg: c
3873                 }
3874                 _this.renderColumn(child);
3875             })
3876         }
3877     }
3878    
3879 });
3880
3881  
3882
3883  /*
3884  * - LGPL
3885  *
3886  * table cell
3887  * 
3888  */
3889
3890 /**
3891  * @class Roo.bootstrap.TableCell
3892  * @extends Roo.bootstrap.Component
3893  * Bootstrap TableCell class
3894  * @cfg {String} html cell contain text
3895  * @cfg {String} cls cell class
3896  * @cfg {String} tag cell tag (td|th) default td
3897  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3898  * @cfg {String} align Aligns the content in a cell
3899  * @cfg {String} axis Categorizes cells
3900  * @cfg {String} bgcolor Specifies the background color of a cell
3901  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3902  * @cfg {Number} colspan Specifies the number of columns a cell should span
3903  * @cfg {String} headers Specifies one or more header cells a cell is related to
3904  * @cfg {Number} height Sets the height of a cell
3905  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3906  * @cfg {Number} rowspan Sets the number of rows a cell should span
3907  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3908  * @cfg {String} valign Vertical aligns the content in a cell
3909  * @cfg {Number} width Specifies the width of a cell
3910  * 
3911  * @constructor
3912  * Create a new TableCell
3913  * @param {Object} config The config object
3914  */
3915
3916 Roo.bootstrap.TableCell = function(config){
3917     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3918 };
3919
3920 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3921     
3922     html: false,
3923     cls: false,
3924     tag: false,
3925     abbr: false,
3926     align: false,
3927     axis: false,
3928     bgcolor: false,
3929     charoff: false,
3930     colspan: false,
3931     headers: false,
3932     height: false,
3933     nowrap: false,
3934     rowspan: false,
3935     scope: false,
3936     valign: false,
3937     width: false,
3938     
3939     
3940     getAutoCreate : function(){
3941         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3942         
3943         cfg = {
3944             tag: 'td'
3945         }
3946         
3947         if(this.tag){
3948             cfg.tag = this.tag;
3949         }
3950         
3951         if (this.html) {
3952             cfg.html=this.html
3953         }
3954         if (this.cls) {
3955             cfg.cls=this.cls
3956         }
3957         if (this.abbr) {
3958             cfg.abbr=this.abbr
3959         }
3960         if (this.align) {
3961             cfg.align=this.align
3962         }
3963         if (this.axis) {
3964             cfg.axis=this.axis
3965         }
3966         if (this.bgcolor) {
3967             cfg.bgcolor=this.bgcolor
3968         }
3969         if (this.charoff) {
3970             cfg.charoff=this.charoff
3971         }
3972         if (this.colspan) {
3973             cfg.colspan=this.colspan
3974         }
3975         if (this.headers) {
3976             cfg.headers=this.headers
3977         }
3978         if (this.height) {
3979             cfg.height=this.height
3980         }
3981         if (this.nowrap) {
3982             cfg.nowrap=this.nowrap
3983         }
3984         if (this.rowspan) {
3985             cfg.rowspan=this.rowspan
3986         }
3987         if (this.scope) {
3988             cfg.scope=this.scope
3989         }
3990         if (this.valign) {
3991             cfg.valign=this.valign
3992         }
3993         if (this.width) {
3994             cfg.width=this.width
3995         }
3996         
3997         
3998         return cfg;
3999     }
4000    
4001 });
4002
4003  
4004
4005  /*
4006  * - LGPL
4007  *
4008  * table row
4009  * 
4010  */
4011
4012 /**
4013  * @class Roo.bootstrap.TableRow
4014  * @extends Roo.bootstrap.Component
4015  * Bootstrap TableRow class
4016  * @cfg {String} cls row class
4017  * @cfg {String} align Aligns the content in a table row
4018  * @cfg {String} bgcolor Specifies a background color for a table row
4019  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4020  * @cfg {String} valign Vertical aligns the content in a table row
4021  * 
4022  * @constructor
4023  * Create a new TableRow
4024  * @param {Object} config The config object
4025  */
4026
4027 Roo.bootstrap.TableRow = function(config){
4028     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4029 };
4030
4031 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4032     
4033     cls: false,
4034     align: false,
4035     bgcolor: false,
4036     charoff: false,
4037     valign: false,
4038     
4039     getAutoCreate : function(){
4040         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4041         
4042         cfg = {
4043             tag: 'tr'
4044         }
4045             
4046         if(this.cls){
4047             cfg.cls = this.cls;
4048         }
4049         if(this.align){
4050             cfg.align = this.align;
4051         }
4052         if(this.bgcolor){
4053             cfg.bgcolor = this.bgcolor;
4054         }
4055         if(this.charoff){
4056             cfg.charoff = this.charoff;
4057         }
4058         if(this.valign){
4059             cfg.valign = this.valign;
4060         }
4061         
4062         return cfg;
4063     }
4064    
4065 });
4066
4067  
4068
4069  /*
4070  * - LGPL
4071  *
4072  * table body
4073  * 
4074  */
4075
4076 /**
4077  * @class Roo.bootstrap.TableBody
4078  * @extends Roo.bootstrap.Component
4079  * Bootstrap TableBody class
4080  * @cfg {String} cls element class
4081  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4082  * @cfg {String} align Aligns the content inside the element
4083  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4084  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4085  * 
4086  * @constructor
4087  * Create a new TableBody
4088  * @param {Object} config The config object
4089  */
4090
4091 Roo.bootstrap.TableBody = function(config){
4092     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4093 };
4094
4095 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4096     
4097     cls: false,
4098     tag: false,
4099     align: false,
4100     charoff: false,
4101     valign: false,
4102     
4103     getAutoCreate : function(){
4104         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4105         
4106         cfg = {
4107             tag: 'tbody'
4108         }
4109             
4110         if (this.cls) {
4111             cfg.cls=this.cls
4112         }
4113         if(this.tag){
4114             cfg.tag = this.tag;
4115         }
4116         
4117         if(this.align){
4118             cfg.align = this.align;
4119         }
4120         if(this.charoff){
4121             cfg.charoff = this.charoff;
4122         }
4123         if(this.valign){
4124             cfg.valign = this.valign;
4125         }
4126         
4127         return cfg;
4128     }
4129     
4130     
4131 //    initEvents : function()
4132 //    {
4133 //        
4134 //        if(!this.store){
4135 //            return;
4136 //        }
4137 //        
4138 //        this.store = Roo.factory(this.store, Roo.data);
4139 //        this.store.on('load', this.onLoad, this);
4140 //        
4141 //        this.store.load();
4142 //        
4143 //    },
4144 //    
4145 //    onLoad: function () 
4146 //    {   
4147 //        this.fireEvent('load', this);
4148 //    }
4149 //    
4150 //   
4151 });
4152
4153  
4154
4155  /*
4156  * Based on:
4157  * Ext JS Library 1.1.1
4158  * Copyright(c) 2006-2007, Ext JS, LLC.
4159  *
4160  * Originally Released Under LGPL - original licence link has changed is not relivant.
4161  *
4162  * Fork - LGPL
4163  * <script type="text/javascript">
4164  */
4165
4166 // as we use this in bootstrap.
4167 Roo.namespace('Roo.form');
4168  /**
4169  * @class Roo.form.Action
4170  * Internal Class used to handle form actions
4171  * @constructor
4172  * @param {Roo.form.BasicForm} el The form element or its id
4173  * @param {Object} config Configuration options
4174  */
4175
4176  
4177  
4178 // define the action interface
4179 Roo.form.Action = function(form, options){
4180     this.form = form;
4181     this.options = options || {};
4182 };
4183 /**
4184  * Client Validation Failed
4185  * @const 
4186  */
4187 Roo.form.Action.CLIENT_INVALID = 'client';
4188 /**
4189  * Server Validation Failed
4190  * @const 
4191  */
4192 Roo.form.Action.SERVER_INVALID = 'server';
4193  /**
4194  * Connect to Server Failed
4195  * @const 
4196  */
4197 Roo.form.Action.CONNECT_FAILURE = 'connect';
4198 /**
4199  * Reading Data from Server Failed
4200  * @const 
4201  */
4202 Roo.form.Action.LOAD_FAILURE = 'load';
4203
4204 Roo.form.Action.prototype = {
4205     type : 'default',
4206     failureType : undefined,
4207     response : undefined,
4208     result : undefined,
4209
4210     // interface method
4211     run : function(options){
4212
4213     },
4214
4215     // interface method
4216     success : function(response){
4217
4218     },
4219
4220     // interface method
4221     handleResponse : function(response){
4222
4223     },
4224
4225     // default connection failure
4226     failure : function(response){
4227         
4228         this.response = response;
4229         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4230         this.form.afterAction(this, false);
4231     },
4232
4233     processResponse : function(response){
4234         this.response = response;
4235         if(!response.responseText){
4236             return true;
4237         }
4238         this.result = this.handleResponse(response);
4239         return this.result;
4240     },
4241
4242     // utility functions used internally
4243     getUrl : function(appendParams){
4244         var url = this.options.url || this.form.url || this.form.el.dom.action;
4245         if(appendParams){
4246             var p = this.getParams();
4247             if(p){
4248                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4249             }
4250         }
4251         return url;
4252     },
4253
4254     getMethod : function(){
4255         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4256     },
4257
4258     getParams : function(){
4259         var bp = this.form.baseParams;
4260         var p = this.options.params;
4261         if(p){
4262             if(typeof p == "object"){
4263                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4264             }else if(typeof p == 'string' && bp){
4265                 p += '&' + Roo.urlEncode(bp);
4266             }
4267         }else if(bp){
4268             p = Roo.urlEncode(bp);
4269         }
4270         return p;
4271     },
4272
4273     createCallback : function(){
4274         return {
4275             success: this.success,
4276             failure: this.failure,
4277             scope: this,
4278             timeout: (this.form.timeout*1000),
4279             upload: this.form.fileUpload ? this.success : undefined
4280         };
4281     }
4282 };
4283
4284 Roo.form.Action.Submit = function(form, options){
4285     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4286 };
4287
4288 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4289     type : 'submit',
4290
4291     haveProgress : false,
4292     uploadComplete : false,
4293     
4294     // uploadProgress indicator.
4295     uploadProgress : function()
4296     {
4297         if (!this.form.progressUrl) {
4298             return;
4299         }
4300         
4301         if (!this.haveProgress) {
4302             Roo.MessageBox.progress("Uploading", "Uploading");
4303         }
4304         if (this.uploadComplete) {
4305            Roo.MessageBox.hide();
4306            return;
4307         }
4308         
4309         this.haveProgress = true;
4310    
4311         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4312         
4313         var c = new Roo.data.Connection();
4314         c.request({
4315             url : this.form.progressUrl,
4316             params: {
4317                 id : uid
4318             },
4319             method: 'GET',
4320             success : function(req){
4321                //console.log(data);
4322                 var rdata = false;
4323                 var edata;
4324                 try  {
4325                    rdata = Roo.decode(req.responseText)
4326                 } catch (e) {
4327                     Roo.log("Invalid data from server..");
4328                     Roo.log(edata);
4329                     return;
4330                 }
4331                 if (!rdata || !rdata.success) {
4332                     Roo.log(rdata);
4333                     Roo.MessageBox.alert(Roo.encode(rdata));
4334                     return;
4335                 }
4336                 var data = rdata.data;
4337                 
4338                 if (this.uploadComplete) {
4339                    Roo.MessageBox.hide();
4340                    return;
4341                 }
4342                    
4343                 if (data){
4344                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4345                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4346                     );
4347                 }
4348                 this.uploadProgress.defer(2000,this);
4349             },
4350        
4351             failure: function(data) {
4352                 Roo.log('progress url failed ');
4353                 Roo.log(data);
4354             },
4355             scope : this
4356         });
4357            
4358     },
4359     
4360     
4361     run : function()
4362     {
4363         // run get Values on the form, so it syncs any secondary forms.
4364         this.form.getValues();
4365         
4366         var o = this.options;
4367         var method = this.getMethod();
4368         var isPost = method == 'POST';
4369         if(o.clientValidation === false || this.form.isValid()){
4370             
4371             if (this.form.progressUrl) {
4372                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4373                     (new Date() * 1) + '' + Math.random());
4374                     
4375             } 
4376             
4377             
4378             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4379                 form:this.form.el.dom,
4380                 url:this.getUrl(!isPost),
4381                 method: method,
4382                 params:isPost ? this.getParams() : null,
4383                 isUpload: this.form.fileUpload
4384             }));
4385             
4386             this.uploadProgress();
4387
4388         }else if (o.clientValidation !== false){ // client validation failed
4389             this.failureType = Roo.form.Action.CLIENT_INVALID;
4390             this.form.afterAction(this, false);
4391         }
4392     },
4393
4394     success : function(response)
4395     {
4396         this.uploadComplete= true;
4397         if (this.haveProgress) {
4398             Roo.MessageBox.hide();
4399         }
4400         
4401         
4402         var result = this.processResponse(response);
4403         if(result === true || result.success){
4404             this.form.afterAction(this, true);
4405             return;
4406         }
4407         if(result.errors){
4408             this.form.markInvalid(result.errors);
4409             this.failureType = Roo.form.Action.SERVER_INVALID;
4410         }
4411         this.form.afterAction(this, false);
4412     },
4413     failure : function(response)
4414     {
4415         this.uploadComplete= true;
4416         if (this.haveProgress) {
4417             Roo.MessageBox.hide();
4418         }
4419         
4420         this.response = response;
4421         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4422         this.form.afterAction(this, false);
4423     },
4424     
4425     handleResponse : function(response){
4426         if(this.form.errorReader){
4427             var rs = this.form.errorReader.read(response);
4428             var errors = [];
4429             if(rs.records){
4430                 for(var i = 0, len = rs.records.length; i < len; i++) {
4431                     var r = rs.records[i];
4432                     errors[i] = r.data;
4433                 }
4434             }
4435             if(errors.length < 1){
4436                 errors = null;
4437             }
4438             return {
4439                 success : rs.success,
4440                 errors : errors
4441             };
4442         }
4443         var ret = false;
4444         try {
4445             ret = Roo.decode(response.responseText);
4446         } catch (e) {
4447             ret = {
4448                 success: false,
4449                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4450                 errors : []
4451             };
4452         }
4453         return ret;
4454         
4455     }
4456 });
4457
4458
4459 Roo.form.Action.Load = function(form, options){
4460     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4461     this.reader = this.form.reader;
4462 };
4463
4464 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4465     type : 'load',
4466
4467     run : function(){
4468         
4469         Roo.Ajax.request(Roo.apply(
4470                 this.createCallback(), {
4471                     method:this.getMethod(),
4472                     url:this.getUrl(false),
4473                     params:this.getParams()
4474         }));
4475     },
4476
4477     success : function(response){
4478         
4479         var result = this.processResponse(response);
4480         if(result === true || !result.success || !result.data){
4481             this.failureType = Roo.form.Action.LOAD_FAILURE;
4482             this.form.afterAction(this, false);
4483             return;
4484         }
4485         this.form.clearInvalid();
4486         this.form.setValues(result.data);
4487         this.form.afterAction(this, true);
4488     },
4489
4490     handleResponse : function(response){
4491         if(this.form.reader){
4492             var rs = this.form.reader.read(response);
4493             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4494             return {
4495                 success : rs.success,
4496                 data : data
4497             };
4498         }
4499         return Roo.decode(response.responseText);
4500     }
4501 });
4502
4503 Roo.form.Action.ACTION_TYPES = {
4504     'load' : Roo.form.Action.Load,
4505     'submit' : Roo.form.Action.Submit
4506 };/*
4507  * - LGPL
4508  *
4509  * form
4510  * 
4511  */
4512
4513 /**
4514  * @class Roo.bootstrap.Form
4515  * @extends Roo.bootstrap.Component
4516  * Bootstrap Form class
4517  * @cfg {String} method  GET | POST (default POST)
4518  * @cfg {String} labelAlign top | left (default top)
4519   * @cfg {String} align left  | right - for navbars
4520
4521  * 
4522  * @constructor
4523  * Create a new Form
4524  * @param {Object} config The config object
4525  */
4526
4527
4528 Roo.bootstrap.Form = function(config){
4529     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4530     this.addEvents({
4531         /**
4532          * @event clientvalidation
4533          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4534          * @param {Form} this
4535          * @param {Boolean} valid true if the form has passed client-side validation
4536          */
4537         clientvalidation: true,
4538         /**
4539          * @event beforeaction
4540          * Fires before any action is performed. Return false to cancel the action.
4541          * @param {Form} this
4542          * @param {Action} action The action to be performed
4543          */
4544         beforeaction: true,
4545         /**
4546          * @event actionfailed
4547          * Fires when an action fails.
4548          * @param {Form} this
4549          * @param {Action} action The action that failed
4550          */
4551         actionfailed : true,
4552         /**
4553          * @event actioncomplete
4554          * Fires when an action is completed.
4555          * @param {Form} this
4556          * @param {Action} action The action that completed
4557          */
4558         actioncomplete : true
4559     });
4560     
4561 };
4562
4563 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4564       
4565      /**
4566      * @cfg {String} method
4567      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4568      */
4569     method : 'POST',
4570     /**
4571      * @cfg {String} url
4572      * The URL to use for form actions if one isn't supplied in the action options.
4573      */
4574     /**
4575      * @cfg {Boolean} fileUpload
4576      * Set to true if this form is a file upload.
4577      */
4578      
4579     /**
4580      * @cfg {Object} baseParams
4581      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4582      */
4583       
4584     /**
4585      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4586      */
4587     timeout: 30,
4588     /**
4589      * @cfg {Sting} align (left|right) for navbar forms
4590      */
4591     align : 'left',
4592
4593     // private
4594     activeAction : null,
4595  
4596     /**
4597      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4598      * element by passing it or its id or mask the form itself by passing in true.
4599      * @type Mixed
4600      */
4601     waitMsgTarget : false,
4602     
4603      
4604     
4605     /**
4606      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4607      * element by passing it or its id or mask the form itself by passing in true.
4608      * @type Mixed
4609      */
4610     
4611     getAutoCreate : function(){
4612         
4613         var cfg = {
4614             tag: 'form',
4615             method : this.method || 'POST',
4616             id : this.id || Roo.id(),
4617             cls : ''
4618         }
4619         if (this.parent().xtype.match(/^Nav/)) {
4620             cfg.cls = 'navbar-form navbar-' + this.align;
4621             
4622         }
4623         
4624         if (this.labelAlign == 'left' ) {
4625             cfg.cls += ' form-horizontal';
4626         }
4627         
4628         
4629         return cfg;
4630     },
4631     initEvents : function()
4632     {
4633         this.el.on('submit', this.onSubmit, this);
4634         
4635         
4636     },
4637     // private
4638     onSubmit : function(e){
4639         e.stopEvent();
4640     },
4641     
4642      /**
4643      * Returns true if client-side validation on the form is successful.
4644      * @return Boolean
4645      */
4646     isValid : function(){
4647         var items = this.getItems();
4648         var valid = true;
4649         items.each(function(f){
4650            if(!f.validate()){
4651                valid = false;
4652                
4653            }
4654         });
4655         return valid;
4656     },
4657     /**
4658      * Returns true if any fields in this form have changed since their original load.
4659      * @return Boolean
4660      */
4661     isDirty : function(){
4662         var dirty = false;
4663         var items = this.getItems();
4664         items.each(function(f){
4665            if(f.isDirty()){
4666                dirty = true;
4667                return false;
4668            }
4669            return true;
4670         });
4671         return dirty;
4672     },
4673      /**
4674      * Performs a predefined action (submit or load) or custom actions you define on this form.
4675      * @param {String} actionName The name of the action type
4676      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
4677      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4678      * accept other config options):
4679      * <pre>
4680 Property          Type             Description
4681 ----------------  ---------------  ----------------------------------------------------------------------------------
4682 url               String           The url for the action (defaults to the form's url)
4683 method            String           The form method to use (defaults to the form's method, or POST if not defined)
4684 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
4685 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
4686                                    validate the form on the client (defaults to false)
4687      * </pre>
4688      * @return {BasicForm} this
4689      */
4690     doAction : function(action, options){
4691         if(typeof action == 'string'){
4692             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4693         }
4694         if(this.fireEvent('beforeaction', this, action) !== false){
4695             this.beforeAction(action);
4696             action.run.defer(100, action);
4697         }
4698         return this;
4699     },
4700     
4701     // private
4702     beforeAction : function(action){
4703         var o = action.options;
4704         
4705         // not really supported yet.. ??
4706         
4707         //if(this.waitMsgTarget === true){
4708             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4709         //}else if(this.waitMsgTarget){
4710         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4711         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4712         //}else {
4713         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4714        // }
4715          
4716     },
4717
4718     // private
4719     afterAction : function(action, success){
4720         this.activeAction = null;
4721         var o = action.options;
4722         
4723         //if(this.waitMsgTarget === true){
4724             this.el.unmask();
4725         //}else if(this.waitMsgTarget){
4726         //    this.waitMsgTarget.unmask();
4727         //}else{
4728         //    Roo.MessageBox.updateProgress(1);
4729         //    Roo.MessageBox.hide();
4730        // }
4731         // 
4732         if(success){
4733             if(o.reset){
4734                 this.reset();
4735             }
4736             Roo.callback(o.success, o.scope, [this, action]);
4737             this.fireEvent('actioncomplete', this, action);
4738             
4739         }else{
4740             
4741             // failure condition..
4742             // we have a scenario where updates need confirming.
4743             // eg. if a locking scenario exists..
4744             // we look for { errors : { needs_confirm : true }} in the response.
4745             if (
4746                 (typeof(action.result) != 'undefined')  &&
4747                 (typeof(action.result.errors) != 'undefined')  &&
4748                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4749            ){
4750                 var _t = this;
4751                 Roo.log("not supported yet");
4752                  /*
4753                 
4754                 Roo.MessageBox.confirm(
4755                     "Change requires confirmation",
4756                     action.result.errorMsg,
4757                     function(r) {
4758                         if (r != 'yes') {
4759                             return;
4760                         }
4761                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4762                     }
4763                     
4764                 );
4765                 */
4766                 
4767                 
4768                 return;
4769             }
4770             
4771             Roo.callback(o.failure, o.scope, [this, action]);
4772             // show an error message if no failed handler is set..
4773             if (!this.hasListener('actionfailed')) {
4774                 Roo.log("need to add dialog support");
4775                 /*
4776                 Roo.MessageBox.alert("Error",
4777                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4778                         action.result.errorMsg :
4779                         "Saving Failed, please check your entries or try again"
4780                 );
4781                 */
4782             }
4783             
4784             this.fireEvent('actionfailed', this, action);
4785         }
4786         
4787     },
4788     /**
4789      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4790      * @param {String} id The value to search for
4791      * @return Field
4792      */
4793     findField : function(id){
4794         var items = this.getItems();
4795         var field = items.get(id);
4796         if(!field){
4797              items.each(function(f){
4798                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4799                     field = f;
4800                     return false;
4801                 }
4802                 return true;
4803             });
4804         }
4805         return field || null;
4806     },
4807      /**
4808      * Mark fields in this form invalid in bulk.
4809      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4810      * @return {BasicForm} this
4811      */
4812     markInvalid : function(errors){
4813         if(errors instanceof Array){
4814             for(var i = 0, len = errors.length; i < len; i++){
4815                 var fieldError = errors[i];
4816                 var f = this.findField(fieldError.id);
4817                 if(f){
4818                     f.markInvalid(fieldError.msg);
4819                 }
4820             }
4821         }else{
4822             var field, id;
4823             for(id in errors){
4824                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4825                     field.markInvalid(errors[id]);
4826                 }
4827             }
4828         }
4829         //Roo.each(this.childForms || [], function (f) {
4830         //    f.markInvalid(errors);
4831         //});
4832         
4833         return this;
4834     },
4835
4836     /**
4837      * Set values for fields in this form in bulk.
4838      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4839      * @return {BasicForm} this
4840      */
4841     setValues : function(values){
4842         if(values instanceof Array){ // array of objects
4843             for(var i = 0, len = values.length; i < len; i++){
4844                 var v = values[i];
4845                 var f = this.findField(v.id);
4846                 if(f){
4847                     f.setValue(v.value);
4848                     if(this.trackResetOnLoad){
4849                         f.originalValue = f.getValue();
4850                     }
4851                 }
4852             }
4853         }else{ // object hash
4854             var field, id;
4855             for(id in values){
4856                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4857                     
4858                     if (field.setFromData && 
4859                         field.valueField && 
4860                         field.displayField &&
4861                         // combos' with local stores can 
4862                         // be queried via setValue()
4863                         // to set their value..
4864                         (field.store && !field.store.isLocal)
4865                         ) {
4866                         // it's a combo
4867                         var sd = { };
4868                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4869                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4870                         field.setFromData(sd);
4871                         
4872                     } else {
4873                         field.setValue(values[id]);
4874                     }
4875                     
4876                     
4877                     if(this.trackResetOnLoad){
4878                         field.originalValue = field.getValue();
4879                     }
4880                 }
4881             }
4882         }
4883          
4884         //Roo.each(this.childForms || [], function (f) {
4885         //    f.setValues(values);
4886         //});
4887                 
4888         return this;
4889     },
4890
4891     /**
4892      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4893      * they are returned as an array.
4894      * @param {Boolean} asString
4895      * @return {Object}
4896      */
4897     getValues : function(asString){
4898         //if (this.childForms) {
4899             // copy values from the child forms
4900         //    Roo.each(this.childForms, function (f) {
4901         //        this.setValues(f.getValues());
4902         //    }, this);
4903         //}
4904         
4905         
4906         
4907         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4908         if(asString === true){
4909             return fs;
4910         }
4911         return Roo.urlDecode(fs);
4912     },
4913     
4914     /**
4915      * Returns the fields in this form as an object with key/value pairs. 
4916      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4917      * @return {Object}
4918      */
4919     getFieldValues : function(with_hidden)
4920     {
4921         var items = this.getItems();
4922         var ret = {};
4923         items.each(function(f){
4924             if (!f.getName()) {
4925                 return;
4926             }
4927             var v = f.getValue();
4928             if (f.inputType =='radio') {
4929                 if (typeof(ret[f.getName()]) == 'undefined') {
4930                     ret[f.getName()] = ''; // empty..
4931                 }
4932                 
4933                 if (!f.el.dom.checked) {
4934                     return;
4935                     
4936                 }
4937                 v = f.el.dom.value;
4938                 
4939             }
4940             
4941             // not sure if this supported any more..
4942             if ((typeof(v) == 'object') && f.getRawValue) {
4943                 v = f.getRawValue() ; // dates..
4944             }
4945             // combo boxes where name != hiddenName...
4946             if (f.name != f.getName()) {
4947                 ret[f.name] = f.getRawValue();
4948             }
4949             ret[f.getName()] = v;
4950         });
4951         
4952         return ret;
4953     },
4954
4955     /**
4956      * Clears all invalid messages in this form.
4957      * @return {BasicForm} this
4958      */
4959     clearInvalid : function(){
4960         var items = this.getItems();
4961         
4962         items.each(function(f){
4963            f.clearInvalid();
4964         });
4965         
4966         
4967         
4968         return this;
4969     },
4970
4971     /**
4972      * Resets this form.
4973      * @return {BasicForm} this
4974      */
4975     reset : function(){
4976         var items = this.getItems();
4977         items.each(function(f){
4978             f.reset();
4979         });
4980         
4981         Roo.each(this.childForms || [], function (f) {
4982             f.reset();
4983         });
4984        
4985         
4986         return this;
4987     },
4988     getItems : function()
4989     {
4990         var r=new Roo.util.MixedCollection(false, function(o){
4991             return o.id || (o.id = Roo.id());
4992         });
4993         var iter = function(el) {
4994             if (el.inputEl) {
4995                 r.add(el);
4996             }
4997             if (!el.items) {
4998                 return;
4999             }
5000             Roo.each(el.items,function(e) {
5001                 iter(e);
5002             });
5003             
5004             
5005         };
5006         iter(this);
5007         return r;
5008         
5009         
5010         
5011         
5012     }
5013     
5014 });
5015
5016  
5017 /*
5018  * Based on:
5019  * Ext JS Library 1.1.1
5020  * Copyright(c) 2006-2007, Ext JS, LLC.
5021  *
5022  * Originally Released Under LGPL - original licence link has changed is not relivant.
5023  *
5024  * Fork - LGPL
5025  * <script type="text/javascript">
5026  */
5027 /**
5028  * @class Roo.form.VTypes
5029  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5030  * @singleton
5031  */
5032 Roo.form.VTypes = function(){
5033     // closure these in so they are only created once.
5034     var alpha = /^[a-zA-Z_]+$/;
5035     var alphanum = /^[a-zA-Z0-9_]+$/;
5036     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5037     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5038
5039     // All these messages and functions are configurable
5040     return {
5041         /**
5042          * The function used to validate email addresses
5043          * @param {String} value The email address
5044          */
5045         'email' : function(v){
5046             return email.test(v);
5047         },
5048         /**
5049          * The error text to display when the email validation function returns false
5050          * @type String
5051          */
5052         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5053         /**
5054          * The keystroke filter mask to be applied on email input
5055          * @type RegExp
5056          */
5057         'emailMask' : /[a-z0-9_\.\-@]/i,
5058
5059         /**
5060          * The function used to validate URLs
5061          * @param {String} value The URL
5062          */
5063         'url' : function(v){
5064             return url.test(v);
5065         },
5066         /**
5067          * The error text to display when the url validation function returns false
5068          * @type String
5069          */
5070         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5071         
5072         /**
5073          * The function used to validate alpha values
5074          * @param {String} value The value
5075          */
5076         'alpha' : function(v){
5077             return alpha.test(v);
5078         },
5079         /**
5080          * The error text to display when the alpha validation function returns false
5081          * @type String
5082          */
5083         'alphaText' : 'This field should only contain letters and _',
5084         /**
5085          * The keystroke filter mask to be applied on alpha input
5086          * @type RegExp
5087          */
5088         'alphaMask' : /[a-z_]/i,
5089
5090         /**
5091          * The function used to validate alphanumeric values
5092          * @param {String} value The value
5093          */
5094         'alphanum' : function(v){
5095             return alphanum.test(v);
5096         },
5097         /**
5098          * The error text to display when the alphanumeric validation function returns false
5099          * @type String
5100          */
5101         'alphanumText' : 'This field should only contain letters, numbers and _',
5102         /**
5103          * The keystroke filter mask to be applied on alphanumeric input
5104          * @type RegExp
5105          */
5106         'alphanumMask' : /[a-z0-9_]/i
5107     };
5108 }();/*
5109  * - LGPL
5110  *
5111  * Input
5112  * 
5113  */
5114
5115 /**
5116  * @class Roo.bootstrap.Input
5117  * @extends Roo.bootstrap.Component
5118  * Bootstrap Input class
5119  * @cfg {Boolean} disabled is it disabled
5120  * @cfg {String} fieldLabel - the label associated
5121  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5122  * @cfg {String} name name of the input
5123  * @cfg {string} fieldLabel - the label associated
5124  * @cfg {string}  inputType - input / file submit ...
5125  * @cfg {string} placeholder - placeholder to put in text.
5126  * @cfg {string}  before - input group add on before
5127  * @cfg {string} after - input group add on after
5128  * @cfg {string} size - (lg|sm) or leave empty..
5129  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5130  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5131  * @cfg {Number} md colspan out of 12 for computer-sized screens
5132  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5133  * @cfg {string} value default value of the input
5134  * @cfg {Number} labelWidth set the width of label (0-12)
5135  * @cfg {String} labelAlign (top|left)
5136  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5137  * 
5138  * 
5139  * @constructor
5140  * Create a new Input
5141  * @param {Object} config The config object
5142  */
5143
5144 Roo.bootstrap.Input = function(config){
5145     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5146    
5147         this.addEvents({
5148             /**
5149              * @event focus
5150              * Fires when this field receives input focus.
5151              * @param {Roo.form.Field} this
5152              */
5153             focus : true,
5154             /**
5155              * @event blur
5156              * Fires when this field loses input focus.
5157              * @param {Roo.form.Field} this
5158              */
5159             blur : true,
5160             /**
5161              * @event specialkey
5162              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5163              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5164              * @param {Roo.form.Field} this
5165              * @param {Roo.EventObject} e The event object
5166              */
5167             specialkey : true,
5168             /**
5169              * @event change
5170              * Fires just before the field blurs if the field value has changed.
5171              * @param {Roo.form.Field} this
5172              * @param {Mixed} newValue The new value
5173              * @param {Mixed} oldValue The original value
5174              */
5175             change : true,
5176             /**
5177              * @event invalid
5178              * Fires after the field has been marked as invalid.
5179              * @param {Roo.form.Field} this
5180              * @param {String} msg The validation message
5181              */
5182             invalid : true,
5183             /**
5184              * @event valid
5185              * Fires after the field has been validated with no errors.
5186              * @param {Roo.form.Field} this
5187              */
5188             valid : true,
5189              /**
5190              * @event keyup
5191              * Fires after the key up
5192              * @param {Roo.form.Field} this
5193              * @param {Roo.EventObject}  e The event Object
5194              */
5195             keyup : true
5196         });
5197 };
5198
5199 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5200      /**
5201      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5202       automatic validation (defaults to "keyup").
5203      */
5204     validationEvent : "keyup",
5205      /**
5206      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5207      */
5208     validateOnBlur : true,
5209     /**
5210      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5211      */
5212     validationDelay : 250,
5213      /**
5214      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5215      */
5216     focusClass : "x-form-focus",  // not needed???
5217     
5218        
5219     /**
5220      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5221      */
5222     invalidClass : "has-error",
5223     
5224     /**
5225      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5226      */
5227     selectOnFocus : false,
5228     
5229      /**
5230      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5231      */
5232     maskRe : null,
5233        /**
5234      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5235      */
5236     vtype : null,
5237     
5238       /**
5239      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5240      */
5241     disableKeyFilter : false,
5242     
5243        /**
5244      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5245      */
5246     disabled : false,
5247      /**
5248      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5249      */
5250     allowBlank : true,
5251     /**
5252      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5253      */
5254     blankText : "This field is required",
5255     
5256      /**
5257      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5258      */
5259     minLength : 0,
5260     /**
5261      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5262      */
5263     maxLength : Number.MAX_VALUE,
5264     /**
5265      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5266      */
5267     minLengthText : "The minimum length for this field is {0}",
5268     /**
5269      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5270      */
5271     maxLengthText : "The maximum length for this field is {0}",
5272   
5273     
5274     /**
5275      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5276      * If available, this function will be called only after the basic validators all return true, and will be passed the
5277      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5278      */
5279     validator : null,
5280     /**
5281      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5282      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5283      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5284      */
5285     regex : null,
5286     /**
5287      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5288      */
5289     regexText : "",
5290     
5291     
5292     
5293     fieldLabel : '',
5294     inputType : 'text',
5295     
5296     name : false,
5297     placeholder: false,
5298     before : false,
5299     after : false,
5300     size : false,
5301     // private
5302     hasFocus : false,
5303     preventMark: false,
5304     isFormField : true,
5305     value : '',
5306     labelWidth : 2,
5307     labelAlign : false,
5308     readOnly : false,
5309     
5310     parentLabelAlign : function()
5311     {
5312         var parent = this;
5313         while (parent.parent()) {
5314             parent = parent.parent();
5315             if (typeof(parent.labelAlign) !='undefined') {
5316                 return parent.labelAlign;
5317             }
5318         }
5319         return 'left';
5320         
5321     },
5322     
5323     getAutoCreate : function(){
5324         
5325         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5326         
5327         var id = Roo.id();
5328         
5329         var cfg = {};
5330         
5331         if(this.inputType != 'hidden'){
5332             cfg.cls = 'form-group' //input-group
5333         }
5334         
5335         var input =  {
5336             tag: 'input',
5337             id : id,
5338             type : this.inputType,
5339             value : this.value,
5340             cls : 'form-control',
5341             placeholder : this.placeholder || ''
5342             
5343         };
5344         
5345         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5346             input.maxLength = this.maxLength;
5347         }
5348         
5349         if (this.disabled) {
5350             input.disabled=true;
5351         }
5352         
5353         if (this.readOnly) {
5354             input.readonly=true;
5355         }
5356         
5357         if (this.name) {
5358             input.name = this.name;
5359         }
5360         if (this.size) {
5361             input.cls += ' input-' + this.size;
5362         }
5363         var settings=this;
5364         ['xs','sm','md','lg'].map(function(size){
5365             if (settings[size]) {
5366                 cfg.cls += ' col-' + size + '-' + settings[size];
5367             }
5368         });
5369         
5370         var inputblock = input;
5371         
5372         if (this.before || this.after) {
5373             
5374             inputblock = {
5375                 cls : 'input-group',
5376                 cn :  [] 
5377             };
5378             if (this.before) {
5379                 inputblock.cn.push({
5380                     tag :'span',
5381                     cls : 'input-group-addon',
5382                     html : this.before
5383                 });
5384             }
5385             inputblock.cn.push(input);
5386             if (this.after) {
5387                 inputblock.cn.push({
5388                     tag :'span',
5389                     cls : 'input-group-addon',
5390                     html : this.after
5391                 });
5392             }
5393             
5394         };
5395         
5396         if (align ==='left' && this.fieldLabel.length) {
5397                 Roo.log("left and has label");
5398                 cfg.cn = [
5399                     
5400                     {
5401                         tag: 'label',
5402                         'for' :  id,
5403                         cls : 'control-label col-sm-' + this.labelWidth,
5404                         html : this.fieldLabel
5405                         
5406                     },
5407                     {
5408                         cls : "col-sm-" + (12 - this.labelWidth), 
5409                         cn: [
5410                             inputblock
5411                         ]
5412                     }
5413                     
5414                 ];
5415         } else if ( this.fieldLabel.length) {
5416                 Roo.log(" label");
5417                  cfg.cn = [
5418                    
5419                     {
5420                         tag: 'label',
5421                         //cls : 'input-group-addon',
5422                         html : this.fieldLabel
5423                         
5424                     },
5425                     
5426                     inputblock
5427                     
5428                 ];
5429
5430         } else {
5431             
5432                 Roo.log(" no label && no align");
5433                 cfg.cn = [
5434                     
5435                         inputblock
5436                     
5437                 ];
5438                 
5439                 
5440         };
5441         Roo.log('input-parentType: ' + this.parentType);
5442         
5443         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5444            cfg.cls += ' navbar-form';
5445            Roo.log(cfg);
5446         }
5447         
5448         return cfg;
5449         
5450     },
5451     /**
5452      * return the real input element.
5453      */
5454     inputEl: function ()
5455     {
5456         return this.el.select('input.form-control',true).first();
5457     },
5458     setDisabled : function(v)
5459     {
5460         var i  = this.inputEl().dom;
5461         if (!v) {
5462             i.removeAttribute('disabled');
5463             return;
5464             
5465         }
5466         i.setAttribute('disabled','true');
5467     },
5468     initEvents : function()
5469     {
5470         
5471         this.inputEl().on("keydown" , this.fireKey,  this);
5472         this.inputEl().on("focus", this.onFocus,  this);
5473         this.inputEl().on("blur", this.onBlur,  this);
5474         
5475         this.inputEl().relayEvent('keyup', this);
5476
5477         // reference to original value for reset
5478         this.originalValue = this.getValue();
5479         //Roo.form.TextField.superclass.initEvents.call(this);
5480         if(this.validationEvent == 'keyup'){
5481             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5482             this.inputEl().on('keyup', this.filterValidation, this);
5483         }
5484         else if(this.validationEvent !== false){
5485             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5486         }
5487         
5488         if(this.selectOnFocus){
5489             this.on("focus", this.preFocus, this);
5490             
5491         }
5492         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5493             this.inputEl().on("keypress", this.filterKeys, this);
5494         }
5495        /* if(this.grow){
5496             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5497             this.el.on("click", this.autoSize,  this);
5498         }
5499         */
5500         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5501             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5502         }
5503         
5504     },
5505     filterValidation : function(e){
5506         if(!e.isNavKeyPress()){
5507             this.validationTask.delay(this.validationDelay);
5508         }
5509     },
5510      /**
5511      * Validates the field value
5512      * @return {Boolean} True if the value is valid, else false
5513      */
5514     validate : function(){
5515         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5516         if(this.disabled || this.validateValue(this.getRawValue())){
5517             this.clearInvalid();
5518             return true;
5519         }
5520         return false;
5521     },
5522     
5523     
5524     /**
5525      * Validates a value according to the field's validation rules and marks the field as invalid
5526      * if the validation fails
5527      * @param {Mixed} value The value to validate
5528      * @return {Boolean} True if the value is valid, else false
5529      */
5530     validateValue : function(value){
5531         if(value.length < 1)  { // if it's blank
5532              if(this.allowBlank){
5533                 this.clearInvalid();
5534                 return true;
5535              }else{
5536                 this.markInvalid(this.blankText);
5537                 return false;
5538              }
5539         }
5540         if(value.length < this.minLength){
5541             this.markInvalid(String.format(this.minLengthText, this.minLength));
5542             return false;
5543         }
5544         if(value.length > this.maxLength){
5545             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5546             return false;
5547         }
5548         if(this.vtype){
5549             var vt = Roo.form.VTypes;
5550             if(!vt[this.vtype](value, this)){
5551                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5552                 return false;
5553             }
5554         }
5555         if(typeof this.validator == "function"){
5556             var msg = this.validator(value);
5557             if(msg !== true){
5558                 this.markInvalid(msg);
5559                 return false;
5560             }
5561         }
5562         if(this.regex && !this.regex.test(value)){
5563             this.markInvalid(this.regexText);
5564             return false;
5565         }
5566         return true;
5567     },
5568
5569     
5570     
5571      // private
5572     fireKey : function(e){
5573         //Roo.log('field ' + e.getKey());
5574         if(e.isNavKeyPress()){
5575             this.fireEvent("specialkey", this, e);
5576         }
5577     },
5578     focus : function (selectText){
5579         if(this.rendered){
5580             this.inputEl().focus();
5581             if(selectText === true){
5582                 this.inputEl().dom.select();
5583             }
5584         }
5585         return this;
5586     } ,
5587     
5588     onFocus : function(){
5589         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5590            // this.el.addClass(this.focusClass);
5591         }
5592         if(!this.hasFocus){
5593             this.hasFocus = true;
5594             this.startValue = this.getValue();
5595             this.fireEvent("focus", this);
5596         }
5597     },
5598     
5599     beforeBlur : Roo.emptyFn,
5600
5601     
5602     // private
5603     onBlur : function(){
5604         this.beforeBlur();
5605         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5606             //this.el.removeClass(this.focusClass);
5607         }
5608         this.hasFocus = false;
5609         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5610             this.validate();
5611         }
5612         var v = this.getValue();
5613         if(String(v) !== String(this.startValue)){
5614             this.fireEvent('change', this, v, this.startValue);
5615         }
5616         this.fireEvent("blur", this);
5617     },
5618     
5619     /**
5620      * Resets the current field value to the originally loaded value and clears any validation messages
5621      */
5622     reset : function(){
5623         this.setValue(this.originalValue);
5624         this.clearInvalid();
5625     },
5626      /**
5627      * Returns the name of the field
5628      * @return {Mixed} name The name field
5629      */
5630     getName: function(){
5631         return this.name;
5632     },
5633      /**
5634      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
5635      * @return {Mixed} value The field value
5636      */
5637     getValue : function(){
5638         return this.inputEl().getValue();
5639     },
5640     /**
5641      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
5642      * @return {Mixed} value The field value
5643      */
5644     getRawValue : function(){
5645         var v = this.inputEl().getValue();
5646         
5647         return v;
5648     },
5649     
5650     /**
5651      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
5652      * @param {Mixed} value The value to set
5653      */
5654     setRawValue : function(v){
5655         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5656     },
5657     
5658     selectText : function(start, end){
5659         var v = this.getRawValue();
5660         if(v.length > 0){
5661             start = start === undefined ? 0 : start;
5662             end = end === undefined ? v.length : end;
5663             var d = this.inputEl().dom;
5664             if(d.setSelectionRange){
5665                 d.setSelectionRange(start, end);
5666             }else if(d.createTextRange){
5667                 var range = d.createTextRange();
5668                 range.moveStart("character", start);
5669                 range.moveEnd("character", v.length-end);
5670                 range.select();
5671             }
5672         }
5673     },
5674     
5675     /**
5676      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
5677      * @param {Mixed} value The value to set
5678      */
5679     setValue : function(v){
5680         this.value = v;
5681         if(this.rendered){
5682             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5683             this.validate();
5684         }
5685     },
5686     
5687     /*
5688     processValue : function(value){
5689         if(this.stripCharsRe){
5690             var newValue = value.replace(this.stripCharsRe, '');
5691             if(newValue !== value){
5692                 this.setRawValue(newValue);
5693                 return newValue;
5694             }
5695         }
5696         return value;
5697     },
5698   */
5699     preFocus : function(){
5700         
5701         if(this.selectOnFocus){
5702             this.inputEl().dom.select();
5703         }
5704     },
5705     filterKeys : function(e){
5706         var k = e.getKey();
5707         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5708             return;
5709         }
5710         var c = e.getCharCode(), cc = String.fromCharCode(c);
5711         if(Roo.isIE && (e.isSpecialKey() || !cc)){
5712             return;
5713         }
5714         if(!this.maskRe.test(cc)){
5715             e.stopEvent();
5716         }
5717     },
5718      /**
5719      * Clear any invalid styles/messages for this field
5720      */
5721     clearInvalid : function(){
5722         
5723         if(!this.el || this.preventMark){ // not rendered
5724             return;
5725         }
5726         this.el.removeClass(this.invalidClass);
5727         /*
5728         switch(this.msgTarget){
5729             case 'qtip':
5730                 this.el.dom.qtip = '';
5731                 break;
5732             case 'title':
5733                 this.el.dom.title = '';
5734                 break;
5735             case 'under':
5736                 if(this.errorEl){
5737                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5738                 }
5739                 break;
5740             case 'side':
5741                 if(this.errorIcon){
5742                     this.errorIcon.dom.qtip = '';
5743                     this.errorIcon.hide();
5744                     this.un('resize', this.alignErrorIcon, this);
5745                 }
5746                 break;
5747             default:
5748                 var t = Roo.getDom(this.msgTarget);
5749                 t.innerHTML = '';
5750                 t.style.display = 'none';
5751                 break;
5752         }
5753         */
5754         this.fireEvent('valid', this);
5755     },
5756      /**
5757      * Mark this field as invalid
5758      * @param {String} msg The validation message
5759      */
5760     markInvalid : function(msg){
5761         if(!this.el  || this.preventMark){ // not rendered
5762             return;
5763         }
5764         this.el.addClass(this.invalidClass);
5765         /*
5766         msg = msg || this.invalidText;
5767         switch(this.msgTarget){
5768             case 'qtip':
5769                 this.el.dom.qtip = msg;
5770                 this.el.dom.qclass = 'x-form-invalid-tip';
5771                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5772                     Roo.QuickTips.enable();
5773                 }
5774                 break;
5775             case 'title':
5776                 this.el.dom.title = msg;
5777                 break;
5778             case 'under':
5779                 if(!this.errorEl){
5780                     var elp = this.el.findParent('.x-form-element', 5, true);
5781                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5782                     this.errorEl.setWidth(elp.getWidth(true)-20);
5783                 }
5784                 this.errorEl.update(msg);
5785                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5786                 break;
5787             case 'side':
5788                 if(!this.errorIcon){
5789                     var elp = this.el.findParent('.x-form-element', 5, true);
5790                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5791                 }
5792                 this.alignErrorIcon();
5793                 this.errorIcon.dom.qtip = msg;
5794                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5795                 this.errorIcon.show();
5796                 this.on('resize', this.alignErrorIcon, this);
5797                 break;
5798             default:
5799                 var t = Roo.getDom(this.msgTarget);
5800                 t.innerHTML = msg;
5801                 t.style.display = this.msgDisplay;
5802                 break;
5803         }
5804         */
5805         this.fireEvent('invalid', this, msg);
5806     },
5807     // private
5808     SafariOnKeyDown : function(event)
5809     {
5810         // this is a workaround for a password hang bug on chrome/ webkit.
5811         
5812         var isSelectAll = false;
5813         
5814         if(this.inputEl().dom.selectionEnd > 0){
5815             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5816         }
5817         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5818             event.preventDefault();
5819             this.setValue('');
5820             return;
5821         }
5822         
5823         if(isSelectAll){ // backspace and delete key
5824             
5825             event.preventDefault();
5826             // this is very hacky as keydown always get's upper case.
5827             //
5828             var cc = String.fromCharCode(event.getCharCode());
5829             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5830             
5831         }
5832     },
5833     adjustWidth : function(tag, w){
5834         tag = tag.toLowerCase();
5835         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5836             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5837                 if(tag == 'input'){
5838                     return w + 2;
5839                 }
5840                 if(tag == 'textarea'){
5841                     return w-2;
5842                 }
5843             }else if(Roo.isOpera){
5844                 if(tag == 'input'){
5845                     return w + 2;
5846                 }
5847                 if(tag == 'textarea'){
5848                     return w-2;
5849                 }
5850             }
5851         }
5852         return w;
5853     }
5854     
5855 });
5856
5857  
5858 /*
5859  * - LGPL
5860  *
5861  * Input
5862  * 
5863  */
5864
5865 /**
5866  * @class Roo.bootstrap.TextArea
5867  * @extends Roo.bootstrap.Input
5868  * Bootstrap TextArea class
5869  * @cfg {Number} cols Specifies the visible width of a text area
5870  * @cfg {Number} rows Specifies the visible number of lines in a text area
5871  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5872  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5873  * @cfg {string} html text
5874  * 
5875  * @constructor
5876  * Create a new TextArea
5877  * @param {Object} config The config object
5878  */
5879
5880 Roo.bootstrap.TextArea = function(config){
5881     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5882    
5883 };
5884
5885 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5886      
5887     cols : false,
5888     rows : 5,
5889     readOnly : false,
5890     warp : 'soft',
5891     resize : false,
5892     value: false,
5893     html: false,
5894     
5895     getAutoCreate : function(){
5896         
5897         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5898         
5899         var id = Roo.id();
5900         
5901         var cfg = {};
5902         
5903         var input =  {
5904             tag: 'textarea',
5905             id : id,
5906             warp : this.warp,
5907             rows : this.rows,
5908             value : this.value || '',
5909             html: this.html || '',
5910             cls : 'form-control',
5911             placeholder : this.placeholder || '' 
5912             
5913         };
5914         
5915         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5916             input.maxLength = this.maxLength;
5917         }
5918         
5919         if(this.resize){
5920             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5921         }
5922         
5923         if(this.cols){
5924             input.cols = this.cols;
5925         }
5926         
5927         if (this.readOnly) {
5928             input.readonly = true;
5929         }
5930         
5931         if (this.name) {
5932             input.name = this.name;
5933         }
5934         
5935         if (this.size) {
5936             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5937         }
5938         
5939         var settings=this;
5940         ['xs','sm','md','lg'].map(function(size){
5941             if (settings[size]) {
5942                 cfg.cls += ' col-' + size + '-' + settings[size];
5943             }
5944         });
5945         
5946         var inputblock = input;
5947         
5948         if (this.before || this.after) {
5949             
5950             inputblock = {
5951                 cls : 'input-group',
5952                 cn :  [] 
5953             };
5954             if (this.before) {
5955                 inputblock.cn.push({
5956                     tag :'span',
5957                     cls : 'input-group-addon',
5958                     html : this.before
5959                 });
5960             }
5961             inputblock.cn.push(input);
5962             if (this.after) {
5963                 inputblock.cn.push({
5964                     tag :'span',
5965                     cls : 'input-group-addon',
5966                     html : this.after
5967                 });
5968             }
5969             
5970         }
5971         
5972         if (align ==='left' && this.fieldLabel.length) {
5973                 Roo.log("left and has label");
5974                 cfg.cn = [
5975                     
5976                     {
5977                         tag: 'label',
5978                         'for' :  id,
5979                         cls : 'control-label col-sm-' + this.labelWidth,
5980                         html : this.fieldLabel
5981                         
5982                     },
5983                     {
5984                         cls : "col-sm-" + (12 - this.labelWidth), 
5985                         cn: [
5986                             inputblock
5987                         ]
5988                     }
5989                     
5990                 ];
5991         } else if ( this.fieldLabel.length) {
5992                 Roo.log(" label");
5993                  cfg.cn = [
5994                    
5995                     {
5996                         tag: 'label',
5997                         //cls : 'input-group-addon',
5998                         html : this.fieldLabel
5999                         
6000                     },
6001                     
6002                     inputblock
6003                     
6004                 ];
6005
6006         } else {
6007             
6008                    Roo.log(" no label && no align");
6009                 cfg.cn = [
6010                     
6011                         inputblock
6012                     
6013                 ];
6014                 
6015                 
6016         }
6017         
6018         if (this.disabled) {
6019             input.disabled=true;
6020         }
6021         
6022         return cfg;
6023         
6024     },
6025     /**
6026      * return the real textarea element.
6027      */
6028     inputEl: function ()
6029     {
6030         return this.el.select('textarea.form-control',true).first();
6031     }
6032 });
6033
6034  
6035 /*
6036  * - LGPL
6037  *
6038  * trigger field - base class for combo..
6039  * 
6040  */
6041  
6042 /**
6043  * @class Roo.bootstrap.TriggerField
6044  * @extends Roo.bootstrap.Input
6045  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6046  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6047  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6048  * for which you can provide a custom implementation.  For example:
6049  * <pre><code>
6050 var trigger = new Roo.bootstrap.TriggerField();
6051 trigger.onTriggerClick = myTriggerFn;
6052 trigger.applyTo('my-field');
6053 </code></pre>
6054  *
6055  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6056  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6057  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6058  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6059  * @constructor
6060  * Create a new TriggerField.
6061  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6062  * to the base TextField)
6063  */
6064 Roo.bootstrap.TriggerField = function(config){
6065     this.mimicing = false;
6066     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6067 };
6068
6069 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6070     /**
6071      * @cfg {String} triggerClass A CSS class to apply to the trigger
6072      */
6073      /**
6074      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6075      */
6076     hideTrigger:false,
6077
6078     /** @cfg {Boolean} grow @hide */
6079     /** @cfg {Number} growMin @hide */
6080     /** @cfg {Number} growMax @hide */
6081
6082     /**
6083      * @hide 
6084      * @method
6085      */
6086     autoSize: Roo.emptyFn,
6087     // private
6088     monitorTab : true,
6089     // private
6090     deferHeight : true,
6091
6092     
6093     actionMode : 'wrap',
6094     
6095     
6096     
6097     getAutoCreate : function(){
6098        
6099         var parent = this.parent();
6100         
6101         var align = this.parentLabelAlign();
6102         
6103         var id = Roo.id();
6104         
6105         var cfg = {
6106             cls: 'form-group' //input-group
6107         };
6108         
6109         
6110         var input =  {
6111             tag: 'input',
6112             id : id,
6113             type : this.inputType,
6114             cls : 'form-control',
6115             autocomplete: 'off',
6116             placeholder : this.placeholder || '' 
6117             
6118         };
6119         if (this.name) {
6120             input.name = this.name;
6121         }
6122         if (this.size) {
6123             input.cls += ' input-' + this.size;
6124         }
6125         
6126         if (this.disabled) {
6127             input.disabled=true;
6128         }
6129         
6130         var inputblock = input;
6131         
6132         if (this.before || this.after) {
6133             
6134             inputblock = {
6135                 cls : 'input-group',
6136                 cn :  [] 
6137             };
6138             if (this.before) {
6139                 inputblock.cn.push({
6140                     tag :'span',
6141                     cls : 'input-group-addon',
6142                     html : this.before
6143                 });
6144             }
6145             inputblock.cn.push(input);
6146             if (this.after) {
6147                 inputblock.cn.push({
6148                     tag :'span',
6149                     cls : 'input-group-addon',
6150                     html : this.after
6151                 });
6152             }
6153             
6154         };
6155         
6156         var box = {
6157             tag: 'div',
6158             cn: [
6159                 {
6160                     tag: 'input',
6161                     type : 'hidden',
6162                     cls: 'form-hidden-field'
6163                 },
6164                 inputblock
6165             ]
6166             
6167         };
6168         
6169         if(this.multiple){
6170             Roo.log('multiple');
6171             
6172             box = {
6173                 tag: 'div',
6174                 cn: [
6175                     {
6176                         tag: 'input',
6177                         type : 'hidden',
6178                         cls: 'form-hidden-field'
6179                     },
6180                     {
6181                         tag: 'ul',
6182                         cls: 'select2-choices',
6183                         cn:[
6184                             {
6185                                 tag: 'li',
6186                                 cls: 'select2-search-field',
6187                                 cn: [
6188
6189                                     inputblock
6190                                 ]
6191                             }
6192                         ]
6193                     }
6194                 ]
6195             }
6196         };
6197         
6198         var combobox = {
6199             cls: 'select2-container input-group',
6200             cn: [
6201                 box,
6202                 {
6203                     tag: 'ul',
6204                     cls: 'typeahead typeahead-long dropdown-menu',
6205                     style: 'display:none'
6206                 }
6207             ]
6208         };
6209         
6210         if(!this.multiple){
6211             combobox.cn.push({
6212                 tag :'span',
6213                 cls : 'input-group-addon btn dropdown-toggle',
6214                 cn : [
6215                     {
6216                         tag: 'span',
6217                         cls: 'caret'
6218                     },
6219                     {
6220                         tag: 'span',
6221                         cls: 'combobox-clear',
6222                         cn  : [
6223                             {
6224                                 tag : 'i',
6225                                 cls: 'icon-remove'
6226                             }
6227                         ]
6228                     }
6229                 ]
6230
6231             })
6232         }
6233         
6234         if(this.multiple){
6235             combobox.cls += ' select2-container-multi';
6236         }
6237         
6238         if (align ==='left' && this.fieldLabel.length) {
6239             
6240                 Roo.log("left and has label");
6241                 cfg.cn = [
6242                     
6243                     {
6244                         tag: 'label',
6245                         'for' :  id,
6246                         cls : 'control-label col-sm-' + this.labelWidth,
6247                         html : this.fieldLabel
6248                         
6249                     },
6250                     {
6251                         cls : "col-sm-" + (12 - this.labelWidth), 
6252                         cn: [
6253                             combobox
6254                         ]
6255                     }
6256                     
6257                 ];
6258         } else if ( this.fieldLabel.length) {
6259                 Roo.log(" label");
6260                  cfg.cn = [
6261                    
6262                     {
6263                         tag: 'label',
6264                         //cls : 'input-group-addon',
6265                         html : this.fieldLabel
6266                         
6267                     },
6268                     
6269                     combobox
6270                     
6271                 ];
6272
6273         } else {
6274             
6275                 Roo.log(" no label && no align");
6276                 cfg = combobox
6277                      
6278                 
6279         }
6280          
6281         var settings=this;
6282         ['xs','sm','md','lg'].map(function(size){
6283             if (settings[size]) {
6284                 cfg.cls += ' col-' + size + '-' + settings[size];
6285             }
6286         });
6287         
6288         return cfg;
6289         
6290     },
6291     
6292     
6293     
6294     // private
6295     onResize : function(w, h){
6296 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6297 //        if(typeof w == 'number'){
6298 //            var x = w - this.trigger.getWidth();
6299 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6300 //            this.trigger.setStyle('left', x+'px');
6301 //        }
6302     },
6303
6304     // private
6305     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6306
6307     // private
6308     getResizeEl : function(){
6309         return this.inputEl();
6310     },
6311
6312     // private
6313     getPositionEl : function(){
6314         return this.inputEl();
6315     },
6316
6317     // private
6318     alignErrorIcon : function(){
6319         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6320     },
6321
6322     // private
6323     initEvents : function(){
6324         
6325         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6326         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6327         if(!this.multiple){
6328             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6329             if(this.hideTrigger){
6330                 this.trigger.setDisplayed(false);
6331             }
6332             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6333         }
6334         
6335         if(this.multiple){
6336             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6337         }
6338         
6339         //this.trigger.addClassOnOver('x-form-trigger-over');
6340         //this.trigger.addClassOnClick('x-form-trigger-click');
6341         
6342         //if(!this.width){
6343         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6344         //}
6345     },
6346
6347     // private
6348     initTrigger : function(){
6349        
6350     },
6351
6352     // private
6353     onDestroy : function(){
6354         if(this.trigger){
6355             this.trigger.removeAllListeners();
6356           //  this.trigger.remove();
6357         }
6358         //if(this.wrap){
6359         //    this.wrap.remove();
6360         //}
6361         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6362     },
6363
6364     // private
6365     onFocus : function(){
6366         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6367         /*
6368         if(!this.mimicing){
6369             this.wrap.addClass('x-trigger-wrap-focus');
6370             this.mimicing = true;
6371             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6372             if(this.monitorTab){
6373                 this.el.on("keydown", this.checkTab, this);
6374             }
6375         }
6376         */
6377     },
6378
6379     // private
6380     checkTab : function(e){
6381         if(e.getKey() == e.TAB){
6382             this.triggerBlur();
6383         }
6384     },
6385
6386     // private
6387     onBlur : function(){
6388         // do nothing
6389     },
6390
6391     // private
6392     mimicBlur : function(e, t){
6393         /*
6394         if(!this.wrap.contains(t) && this.validateBlur()){
6395             this.triggerBlur();
6396         }
6397         */
6398     },
6399
6400     // private
6401     triggerBlur : function(){
6402         this.mimicing = false;
6403         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6404         if(this.monitorTab){
6405             this.el.un("keydown", this.checkTab, this);
6406         }
6407         //this.wrap.removeClass('x-trigger-wrap-focus');
6408         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6409     },
6410
6411     // private
6412     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6413     validateBlur : function(e, t){
6414         return true;
6415     },
6416
6417     // private
6418     onDisable : function(){
6419         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6420         //if(this.wrap){
6421         //    this.wrap.addClass('x-item-disabled');
6422         //}
6423     },
6424
6425     // private
6426     onEnable : function(){
6427         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6428         //if(this.wrap){
6429         //    this.el.removeClass('x-item-disabled');
6430         //}
6431     },
6432
6433     // private
6434     onShow : function(){
6435         var ae = this.getActionEl();
6436         
6437         if(ae){
6438             ae.dom.style.display = '';
6439             ae.dom.style.visibility = 'visible';
6440         }
6441     },
6442
6443     // private
6444     
6445     onHide : function(){
6446         var ae = this.getActionEl();
6447         ae.dom.style.display = 'none';
6448     },
6449
6450     /**
6451      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6452      * by an implementing function.
6453      * @method
6454      * @param {EventObject} e
6455      */
6456     onTriggerClick : Roo.emptyFn
6457 });
6458  /*
6459  * Based on:
6460  * Ext JS Library 1.1.1
6461  * Copyright(c) 2006-2007, Ext JS, LLC.
6462  *
6463  * Originally Released Under LGPL - original licence link has changed is not relivant.
6464  *
6465  * Fork - LGPL
6466  * <script type="text/javascript">
6467  */
6468
6469
6470 /**
6471  * @class Roo.data.SortTypes
6472  * @singleton
6473  * Defines the default sorting (casting?) comparison functions used when sorting data.
6474  */
6475 Roo.data.SortTypes = {
6476     /**
6477      * Default sort that does nothing
6478      * @param {Mixed} s The value being converted
6479      * @return {Mixed} The comparison value
6480      */
6481     none : function(s){
6482         return s;
6483     },
6484     
6485     /**
6486      * The regular expression used to strip tags
6487      * @type {RegExp}
6488      * @property
6489      */
6490     stripTagsRE : /<\/?[^>]+>/gi,
6491     
6492     /**
6493      * Strips all HTML tags to sort on text only
6494      * @param {Mixed} s The value being converted
6495      * @return {String} The comparison value
6496      */
6497     asText : function(s){
6498         return String(s).replace(this.stripTagsRE, "");
6499     },
6500     
6501     /**
6502      * Strips all HTML tags to sort on text only - Case insensitive
6503      * @param {Mixed} s The value being converted
6504      * @return {String} The comparison value
6505      */
6506     asUCText : function(s){
6507         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6508     },
6509     
6510     /**
6511      * Case insensitive string
6512      * @param {Mixed} s The value being converted
6513      * @return {String} The comparison value
6514      */
6515     asUCString : function(s) {
6516         return String(s).toUpperCase();
6517     },
6518     
6519     /**
6520      * Date sorting
6521      * @param {Mixed} s The value being converted
6522      * @return {Number} The comparison value
6523      */
6524     asDate : function(s) {
6525         if(!s){
6526             return 0;
6527         }
6528         if(s instanceof Date){
6529             return s.getTime();
6530         }
6531         return Date.parse(String(s));
6532     },
6533     
6534     /**
6535      * Float sorting
6536      * @param {Mixed} s The value being converted
6537      * @return {Float} The comparison value
6538      */
6539     asFloat : function(s) {
6540         var val = parseFloat(String(s).replace(/,/g, ""));
6541         if(isNaN(val)) val = 0;
6542         return val;
6543     },
6544     
6545     /**
6546      * Integer sorting
6547      * @param {Mixed} s The value being converted
6548      * @return {Number} The comparison value
6549      */
6550     asInt : function(s) {
6551         var val = parseInt(String(s).replace(/,/g, ""));
6552         if(isNaN(val)) val = 0;
6553         return val;
6554     }
6555 };/*
6556  * Based on:
6557  * Ext JS Library 1.1.1
6558  * Copyright(c) 2006-2007, Ext JS, LLC.
6559  *
6560  * Originally Released Under LGPL - original licence link has changed is not relivant.
6561  *
6562  * Fork - LGPL
6563  * <script type="text/javascript">
6564  */
6565
6566 /**
6567 * @class Roo.data.Record
6568  * Instances of this class encapsulate both record <em>definition</em> information, and record
6569  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6570  * to access Records cached in an {@link Roo.data.Store} object.<br>
6571  * <p>
6572  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6573  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6574  * objects.<br>
6575  * <p>
6576  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6577  * @constructor
6578  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6579  * {@link #create}. The parameters are the same.
6580  * @param {Array} data An associative Array of data values keyed by the field name.
6581  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6582  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6583  * not specified an integer id is generated.
6584  */
6585 Roo.data.Record = function(data, id){
6586     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6587     this.data = data;
6588 };
6589
6590 /**
6591  * Generate a constructor for a specific record layout.
6592  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6593  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6594  * Each field definition object may contain the following properties: <ul>
6595  * <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,
6596  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6597  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6598  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6599  * is being used, then this is a string containing the javascript expression to reference the data relative to 
6600  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6601  * to the data item relative to the record element. If the mapping expression is the same as the field name,
6602  * this may be omitted.</p></li>
6603  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6604  * <ul><li>auto (Default, implies no conversion)</li>
6605  * <li>string</li>
6606  * <li>int</li>
6607  * <li>float</li>
6608  * <li>boolean</li>
6609  * <li>date</li></ul></p></li>
6610  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6611  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6612  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6613  * by the Reader into an object that will be stored in the Record. It is passed the
6614  * following parameters:<ul>
6615  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6616  * </ul></p></li>
6617  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6618  * </ul>
6619  * <br>usage:<br><pre><code>
6620 var TopicRecord = Roo.data.Record.create(
6621     {name: 'title', mapping: 'topic_title'},
6622     {name: 'author', mapping: 'username'},
6623     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6624     {name: 'lastPost', mapping: 'post_time', type: 'date'},
6625     {name: 'lastPoster', mapping: 'user2'},
6626     {name: 'excerpt', mapping: 'post_text'}
6627 );
6628
6629 var myNewRecord = new TopicRecord({
6630     title: 'Do my job please',
6631     author: 'noobie',
6632     totalPosts: 1,
6633     lastPost: new Date(),
6634     lastPoster: 'Animal',
6635     excerpt: 'No way dude!'
6636 });
6637 myStore.add(myNewRecord);
6638 </code></pre>
6639  * @method create
6640  * @static
6641  */
6642 Roo.data.Record.create = function(o){
6643     var f = function(){
6644         f.superclass.constructor.apply(this, arguments);
6645     };
6646     Roo.extend(f, Roo.data.Record);
6647     var p = f.prototype;
6648     p.fields = new Roo.util.MixedCollection(false, function(field){
6649         return field.name;
6650     });
6651     for(var i = 0, len = o.length; i < len; i++){
6652         p.fields.add(new Roo.data.Field(o[i]));
6653     }
6654     f.getField = function(name){
6655         return p.fields.get(name);  
6656     };
6657     return f;
6658 };
6659
6660 Roo.data.Record.AUTO_ID = 1000;
6661 Roo.data.Record.EDIT = 'edit';
6662 Roo.data.Record.REJECT = 'reject';
6663 Roo.data.Record.COMMIT = 'commit';
6664
6665 Roo.data.Record.prototype = {
6666     /**
6667      * Readonly flag - true if this record has been modified.
6668      * @type Boolean
6669      */
6670     dirty : false,
6671     editing : false,
6672     error: null,
6673     modified: null,
6674
6675     // private
6676     join : function(store){
6677         this.store = store;
6678     },
6679
6680     /**
6681      * Set the named field to the specified value.
6682      * @param {String} name The name of the field to set.
6683      * @param {Object} value The value to set the field to.
6684      */
6685     set : function(name, value){
6686         if(this.data[name] == value){
6687             return;
6688         }
6689         this.dirty = true;
6690         if(!this.modified){
6691             this.modified = {};
6692         }
6693         if(typeof this.modified[name] == 'undefined'){
6694             this.modified[name] = this.data[name];
6695         }
6696         this.data[name] = value;
6697         if(!this.editing && this.store){
6698             this.store.afterEdit(this);
6699         }       
6700     },
6701
6702     /**
6703      * Get the value of the named field.
6704      * @param {String} name The name of the field to get the value of.
6705      * @return {Object} The value of the field.
6706      */
6707     get : function(name){
6708         return this.data[name]; 
6709     },
6710
6711     // private
6712     beginEdit : function(){
6713         this.editing = true;
6714         this.modified = {}; 
6715     },
6716
6717     // private
6718     cancelEdit : function(){
6719         this.editing = false;
6720         delete this.modified;
6721     },
6722
6723     // private
6724     endEdit : function(){
6725         this.editing = false;
6726         if(this.dirty && this.store){
6727             this.store.afterEdit(this);
6728         }
6729     },
6730
6731     /**
6732      * Usually called by the {@link Roo.data.Store} which owns the Record.
6733      * Rejects all changes made to the Record since either creation, or the last commit operation.
6734      * Modified fields are reverted to their original values.
6735      * <p>
6736      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6737      * of reject operations.
6738      */
6739     reject : function(){
6740         var m = this.modified;
6741         for(var n in m){
6742             if(typeof m[n] != "function"){
6743                 this.data[n] = m[n];
6744             }
6745         }
6746         this.dirty = false;
6747         delete this.modified;
6748         this.editing = false;
6749         if(this.store){
6750             this.store.afterReject(this);
6751         }
6752     },
6753
6754     /**
6755      * Usually called by the {@link Roo.data.Store} which owns the Record.
6756      * Commits all changes made to the Record since either creation, or the last commit operation.
6757      * <p>
6758      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6759      * of commit operations.
6760      */
6761     commit : function(){
6762         this.dirty = false;
6763         delete this.modified;
6764         this.editing = false;
6765         if(this.store){
6766             this.store.afterCommit(this);
6767         }
6768     },
6769
6770     // private
6771     hasError : function(){
6772         return this.error != null;
6773     },
6774
6775     // private
6776     clearError : function(){
6777         this.error = null;
6778     },
6779
6780     /**
6781      * Creates a copy of this record.
6782      * @param {String} id (optional) A new record id if you don't want to use this record's id
6783      * @return {Record}
6784      */
6785     copy : function(newId) {
6786         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6787     }
6788 };/*
6789  * Based on:
6790  * Ext JS Library 1.1.1
6791  * Copyright(c) 2006-2007, Ext JS, LLC.
6792  *
6793  * Originally Released Under LGPL - original licence link has changed is not relivant.
6794  *
6795  * Fork - LGPL
6796  * <script type="text/javascript">
6797  */
6798
6799
6800
6801 /**
6802  * @class Roo.data.Store
6803  * @extends Roo.util.Observable
6804  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6805  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6806  * <p>
6807  * 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
6808  * has no knowledge of the format of the data returned by the Proxy.<br>
6809  * <p>
6810  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6811  * instances from the data object. These records are cached and made available through accessor functions.
6812  * @constructor
6813  * Creates a new Store.
6814  * @param {Object} config A config object containing the objects needed for the Store to access data,
6815  * and read the data into Records.
6816  */
6817 Roo.data.Store = function(config){
6818     this.data = new Roo.util.MixedCollection(false);
6819     this.data.getKey = function(o){
6820         return o.id;
6821     };
6822     this.baseParams = {};
6823     // private
6824     this.paramNames = {
6825         "start" : "start",
6826         "limit" : "limit",
6827         "sort" : "sort",
6828         "dir" : "dir",
6829         "multisort" : "_multisort"
6830     };
6831
6832     if(config && config.data){
6833         this.inlineData = config.data;
6834         delete config.data;
6835     }
6836
6837     Roo.apply(this, config);
6838     
6839     if(this.reader){ // reader passed
6840         this.reader = Roo.factory(this.reader, Roo.data);
6841         this.reader.xmodule = this.xmodule || false;
6842         if(!this.recordType){
6843             this.recordType = this.reader.recordType;
6844         }
6845         if(this.reader.onMetaChange){
6846             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6847         }
6848     }
6849
6850     if(this.recordType){
6851         this.fields = this.recordType.prototype.fields;
6852     }
6853     this.modified = [];
6854
6855     this.addEvents({
6856         /**
6857          * @event datachanged
6858          * Fires when the data cache has changed, and a widget which is using this Store
6859          * as a Record cache should refresh its view.
6860          * @param {Store} this
6861          */
6862         datachanged : true,
6863         /**
6864          * @event metachange
6865          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6866          * @param {Store} this
6867          * @param {Object} meta The JSON metadata
6868          */
6869         metachange : true,
6870         /**
6871          * @event add
6872          * Fires when Records have been added to the Store
6873          * @param {Store} this
6874          * @param {Roo.data.Record[]} records The array of Records added
6875          * @param {Number} index The index at which the record(s) were added
6876          */
6877         add : true,
6878         /**
6879          * @event remove
6880          * Fires when a Record has been removed from the Store
6881          * @param {Store} this
6882          * @param {Roo.data.Record} record The Record that was removed
6883          * @param {Number} index The index at which the record was removed
6884          */
6885         remove : true,
6886         /**
6887          * @event update
6888          * Fires when a Record has been updated
6889          * @param {Store} this
6890          * @param {Roo.data.Record} record The Record that was updated
6891          * @param {String} operation The update operation being performed.  Value may be one of:
6892          * <pre><code>
6893  Roo.data.Record.EDIT
6894  Roo.data.Record.REJECT
6895  Roo.data.Record.COMMIT
6896          * </code></pre>
6897          */
6898         update : true,
6899         /**
6900          * @event clear
6901          * Fires when the data cache has been cleared.
6902          * @param {Store} this
6903          */
6904         clear : true,
6905         /**
6906          * @event beforeload
6907          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6908          * the load action will be canceled.
6909          * @param {Store} this
6910          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6911          */
6912         beforeload : true,
6913         /**
6914          * @event beforeloadadd
6915          * Fires after a new set of Records has been loaded.
6916          * @param {Store} this
6917          * @param {Roo.data.Record[]} records The Records that were loaded
6918          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6919          */
6920         beforeloadadd : true,
6921         /**
6922          * @event load
6923          * Fires after a new set of Records has been loaded, before they are added to the store.
6924          * @param {Store} this
6925          * @param {Roo.data.Record[]} records The Records that were loaded
6926          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6927          * @params {Object} return from reader
6928          */
6929         load : true,
6930         /**
6931          * @event loadexception
6932          * Fires if an exception occurs in the Proxy during loading.
6933          * Called with the signature of the Proxy's "loadexception" event.
6934          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6935          * 
6936          * @param {Proxy} 
6937          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6938          * @param {Object} load options 
6939          * @param {Object} jsonData from your request (normally this contains the Exception)
6940          */
6941         loadexception : true
6942     });
6943     
6944     if(this.proxy){
6945         this.proxy = Roo.factory(this.proxy, Roo.data);
6946         this.proxy.xmodule = this.xmodule || false;
6947         this.relayEvents(this.proxy,  ["loadexception"]);
6948     }
6949     this.sortToggle = {};
6950     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6951
6952     Roo.data.Store.superclass.constructor.call(this);
6953
6954     if(this.inlineData){
6955         this.loadData(this.inlineData);
6956         delete this.inlineData;
6957     }
6958 };
6959
6960 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6961      /**
6962     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6963     * without a remote query - used by combo/forms at present.
6964     */
6965     
6966     /**
6967     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6968     */
6969     /**
6970     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6971     */
6972     /**
6973     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6974     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6975     */
6976     /**
6977     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6978     * on any HTTP request
6979     */
6980     /**
6981     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6982     */
6983     /**
6984     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6985     */
6986     multiSort: false,
6987     /**
6988     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6989     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6990     */
6991     remoteSort : false,
6992
6993     /**
6994     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6995      * loaded or when a record is removed. (defaults to false).
6996     */
6997     pruneModifiedRecords : false,
6998
6999     // private
7000     lastOptions : null,
7001
7002     /**
7003      * Add Records to the Store and fires the add event.
7004      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7005      */
7006     add : function(records){
7007         records = [].concat(records);
7008         for(var i = 0, len = records.length; i < len; i++){
7009             records[i].join(this);
7010         }
7011         var index = this.data.length;
7012         this.data.addAll(records);
7013         this.fireEvent("add", this, records, index);
7014     },
7015
7016     /**
7017      * Remove a Record from the Store and fires the remove event.
7018      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7019      */
7020     remove : function(record){
7021         var index = this.data.indexOf(record);
7022         this.data.removeAt(index);
7023         if(this.pruneModifiedRecords){
7024             this.modified.remove(record);
7025         }
7026         this.fireEvent("remove", this, record, index);
7027     },
7028
7029     /**
7030      * Remove all Records from the Store and fires the clear event.
7031      */
7032     removeAll : function(){
7033         this.data.clear();
7034         if(this.pruneModifiedRecords){
7035             this.modified = [];
7036         }
7037         this.fireEvent("clear", this);
7038     },
7039
7040     /**
7041      * Inserts Records to the Store at the given index and fires the add event.
7042      * @param {Number} index The start index at which to insert the passed Records.
7043      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7044      */
7045     insert : function(index, records){
7046         records = [].concat(records);
7047         for(var i = 0, len = records.length; i < len; i++){
7048             this.data.insert(index, records[i]);
7049             records[i].join(this);
7050         }
7051         this.fireEvent("add", this, records, index);
7052     },
7053
7054     /**
7055      * Get the index within the cache of the passed Record.
7056      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7057      * @return {Number} The index of the passed Record. Returns -1 if not found.
7058      */
7059     indexOf : function(record){
7060         return this.data.indexOf(record);
7061     },
7062
7063     /**
7064      * Get the index within the cache of the Record with the passed id.
7065      * @param {String} id The id of the Record to find.
7066      * @return {Number} The index of the Record. Returns -1 if not found.
7067      */
7068     indexOfId : function(id){
7069         return this.data.indexOfKey(id);
7070     },
7071
7072     /**
7073      * Get the Record with the specified id.
7074      * @param {String} id The id of the Record to find.
7075      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7076      */
7077     getById : function(id){
7078         return this.data.key(id);
7079     },
7080
7081     /**
7082      * Get the Record at the specified index.
7083      * @param {Number} index The index of the Record to find.
7084      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7085      */
7086     getAt : function(index){
7087         return this.data.itemAt(index);
7088     },
7089
7090     /**
7091      * Returns a range of Records between specified indices.
7092      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7093      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7094      * @return {Roo.data.Record[]} An array of Records
7095      */
7096     getRange : function(start, end){
7097         return this.data.getRange(start, end);
7098     },
7099
7100     // private
7101     storeOptions : function(o){
7102         o = Roo.apply({}, o);
7103         delete o.callback;
7104         delete o.scope;
7105         this.lastOptions = o;
7106     },
7107
7108     /**
7109      * Loads the Record cache from the configured Proxy using the configured Reader.
7110      * <p>
7111      * If using remote paging, then the first load call must specify the <em>start</em>
7112      * and <em>limit</em> properties in the options.params property to establish the initial
7113      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7114      * <p>
7115      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7116      * and this call will return before the new data has been loaded. Perform any post-processing
7117      * in a callback function, or in a "load" event handler.</strong>
7118      * <p>
7119      * @param {Object} options An object containing properties which control loading options:<ul>
7120      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7121      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7122      * passed the following arguments:<ul>
7123      * <li>r : Roo.data.Record[]</li>
7124      * <li>options: Options object from the load call</li>
7125      * <li>success: Boolean success indicator</li></ul></li>
7126      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7127      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7128      * </ul>
7129      */
7130     load : function(options){
7131         options = options || {};
7132         if(this.fireEvent("beforeload", this, options) !== false){
7133             this.storeOptions(options);
7134             var p = Roo.apply(options.params || {}, this.baseParams);
7135             // if meta was not loaded from remote source.. try requesting it.
7136             if (!this.reader.metaFromRemote) {
7137                 p._requestMeta = 1;
7138             }
7139             if(this.sortInfo && this.remoteSort){
7140                 var pn = this.paramNames;
7141                 p[pn["sort"]] = this.sortInfo.field;
7142                 p[pn["dir"]] = this.sortInfo.direction;
7143             }
7144             if (this.multiSort) {
7145                 var pn = this.paramNames;
7146                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7147             }
7148             
7149             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7150         }
7151     },
7152
7153     /**
7154      * Reloads the Record cache from the configured Proxy using the configured Reader and
7155      * the options from the last load operation performed.
7156      * @param {Object} options (optional) An object containing properties which may override the options
7157      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7158      * the most recently used options are reused).
7159      */
7160     reload : function(options){
7161         this.load(Roo.applyIf(options||{}, this.lastOptions));
7162     },
7163
7164     // private
7165     // Called as a callback by the Reader during a load operation.
7166     loadRecords : function(o, options, success){
7167         if(!o || success === false){
7168             if(success !== false){
7169                 this.fireEvent("load", this, [], options, o);
7170             }
7171             if(options.callback){
7172                 options.callback.call(options.scope || this, [], options, false);
7173             }
7174             return;
7175         }
7176         // if data returned failure - throw an exception.
7177         if (o.success === false) {
7178             // show a message if no listener is registered.
7179             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7180                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7181             }
7182             // loadmask wil be hooked into this..
7183             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7184             return;
7185         }
7186         var r = o.records, t = o.totalRecords || r.length;
7187         
7188         this.fireEvent("beforeloadadd", this, r, options, o);
7189         
7190         if(!options || options.add !== true){
7191             if(this.pruneModifiedRecords){
7192                 this.modified = [];
7193             }
7194             for(var i = 0, len = r.length; i < len; i++){
7195                 r[i].join(this);
7196             }
7197             if(this.snapshot){
7198                 this.data = this.snapshot;
7199                 delete this.snapshot;
7200             }
7201             this.data.clear();
7202             this.data.addAll(r);
7203             this.totalLength = t;
7204             this.applySort();
7205             this.fireEvent("datachanged", this);
7206         }else{
7207             this.totalLength = Math.max(t, this.data.length+r.length);
7208             this.add(r);
7209         }
7210         this.fireEvent("load", this, r, options, o);
7211         if(options.callback){
7212             options.callback.call(options.scope || this, r, options, true);
7213         }
7214     },
7215
7216
7217     /**
7218      * Loads data from a passed data block. A Reader which understands the format of the data
7219      * must have been configured in the constructor.
7220      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7221      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7222      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7223      */
7224     loadData : function(o, append){
7225         var r = this.reader.readRecords(o);
7226         this.loadRecords(r, {add: append}, true);
7227     },
7228
7229     /**
7230      * Gets the number of cached records.
7231      * <p>
7232      * <em>If using paging, this may not be the total size of the dataset. If the data object
7233      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7234      * the data set size</em>
7235      */
7236     getCount : function(){
7237         return this.data.length || 0;
7238     },
7239
7240     /**
7241      * Gets the total number of records in the dataset as returned by the server.
7242      * <p>
7243      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7244      * the dataset size</em>
7245      */
7246     getTotalCount : function(){
7247         return this.totalLength || 0;
7248     },
7249
7250     /**
7251      * Returns the sort state of the Store as an object with two properties:
7252      * <pre><code>
7253  field {String} The name of the field by which the Records are sorted
7254  direction {String} The sort order, "ASC" or "DESC"
7255      * </code></pre>
7256      */
7257     getSortState : function(){
7258         return this.sortInfo;
7259     },
7260
7261     // private
7262     applySort : function(){
7263         if(this.sortInfo && !this.remoteSort){
7264             var s = this.sortInfo, f = s.field;
7265             var st = this.fields.get(f).sortType;
7266             var fn = function(r1, r2){
7267                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7268                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7269             };
7270             this.data.sort(s.direction, fn);
7271             if(this.snapshot && this.snapshot != this.data){
7272                 this.snapshot.sort(s.direction, fn);
7273             }
7274         }
7275     },
7276
7277     /**
7278      * Sets the default sort column and order to be used by the next load operation.
7279      * @param {String} fieldName The name of the field to sort by.
7280      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7281      */
7282     setDefaultSort : function(field, dir){
7283         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7284     },
7285
7286     /**
7287      * Sort the Records.
7288      * If remote sorting is used, the sort is performed on the server, and the cache is
7289      * reloaded. If local sorting is used, the cache is sorted internally.
7290      * @param {String} fieldName The name of the field to sort by.
7291      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7292      */
7293     sort : function(fieldName, dir){
7294         var f = this.fields.get(fieldName);
7295         if(!dir){
7296             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7297             
7298             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7299                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7300             }else{
7301                 dir = f.sortDir;
7302             }
7303         }
7304         this.sortToggle[f.name] = dir;
7305         this.sortInfo = {field: f.name, direction: dir};
7306         if(!this.remoteSort){
7307             this.applySort();
7308             this.fireEvent("datachanged", this);
7309         }else{
7310             this.load(this.lastOptions);
7311         }
7312     },
7313
7314     /**
7315      * Calls the specified function for each of the Records in the cache.
7316      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7317      * Returning <em>false</em> aborts and exits the iteration.
7318      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7319      */
7320     each : function(fn, scope){
7321         this.data.each(fn, scope);
7322     },
7323
7324     /**
7325      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7326      * (e.g., during paging).
7327      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7328      */
7329     getModifiedRecords : function(){
7330         return this.modified;
7331     },
7332
7333     // private
7334     createFilterFn : function(property, value, anyMatch){
7335         if(!value.exec){ // not a regex
7336             value = String(value);
7337             if(value.length == 0){
7338                 return false;
7339             }
7340             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7341         }
7342         return function(r){
7343             return value.test(r.data[property]);
7344         };
7345     },
7346
7347     /**
7348      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7349      * @param {String} property A field on your records
7350      * @param {Number} start The record index to start at (defaults to 0)
7351      * @param {Number} end The last record index to include (defaults to length - 1)
7352      * @return {Number} The sum
7353      */
7354     sum : function(property, start, end){
7355         var rs = this.data.items, v = 0;
7356         start = start || 0;
7357         end = (end || end === 0) ? end : rs.length-1;
7358
7359         for(var i = start; i <= end; i++){
7360             v += (rs[i].data[property] || 0);
7361         }
7362         return v;
7363     },
7364
7365     /**
7366      * Filter the records by a specified property.
7367      * @param {String} field A field on your records
7368      * @param {String/RegExp} value Either a string that the field
7369      * should start with or a RegExp to test against the field
7370      * @param {Boolean} anyMatch True to match any part not just the beginning
7371      */
7372     filter : function(property, value, anyMatch){
7373         var fn = this.createFilterFn(property, value, anyMatch);
7374         return fn ? this.filterBy(fn) : this.clearFilter();
7375     },
7376
7377     /**
7378      * Filter by a function. The specified function will be called with each
7379      * record in this data source. If the function returns true the record is included,
7380      * otherwise it is filtered.
7381      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7382      * @param {Object} scope (optional) The scope of the function (defaults to this)
7383      */
7384     filterBy : function(fn, scope){
7385         this.snapshot = this.snapshot || this.data;
7386         this.data = this.queryBy(fn, scope||this);
7387         this.fireEvent("datachanged", this);
7388     },
7389
7390     /**
7391      * Query the records by a specified property.
7392      * @param {String} field A field on your records
7393      * @param {String/RegExp} value Either a string that the field
7394      * should start with or a RegExp to test against the field
7395      * @param {Boolean} anyMatch True to match any part not just the beginning
7396      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7397      */
7398     query : function(property, value, anyMatch){
7399         var fn = this.createFilterFn(property, value, anyMatch);
7400         return fn ? this.queryBy(fn) : this.data.clone();
7401     },
7402
7403     /**
7404      * Query by a function. The specified function will be called with each
7405      * record in this data source. If the function returns true the record is included
7406      * in the results.
7407      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7408      * @param {Object} scope (optional) The scope of the function (defaults to this)
7409       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7410      **/
7411     queryBy : function(fn, scope){
7412         var data = this.snapshot || this.data;
7413         return data.filterBy(fn, scope||this);
7414     },
7415
7416     /**
7417      * Collects unique values for a particular dataIndex from this store.
7418      * @param {String} dataIndex The property to collect
7419      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7420      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7421      * @return {Array} An array of the unique values
7422      **/
7423     collect : function(dataIndex, allowNull, bypassFilter){
7424         var d = (bypassFilter === true && this.snapshot) ?
7425                 this.snapshot.items : this.data.items;
7426         var v, sv, r = [], l = {};
7427         for(var i = 0, len = d.length; i < len; i++){
7428             v = d[i].data[dataIndex];
7429             sv = String(v);
7430             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7431                 l[sv] = true;
7432                 r[r.length] = v;
7433             }
7434         }
7435         return r;
7436     },
7437
7438     /**
7439      * Revert to a view of the Record cache with no filtering applied.
7440      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7441      */
7442     clearFilter : function(suppressEvent){
7443         if(this.snapshot && this.snapshot != this.data){
7444             this.data = this.snapshot;
7445             delete this.snapshot;
7446             if(suppressEvent !== true){
7447                 this.fireEvent("datachanged", this);
7448             }
7449         }
7450     },
7451
7452     // private
7453     afterEdit : function(record){
7454         if(this.modified.indexOf(record) == -1){
7455             this.modified.push(record);
7456         }
7457         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7458     },
7459     
7460     // private
7461     afterReject : function(record){
7462         this.modified.remove(record);
7463         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7464     },
7465
7466     // private
7467     afterCommit : function(record){
7468         this.modified.remove(record);
7469         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7470     },
7471
7472     /**
7473      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7474      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7475      */
7476     commitChanges : function(){
7477         var m = this.modified.slice(0);
7478         this.modified = [];
7479         for(var i = 0, len = m.length; i < len; i++){
7480             m[i].commit();
7481         }
7482     },
7483
7484     /**
7485      * Cancel outstanding changes on all changed records.
7486      */
7487     rejectChanges : function(){
7488         var m = this.modified.slice(0);
7489         this.modified = [];
7490         for(var i = 0, len = m.length; i < len; i++){
7491             m[i].reject();
7492         }
7493     },
7494
7495     onMetaChange : function(meta, rtype, o){
7496         this.recordType = rtype;
7497         this.fields = rtype.prototype.fields;
7498         delete this.snapshot;
7499         this.sortInfo = meta.sortInfo || this.sortInfo;
7500         this.modified = [];
7501         this.fireEvent('metachange', this, this.reader.meta);
7502     },
7503     
7504     moveIndex : function(data, type)
7505     {
7506         var index = this.indexOf(data);
7507         
7508         var newIndex = index + type;
7509         
7510         this.remove(data);
7511         
7512         this.insert(newIndex, data);
7513         
7514     }
7515 });/*
7516  * Based on:
7517  * Ext JS Library 1.1.1
7518  * Copyright(c) 2006-2007, Ext JS, LLC.
7519  *
7520  * Originally Released Under LGPL - original licence link has changed is not relivant.
7521  *
7522  * Fork - LGPL
7523  * <script type="text/javascript">
7524  */
7525
7526 /**
7527  * @class Roo.data.SimpleStore
7528  * @extends Roo.data.Store
7529  * Small helper class to make creating Stores from Array data easier.
7530  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7531  * @cfg {Array} fields An array of field definition objects, or field name strings.
7532  * @cfg {Array} data The multi-dimensional array of data
7533  * @constructor
7534  * @param {Object} config
7535  */
7536 Roo.data.SimpleStore = function(config){
7537     Roo.data.SimpleStore.superclass.constructor.call(this, {
7538         isLocal : true,
7539         reader: new Roo.data.ArrayReader({
7540                 id: config.id
7541             },
7542             Roo.data.Record.create(config.fields)
7543         ),
7544         proxy : new Roo.data.MemoryProxy(config.data)
7545     });
7546     this.load();
7547 };
7548 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7549  * Based on:
7550  * Ext JS Library 1.1.1
7551  * Copyright(c) 2006-2007, Ext JS, LLC.
7552  *
7553  * Originally Released Under LGPL - original licence link has changed is not relivant.
7554  *
7555  * Fork - LGPL
7556  * <script type="text/javascript">
7557  */
7558
7559 /**
7560 /**
7561  * @extends Roo.data.Store
7562  * @class Roo.data.JsonStore
7563  * Small helper class to make creating Stores for JSON data easier. <br/>
7564 <pre><code>
7565 var store = new Roo.data.JsonStore({
7566     url: 'get-images.php',
7567     root: 'images',
7568     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7569 });
7570 </code></pre>
7571  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7572  * JsonReader and HttpProxy (unless inline data is provided).</b>
7573  * @cfg {Array} fields An array of field definition objects, or field name strings.
7574  * @constructor
7575  * @param {Object} config
7576  */
7577 Roo.data.JsonStore = function(c){
7578     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7579         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7580         reader: new Roo.data.JsonReader(c, c.fields)
7581     }));
7582 };
7583 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7584  * Based on:
7585  * Ext JS Library 1.1.1
7586  * Copyright(c) 2006-2007, Ext JS, LLC.
7587  *
7588  * Originally Released Under LGPL - original licence link has changed is not relivant.
7589  *
7590  * Fork - LGPL
7591  * <script type="text/javascript">
7592  */
7593
7594  
7595 Roo.data.Field = function(config){
7596     if(typeof config == "string"){
7597         config = {name: config};
7598     }
7599     Roo.apply(this, config);
7600     
7601     if(!this.type){
7602         this.type = "auto";
7603     }
7604     
7605     var st = Roo.data.SortTypes;
7606     // named sortTypes are supported, here we look them up
7607     if(typeof this.sortType == "string"){
7608         this.sortType = st[this.sortType];
7609     }
7610     
7611     // set default sortType for strings and dates
7612     if(!this.sortType){
7613         switch(this.type){
7614             case "string":
7615                 this.sortType = st.asUCString;
7616                 break;
7617             case "date":
7618                 this.sortType = st.asDate;
7619                 break;
7620             default:
7621                 this.sortType = st.none;
7622         }
7623     }
7624
7625     // define once
7626     var stripRe = /[\$,%]/g;
7627
7628     // prebuilt conversion function for this field, instead of
7629     // switching every time we're reading a value
7630     if(!this.convert){
7631         var cv, dateFormat = this.dateFormat;
7632         switch(this.type){
7633             case "":
7634             case "auto":
7635             case undefined:
7636                 cv = function(v){ return v; };
7637                 break;
7638             case "string":
7639                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7640                 break;
7641             case "int":
7642                 cv = function(v){
7643                     return v !== undefined && v !== null && v !== '' ?
7644                            parseInt(String(v).replace(stripRe, ""), 10) : '';
7645                     };
7646                 break;
7647             case "float":
7648                 cv = function(v){
7649                     return v !== undefined && v !== null && v !== '' ?
7650                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
7651                     };
7652                 break;
7653             case "bool":
7654             case "boolean":
7655                 cv = function(v){ return v === true || v === "true" || v == 1; };
7656                 break;
7657             case "date":
7658                 cv = function(v){
7659                     if(!v){
7660                         return '';
7661                     }
7662                     if(v instanceof Date){
7663                         return v;
7664                     }
7665                     if(dateFormat){
7666                         if(dateFormat == "timestamp"){
7667                             return new Date(v*1000);
7668                         }
7669                         return Date.parseDate(v, dateFormat);
7670                     }
7671                     var parsed = Date.parse(v);
7672                     return parsed ? new Date(parsed) : null;
7673                 };
7674              break;
7675             
7676         }
7677         this.convert = cv;
7678     }
7679 };
7680
7681 Roo.data.Field.prototype = {
7682     dateFormat: null,
7683     defaultValue: "",
7684     mapping: null,
7685     sortType : null,
7686     sortDir : "ASC"
7687 };/*
7688  * Based on:
7689  * Ext JS Library 1.1.1
7690  * Copyright(c) 2006-2007, Ext JS, LLC.
7691  *
7692  * Originally Released Under LGPL - original licence link has changed is not relivant.
7693  *
7694  * Fork - LGPL
7695  * <script type="text/javascript">
7696  */
7697  
7698 // Base class for reading structured data from a data source.  This class is intended to be
7699 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7700
7701 /**
7702  * @class Roo.data.DataReader
7703  * Base class for reading structured data from a data source.  This class is intended to be
7704  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7705  */
7706
7707 Roo.data.DataReader = function(meta, recordType){
7708     
7709     this.meta = meta;
7710     
7711     this.recordType = recordType instanceof Array ? 
7712         Roo.data.Record.create(recordType) : recordType;
7713 };
7714
7715 Roo.data.DataReader.prototype = {
7716      /**
7717      * Create an empty record
7718      * @param {Object} data (optional) - overlay some values
7719      * @return {Roo.data.Record} record created.
7720      */
7721     newRow :  function(d) {
7722         var da =  {};
7723         this.recordType.prototype.fields.each(function(c) {
7724             switch( c.type) {
7725                 case 'int' : da[c.name] = 0; break;
7726                 case 'date' : da[c.name] = new Date(); break;
7727                 case 'float' : da[c.name] = 0.0; break;
7728                 case 'boolean' : da[c.name] = false; break;
7729                 default : da[c.name] = ""; break;
7730             }
7731             
7732         });
7733         return new this.recordType(Roo.apply(da, d));
7734     }
7735     
7736 };/*
7737  * Based on:
7738  * Ext JS Library 1.1.1
7739  * Copyright(c) 2006-2007, Ext JS, LLC.
7740  *
7741  * Originally Released Under LGPL - original licence link has changed is not relivant.
7742  *
7743  * Fork - LGPL
7744  * <script type="text/javascript">
7745  */
7746
7747 /**
7748  * @class Roo.data.DataProxy
7749  * @extends Roo.data.Observable
7750  * This class is an abstract base class for implementations which provide retrieval of
7751  * unformatted data objects.<br>
7752  * <p>
7753  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7754  * (of the appropriate type which knows how to parse the data object) to provide a block of
7755  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7756  * <p>
7757  * Custom implementations must implement the load method as described in
7758  * {@link Roo.data.HttpProxy#load}.
7759  */
7760 Roo.data.DataProxy = function(){
7761     this.addEvents({
7762         /**
7763          * @event beforeload
7764          * Fires before a network request is made to retrieve a data object.
7765          * @param {Object} This DataProxy object.
7766          * @param {Object} params The params parameter to the load function.
7767          */
7768         beforeload : true,
7769         /**
7770          * @event load
7771          * Fires before the load method's callback is called.
7772          * @param {Object} This DataProxy object.
7773          * @param {Object} o The data object.
7774          * @param {Object} arg The callback argument object passed to the load function.
7775          */
7776         load : true,
7777         /**
7778          * @event loadexception
7779          * Fires if an Exception occurs during data retrieval.
7780          * @param {Object} This DataProxy object.
7781          * @param {Object} o The data object.
7782          * @param {Object} arg The callback argument object passed to the load function.
7783          * @param {Object} e The Exception.
7784          */
7785         loadexception : true
7786     });
7787     Roo.data.DataProxy.superclass.constructor.call(this);
7788 };
7789
7790 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7791
7792     /**
7793      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7794      */
7795 /*
7796  * Based on:
7797  * Ext JS Library 1.1.1
7798  * Copyright(c) 2006-2007, Ext JS, LLC.
7799  *
7800  * Originally Released Under LGPL - original licence link has changed is not relivant.
7801  *
7802  * Fork - LGPL
7803  * <script type="text/javascript">
7804  */
7805 /**
7806  * @class Roo.data.MemoryProxy
7807  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7808  * to the Reader when its load method is called.
7809  * @constructor
7810  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7811  */
7812 Roo.data.MemoryProxy = function(data){
7813     if (data.data) {
7814         data = data.data;
7815     }
7816     Roo.data.MemoryProxy.superclass.constructor.call(this);
7817     this.data = data;
7818 };
7819
7820 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7821     /**
7822      * Load data from the requested source (in this case an in-memory
7823      * data object passed to the constructor), read the data object into
7824      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7825      * process that block using the passed callback.
7826      * @param {Object} params This parameter is not used by the MemoryProxy class.
7827      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7828      * object into a block of Roo.data.Records.
7829      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7830      * The function must be passed <ul>
7831      * <li>The Record block object</li>
7832      * <li>The "arg" argument from the load function</li>
7833      * <li>A boolean success indicator</li>
7834      * </ul>
7835      * @param {Object} scope The scope in which to call the callback
7836      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7837      */
7838     load : function(params, reader, callback, scope, arg){
7839         params = params || {};
7840         var result;
7841         try {
7842             result = reader.readRecords(this.data);
7843         }catch(e){
7844             this.fireEvent("loadexception", this, arg, null, e);
7845             callback.call(scope, null, arg, false);
7846             return;
7847         }
7848         callback.call(scope, result, arg, true);
7849     },
7850     
7851     // private
7852     update : function(params, records){
7853         
7854     }
7855 });/*
7856  * Based on:
7857  * Ext JS Library 1.1.1
7858  * Copyright(c) 2006-2007, Ext JS, LLC.
7859  *
7860  * Originally Released Under LGPL - original licence link has changed is not relivant.
7861  *
7862  * Fork - LGPL
7863  * <script type="text/javascript">
7864  */
7865 /**
7866  * @class Roo.data.HttpProxy
7867  * @extends Roo.data.DataProxy
7868  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7869  * configured to reference a certain URL.<br><br>
7870  * <p>
7871  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7872  * from which the running page was served.<br><br>
7873  * <p>
7874  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7875  * <p>
7876  * Be aware that to enable the browser to parse an XML document, the server must set
7877  * the Content-Type header in the HTTP response to "text/xml".
7878  * @constructor
7879  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7880  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7881  * will be used to make the request.
7882  */
7883 Roo.data.HttpProxy = function(conn){
7884     Roo.data.HttpProxy.superclass.constructor.call(this);
7885     // is conn a conn config or a real conn?
7886     this.conn = conn;
7887     this.useAjax = !conn || !conn.events;
7888   
7889 };
7890
7891 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7892     // thse are take from connection...
7893     
7894     /**
7895      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7896      */
7897     /**
7898      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7899      * extra parameters to each request made by this object. (defaults to undefined)
7900      */
7901     /**
7902      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7903      *  to each request made by this object. (defaults to undefined)
7904      */
7905     /**
7906      * @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)
7907      */
7908     /**
7909      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7910      */
7911      /**
7912      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7913      * @type Boolean
7914      */
7915   
7916
7917     /**
7918      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7919      * @type Boolean
7920      */
7921     /**
7922      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7923      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7924      * a finer-grained basis than the DataProxy events.
7925      */
7926     getConnection : function(){
7927         return this.useAjax ? Roo.Ajax : this.conn;
7928     },
7929
7930     /**
7931      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7932      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7933      * process that block using the passed callback.
7934      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7935      * for the request to the remote server.
7936      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7937      * object into a block of Roo.data.Records.
7938      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7939      * The function must be passed <ul>
7940      * <li>The Record block object</li>
7941      * <li>The "arg" argument from the load function</li>
7942      * <li>A boolean success indicator</li>
7943      * </ul>
7944      * @param {Object} scope The scope in which to call the callback
7945      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7946      */
7947     load : function(params, reader, callback, scope, arg){
7948         if(this.fireEvent("beforeload", this, params) !== false){
7949             var  o = {
7950                 params : params || {},
7951                 request: {
7952                     callback : callback,
7953                     scope : scope,
7954                     arg : arg
7955                 },
7956                 reader: reader,
7957                 callback : this.loadResponse,
7958                 scope: this
7959             };
7960             if(this.useAjax){
7961                 Roo.applyIf(o, this.conn);
7962                 if(this.activeRequest){
7963                     Roo.Ajax.abort(this.activeRequest);
7964                 }
7965                 this.activeRequest = Roo.Ajax.request(o);
7966             }else{
7967                 this.conn.request(o);
7968             }
7969         }else{
7970             callback.call(scope||this, null, arg, false);
7971         }
7972     },
7973
7974     // private
7975     loadResponse : function(o, success, response){
7976         delete this.activeRequest;
7977         if(!success){
7978             this.fireEvent("loadexception", this, o, response);
7979             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7980             return;
7981         }
7982         var result;
7983         try {
7984             result = o.reader.read(response);
7985         }catch(e){
7986             this.fireEvent("loadexception", this, o, response, e);
7987             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7988             return;
7989         }
7990         
7991         this.fireEvent("load", this, o, o.request.arg);
7992         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7993     },
7994
7995     // private
7996     update : function(dataSet){
7997
7998     },
7999
8000     // private
8001     updateResponse : function(dataSet){
8002
8003     }
8004 });/*
8005  * Based on:
8006  * Ext JS Library 1.1.1
8007  * Copyright(c) 2006-2007, Ext JS, LLC.
8008  *
8009  * Originally Released Under LGPL - original licence link has changed is not relivant.
8010  *
8011  * Fork - LGPL
8012  * <script type="text/javascript">
8013  */
8014
8015 /**
8016  * @class Roo.data.ScriptTagProxy
8017  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8018  * other than the originating domain of the running page.<br><br>
8019  * <p>
8020  * <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
8021  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8022  * <p>
8023  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8024  * source code that is used as the source inside a &lt;script> tag.<br><br>
8025  * <p>
8026  * In order for the browser to process the returned data, the server must wrap the data object
8027  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8028  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8029  * depending on whether the callback name was passed:
8030  * <p>
8031  * <pre><code>
8032 boolean scriptTag = false;
8033 String cb = request.getParameter("callback");
8034 if (cb != null) {
8035     scriptTag = true;
8036     response.setContentType("text/javascript");
8037 } else {
8038     response.setContentType("application/x-json");
8039 }
8040 Writer out = response.getWriter();
8041 if (scriptTag) {
8042     out.write(cb + "(");
8043 }
8044 out.print(dataBlock.toJsonString());
8045 if (scriptTag) {
8046     out.write(");");
8047 }
8048 </pre></code>
8049  *
8050  * @constructor
8051  * @param {Object} config A configuration object.
8052  */
8053 Roo.data.ScriptTagProxy = function(config){
8054     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8055     Roo.apply(this, config);
8056     this.head = document.getElementsByTagName("head")[0];
8057 };
8058
8059 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8060
8061 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8062     /**
8063      * @cfg {String} url The URL from which to request the data object.
8064      */
8065     /**
8066      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8067      */
8068     timeout : 30000,
8069     /**
8070      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8071      * the server the name of the callback function set up by the load call to process the returned data object.
8072      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8073      * javascript output which calls this named function passing the data object as its only parameter.
8074      */
8075     callbackParam : "callback",
8076     /**
8077      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8078      * name to the request.
8079      */
8080     nocache : true,
8081
8082     /**
8083      * Load data from the configured URL, read the data object into
8084      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8085      * process that block using the passed callback.
8086      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8087      * for the request to the remote server.
8088      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8089      * object into a block of Roo.data.Records.
8090      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8091      * The function must be passed <ul>
8092      * <li>The Record block object</li>
8093      * <li>The "arg" argument from the load function</li>
8094      * <li>A boolean success indicator</li>
8095      * </ul>
8096      * @param {Object} scope The scope in which to call the callback
8097      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8098      */
8099     load : function(params, reader, callback, scope, arg){
8100         if(this.fireEvent("beforeload", this, params) !== false){
8101
8102             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8103
8104             var url = this.url;
8105             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8106             if(this.nocache){
8107                 url += "&_dc=" + (new Date().getTime());
8108             }
8109             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8110             var trans = {
8111                 id : transId,
8112                 cb : "stcCallback"+transId,
8113                 scriptId : "stcScript"+transId,
8114                 params : params,
8115                 arg : arg,
8116                 url : url,
8117                 callback : callback,
8118                 scope : scope,
8119                 reader : reader
8120             };
8121             var conn = this;
8122
8123             window[trans.cb] = function(o){
8124                 conn.handleResponse(o, trans);
8125             };
8126
8127             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8128
8129             if(this.autoAbort !== false){
8130                 this.abort();
8131             }
8132
8133             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8134
8135             var script = document.createElement("script");
8136             script.setAttribute("src", url);
8137             script.setAttribute("type", "text/javascript");
8138             script.setAttribute("id", trans.scriptId);
8139             this.head.appendChild(script);
8140
8141             this.trans = trans;
8142         }else{
8143             callback.call(scope||this, null, arg, false);
8144         }
8145     },
8146
8147     // private
8148     isLoading : function(){
8149         return this.trans ? true : false;
8150     },
8151
8152     /**
8153      * Abort the current server request.
8154      */
8155     abort : function(){
8156         if(this.isLoading()){
8157             this.destroyTrans(this.trans);
8158         }
8159     },
8160
8161     // private
8162     destroyTrans : function(trans, isLoaded){
8163         this.head.removeChild(document.getElementById(trans.scriptId));
8164         clearTimeout(trans.timeoutId);
8165         if(isLoaded){
8166             window[trans.cb] = undefined;
8167             try{
8168                 delete window[trans.cb];
8169             }catch(e){}
8170         }else{
8171             // if hasn't been loaded, wait for load to remove it to prevent script error
8172             window[trans.cb] = function(){
8173                 window[trans.cb] = undefined;
8174                 try{
8175                     delete window[trans.cb];
8176                 }catch(e){}
8177             };
8178         }
8179     },
8180
8181     // private
8182     handleResponse : function(o, trans){
8183         this.trans = false;
8184         this.destroyTrans(trans, true);
8185         var result;
8186         try {
8187             result = trans.reader.readRecords(o);
8188         }catch(e){
8189             this.fireEvent("loadexception", this, o, trans.arg, e);
8190             trans.callback.call(trans.scope||window, null, trans.arg, false);
8191             return;
8192         }
8193         this.fireEvent("load", this, o, trans.arg);
8194         trans.callback.call(trans.scope||window, result, trans.arg, true);
8195     },
8196
8197     // private
8198     handleFailure : function(trans){
8199         this.trans = false;
8200         this.destroyTrans(trans, false);
8201         this.fireEvent("loadexception", this, null, trans.arg);
8202         trans.callback.call(trans.scope||window, null, trans.arg, false);
8203     }
8204 });/*
8205  * Based on:
8206  * Ext JS Library 1.1.1
8207  * Copyright(c) 2006-2007, Ext JS, LLC.
8208  *
8209  * Originally Released Under LGPL - original licence link has changed is not relivant.
8210  *
8211  * Fork - LGPL
8212  * <script type="text/javascript">
8213  */
8214
8215 /**
8216  * @class Roo.data.JsonReader
8217  * @extends Roo.data.DataReader
8218  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8219  * based on mappings in a provided Roo.data.Record constructor.
8220  * 
8221  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8222  * in the reply previously. 
8223  * 
8224  * <p>
8225  * Example code:
8226  * <pre><code>
8227 var RecordDef = Roo.data.Record.create([
8228     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8229     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8230 ]);
8231 var myReader = new Roo.data.JsonReader({
8232     totalProperty: "results",    // The property which contains the total dataset size (optional)
8233     root: "rows",                // The property which contains an Array of row objects
8234     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8235 }, RecordDef);
8236 </code></pre>
8237  * <p>
8238  * This would consume a JSON file like this:
8239  * <pre><code>
8240 { 'results': 2, 'rows': [
8241     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8242     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8243 }
8244 </code></pre>
8245  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8246  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8247  * paged from the remote server.
8248  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8249  * @cfg {String} root name of the property which contains the Array of row objects.
8250  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8251  * @constructor
8252  * Create a new JsonReader
8253  * @param {Object} meta Metadata configuration options
8254  * @param {Object} recordType Either an Array of field definition objects,
8255  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8256  */
8257 Roo.data.JsonReader = function(meta, recordType){
8258     
8259     meta = meta || {};
8260     // set some defaults:
8261     Roo.applyIf(meta, {
8262         totalProperty: 'total',
8263         successProperty : 'success',
8264         root : 'data',
8265         id : 'id'
8266     });
8267     
8268     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8269 };
8270 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8271     
8272     /**
8273      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8274      * Used by Store query builder to append _requestMeta to params.
8275      * 
8276      */
8277     metaFromRemote : false,
8278     /**
8279      * This method is only used by a DataProxy which has retrieved data from a remote server.
8280      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8281      * @return {Object} data A data block which is used by an Roo.data.Store object as
8282      * a cache of Roo.data.Records.
8283      */
8284     read : function(response){
8285         var json = response.responseText;
8286        
8287         var o = /* eval:var:o */ eval("("+json+")");
8288         if(!o) {
8289             throw {message: "JsonReader.read: Json object not found"};
8290         }
8291         
8292         if(o.metaData){
8293             
8294             delete this.ef;
8295             this.metaFromRemote = true;
8296             this.meta = o.metaData;
8297             this.recordType = Roo.data.Record.create(o.metaData.fields);
8298             this.onMetaChange(this.meta, this.recordType, o);
8299         }
8300         return this.readRecords(o);
8301     },
8302
8303     // private function a store will implement
8304     onMetaChange : function(meta, recordType, o){
8305
8306     },
8307
8308     /**
8309          * @ignore
8310          */
8311     simpleAccess: function(obj, subsc) {
8312         return obj[subsc];
8313     },
8314
8315         /**
8316          * @ignore
8317          */
8318     getJsonAccessor: function(){
8319         var re = /[\[\.]/;
8320         return function(expr) {
8321             try {
8322                 return(re.test(expr))
8323                     ? new Function("obj", "return obj." + expr)
8324                     : function(obj){
8325                         return obj[expr];
8326                     };
8327             } catch(e){}
8328             return Roo.emptyFn;
8329         };
8330     }(),
8331
8332     /**
8333      * Create a data block containing Roo.data.Records from an XML document.
8334      * @param {Object} o An object which contains an Array of row objects in the property specified
8335      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8336      * which contains the total size of the dataset.
8337      * @return {Object} data A data block which is used by an Roo.data.Store object as
8338      * a cache of Roo.data.Records.
8339      */
8340     readRecords : function(o){
8341         /**
8342          * After any data loads, the raw JSON data is available for further custom processing.
8343          * @type Object
8344          */
8345         this.o = o;
8346         var s = this.meta, Record = this.recordType,
8347             f = Record.prototype.fields, fi = f.items, fl = f.length;
8348
8349 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8350         if (!this.ef) {
8351             if(s.totalProperty) {
8352                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8353                 }
8354                 if(s.successProperty) {
8355                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8356                 }
8357                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8358                 if (s.id) {
8359                         var g = this.getJsonAccessor(s.id);
8360                         this.getId = function(rec) {
8361                                 var r = g(rec);
8362                                 return (r === undefined || r === "") ? null : r;
8363                         };
8364                 } else {
8365                         this.getId = function(){return null;};
8366                 }
8367             this.ef = [];
8368             for(var jj = 0; jj < fl; jj++){
8369                 f = fi[jj];
8370                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8371                 this.ef[jj] = this.getJsonAccessor(map);
8372             }
8373         }
8374
8375         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8376         if(s.totalProperty){
8377             var vt = parseInt(this.getTotal(o), 10);
8378             if(!isNaN(vt)){
8379                 totalRecords = vt;
8380             }
8381         }
8382         if(s.successProperty){
8383             var vs = this.getSuccess(o);
8384             if(vs === false || vs === 'false'){
8385                 success = false;
8386             }
8387         }
8388         var records = [];
8389             for(var i = 0; i < c; i++){
8390                     var n = root[i];
8391                 var values = {};
8392                 var id = this.getId(n);
8393                 for(var j = 0; j < fl; j++){
8394                     f = fi[j];
8395                 var v = this.ef[j](n);
8396                 if (!f.convert) {
8397                     Roo.log('missing convert for ' + f.name);
8398                     Roo.log(f);
8399                     continue;
8400                 }
8401                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8402                 }
8403                 var record = new Record(values, id);
8404                 record.json = n;
8405                 records[i] = record;
8406             }
8407             return {
8408             raw : o,
8409                 success : success,
8410                 records : records,
8411                 totalRecords : totalRecords
8412             };
8413     }
8414 });/*
8415  * Based on:
8416  * Ext JS Library 1.1.1
8417  * Copyright(c) 2006-2007, Ext JS, LLC.
8418  *
8419  * Originally Released Under LGPL - original licence link has changed is not relivant.
8420  *
8421  * Fork - LGPL
8422  * <script type="text/javascript">
8423  */
8424
8425 /**
8426  * @class Roo.data.ArrayReader
8427  * @extends Roo.data.DataReader
8428  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8429  * Each element of that Array represents a row of data fields. The
8430  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8431  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8432  * <p>
8433  * Example code:.
8434  * <pre><code>
8435 var RecordDef = Roo.data.Record.create([
8436     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8437     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8438 ]);
8439 var myReader = new Roo.data.ArrayReader({
8440     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8441 }, RecordDef);
8442 </code></pre>
8443  * <p>
8444  * This would consume an Array like this:
8445  * <pre><code>
8446 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8447   </code></pre>
8448  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8449  * @constructor
8450  * Create a new JsonReader
8451  * @param {Object} meta Metadata configuration options.
8452  * @param {Object} recordType Either an Array of field definition objects
8453  * as specified to {@link Roo.data.Record#create},
8454  * or an {@link Roo.data.Record} object
8455  * created using {@link Roo.data.Record#create}.
8456  */
8457 Roo.data.ArrayReader = function(meta, recordType){
8458     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8459 };
8460
8461 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8462     /**
8463      * Create a data block containing Roo.data.Records from an XML document.
8464      * @param {Object} o An Array of row objects which represents the dataset.
8465      * @return {Object} data A data block which is used by an Roo.data.Store object as
8466      * a cache of Roo.data.Records.
8467      */
8468     readRecords : function(o){
8469         var sid = this.meta ? this.meta.id : null;
8470         var recordType = this.recordType, fields = recordType.prototype.fields;
8471         var records = [];
8472         var root = o;
8473             for(var i = 0; i < root.length; i++){
8474                     var n = root[i];
8475                 var values = {};
8476                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8477                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8478                 var f = fields.items[j];
8479                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8480                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8481                 v = f.convert(v);
8482                 values[f.name] = v;
8483             }
8484                 var record = new recordType(values, id);
8485                 record.json = n;
8486                 records[records.length] = record;
8487             }
8488             return {
8489                 records : records,
8490                 totalRecords : records.length
8491             };
8492     }
8493 });/*
8494  * - LGPL
8495  * * 
8496  */
8497
8498 /**
8499  * @class Roo.bootstrap.ComboBox
8500  * @extends Roo.bootstrap.TriggerField
8501  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8502  * @cfg {Boolean} append (true|false) default false
8503  * @constructor
8504  * Create a new ComboBox.
8505  * @param {Object} config Configuration options
8506  */
8507 Roo.bootstrap.ComboBox = function(config){
8508     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8509     this.addEvents({
8510         /**
8511          * @event expand
8512          * Fires when the dropdown list is expanded
8513              * @param {Roo.bootstrap.ComboBox} combo This combo box
8514              */
8515         'expand' : true,
8516         /**
8517          * @event collapse
8518          * Fires when the dropdown list is collapsed
8519              * @param {Roo.bootstrap.ComboBox} combo This combo box
8520              */
8521         'collapse' : true,
8522         /**
8523          * @event beforeselect
8524          * Fires before a list item is selected. Return false to cancel the selection.
8525              * @param {Roo.bootstrap.ComboBox} combo This combo box
8526              * @param {Roo.data.Record} record The data record returned from the underlying store
8527              * @param {Number} index The index of the selected item in the dropdown list
8528              */
8529         'beforeselect' : true,
8530         /**
8531          * @event select
8532          * Fires when a list item is selected
8533              * @param {Roo.bootstrap.ComboBox} combo This combo box
8534              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8535              * @param {Number} index The index of the selected item in the dropdown list
8536              */
8537         'select' : true,
8538         /**
8539          * @event beforequery
8540          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8541          * The event object passed has these properties:
8542              * @param {Roo.bootstrap.ComboBox} combo This combo box
8543              * @param {String} query The query
8544              * @param {Boolean} forceAll true to force "all" query
8545              * @param {Boolean} cancel true to cancel the query
8546              * @param {Object} e The query event object
8547              */
8548         'beforequery': true,
8549          /**
8550          * @event add
8551          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8552              * @param {Roo.bootstrap.ComboBox} combo This combo box
8553              */
8554         'add' : true,
8555         /**
8556          * @event edit
8557          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8558              * @param {Roo.bootstrap.ComboBox} combo This combo box
8559              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8560              */
8561         'edit' : true,
8562         /**
8563          * @event remove
8564          * Fires when the remove value from the combobox array
8565              * @param {Roo.bootstrap.ComboBox} combo This combo box
8566              */
8567         'remove' : true
8568         
8569     });
8570     
8571     
8572     this.selectedIndex = -1;
8573     if(this.mode == 'local'){
8574         if(config.queryDelay === undefined){
8575             this.queryDelay = 10;
8576         }
8577         if(config.minChars === undefined){
8578             this.minChars = 0;
8579         }
8580     }
8581 };
8582
8583 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8584      
8585     /**
8586      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8587      * rendering into an Roo.Editor, defaults to false)
8588      */
8589     /**
8590      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8591      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8592      */
8593     /**
8594      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8595      */
8596     /**
8597      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8598      * the dropdown list (defaults to undefined, with no header element)
8599      */
8600
8601      /**
8602      * @cfg {String/Roo.Template} tpl The template to use to render the output
8603      */
8604      
8605      /**
8606      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8607      */
8608     listWidth: undefined,
8609     /**
8610      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8611      * mode = 'remote' or 'text' if mode = 'local')
8612      */
8613     displayField: undefined,
8614     /**
8615      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8616      * mode = 'remote' or 'value' if mode = 'local'). 
8617      * Note: use of a valueField requires the user make a selection
8618      * in order for a value to be mapped.
8619      */
8620     valueField: undefined,
8621     
8622     
8623     /**
8624      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8625      * field's data value (defaults to the underlying DOM element's name)
8626      */
8627     hiddenName: undefined,
8628     /**
8629      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8630      */
8631     listClass: '',
8632     /**
8633      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8634      */
8635     selectedClass: 'active',
8636     
8637     /**
8638      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8639      */
8640     shadow:'sides',
8641     /**
8642      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8643      * anchor positions (defaults to 'tl-bl')
8644      */
8645     listAlign: 'tl-bl?',
8646     /**
8647      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8648      */
8649     maxHeight: 300,
8650     /**
8651      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
8652      * query specified by the allQuery config option (defaults to 'query')
8653      */
8654     triggerAction: 'query',
8655     /**
8656      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8657      * (defaults to 4, does not apply if editable = false)
8658      */
8659     minChars : 4,
8660     /**
8661      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8662      * delay (typeAheadDelay) if it matches a known value (defaults to false)
8663      */
8664     typeAhead: false,
8665     /**
8666      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8667      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8668      */
8669     queryDelay: 500,
8670     /**
8671      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8672      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
8673      */
8674     pageSize: 0,
8675     /**
8676      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
8677      * when editable = true (defaults to false)
8678      */
8679     selectOnFocus:false,
8680     /**
8681      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8682      */
8683     queryParam: 'query',
8684     /**
8685      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
8686      * when mode = 'remote' (defaults to 'Loading...')
8687      */
8688     loadingText: 'Loading...',
8689     /**
8690      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8691      */
8692     resizable: false,
8693     /**
8694      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8695      */
8696     handleHeight : 8,
8697     /**
8698      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8699      * traditional select (defaults to true)
8700      */
8701     editable: true,
8702     /**
8703      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8704      */
8705     allQuery: '',
8706     /**
8707      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8708      */
8709     mode: 'remote',
8710     /**
8711      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8712      * listWidth has a higher value)
8713      */
8714     minListWidth : 70,
8715     /**
8716      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8717      * allow the user to set arbitrary text into the field (defaults to false)
8718      */
8719     forceSelection:false,
8720     /**
8721      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8722      * if typeAhead = true (defaults to 250)
8723      */
8724     typeAheadDelay : 250,
8725     /**
8726      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8727      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8728      */
8729     valueNotFoundText : undefined,
8730     /**
8731      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8732      */
8733     blockFocus : false,
8734     
8735     /**
8736      * @cfg {Boolean} disableClear Disable showing of clear button.
8737      */
8738     disableClear : false,
8739     /**
8740      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8741      */
8742     alwaysQuery : false,
8743     
8744     /**
8745      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8746      */
8747     multiple : false,
8748     
8749     //private
8750     addicon : false,
8751     editicon: false,
8752     
8753     page: 0,
8754     hasQuery: false,
8755     append: false,
8756     loadNext: false,
8757     item: [],
8758     
8759     // element that contains real text value.. (when hidden is used..)
8760      
8761     // private
8762     initEvents: function(){
8763         
8764         if (!this.store) {
8765             throw "can not find store for combo";
8766         }
8767         this.store = Roo.factory(this.store, Roo.data);
8768         
8769         
8770         
8771         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8772         
8773         
8774         if(this.hiddenName){
8775             
8776             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8777             
8778             this.hiddenField.dom.value =
8779                 this.hiddenValue !== undefined ? this.hiddenValue :
8780                 this.value !== undefined ? this.value : '';
8781
8782             // prevent input submission
8783             this.el.dom.removeAttribute('name');
8784             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8785              
8786              
8787         }
8788         //if(Roo.isGecko){
8789         //    this.el.dom.setAttribute('autocomplete', 'off');
8790         //}
8791
8792         var cls = 'x-combo-list';
8793         this.list = this.el.select('ul.dropdown-menu',true).first();
8794
8795         //this.list = new Roo.Layer({
8796         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8797         //});
8798         
8799         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8800         this.list.setWidth(lw);
8801         
8802         this.list.on('mouseover', this.onViewOver, this);
8803         this.list.on('mousemove', this.onViewMove, this);
8804         
8805         this.list.on('scroll', this.onViewScroll, this);
8806         
8807         /*
8808         this.list.swallowEvent('mousewheel');
8809         this.assetHeight = 0;
8810
8811         if(this.title){
8812             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8813             this.assetHeight += this.header.getHeight();
8814         }
8815
8816         this.innerList = this.list.createChild({cls:cls+'-inner'});
8817         this.innerList.on('mouseover', this.onViewOver, this);
8818         this.innerList.on('mousemove', this.onViewMove, this);
8819         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8820         
8821         if(this.allowBlank && !this.pageSize && !this.disableClear){
8822             this.footer = this.list.createChild({cls:cls+'-ft'});
8823             this.pageTb = new Roo.Toolbar(this.footer);
8824            
8825         }
8826         if(this.pageSize){
8827             this.footer = this.list.createChild({cls:cls+'-ft'});
8828             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8829                     {pageSize: this.pageSize});
8830             
8831         }
8832         
8833         if (this.pageTb && this.allowBlank && !this.disableClear) {
8834             var _this = this;
8835             this.pageTb.add(new Roo.Toolbar.Fill(), {
8836                 cls: 'x-btn-icon x-btn-clear',
8837                 text: '&#160;',
8838                 handler: function()
8839                 {
8840                     _this.collapse();
8841                     _this.clearValue();
8842                     _this.onSelect(false, -1);
8843                 }
8844             });
8845         }
8846         if (this.footer) {
8847             this.assetHeight += this.footer.getHeight();
8848         }
8849         */
8850             
8851         if(!this.tpl){
8852             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8853         }
8854
8855         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8856             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8857         });
8858         //this.view.wrapEl.setDisplayed(false);
8859         this.view.on('click', this.onViewClick, this);
8860         
8861         
8862         
8863         this.store.on('beforeload', this.onBeforeLoad, this);
8864         this.store.on('load', this.onLoad, this);
8865         this.store.on('loadexception', this.onLoadException, this);
8866         /*
8867         if(this.resizable){
8868             this.resizer = new Roo.Resizable(this.list,  {
8869                pinned:true, handles:'se'
8870             });
8871             this.resizer.on('resize', function(r, w, h){
8872                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8873                 this.listWidth = w;
8874                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8875                 this.restrictHeight();
8876             }, this);
8877             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8878         }
8879         */
8880         if(!this.editable){
8881             this.editable = true;
8882             this.setEditable(false);
8883         }
8884         
8885         /*
8886         
8887         if (typeof(this.events.add.listeners) != 'undefined') {
8888             
8889             this.addicon = this.wrap.createChild(
8890                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8891        
8892             this.addicon.on('click', function(e) {
8893                 this.fireEvent('add', this);
8894             }, this);
8895         }
8896         if (typeof(this.events.edit.listeners) != 'undefined') {
8897             
8898             this.editicon = this.wrap.createChild(
8899                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8900             if (this.addicon) {
8901                 this.editicon.setStyle('margin-left', '40px');
8902             }
8903             this.editicon.on('click', function(e) {
8904                 
8905                 // we fire even  if inothing is selected..
8906                 this.fireEvent('edit', this, this.lastData );
8907                 
8908             }, this);
8909         }
8910         */
8911         
8912         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8913             "up" : function(e){
8914                 this.inKeyMode = true;
8915                 this.selectPrev();
8916             },
8917
8918             "down" : function(e){
8919                 if(!this.isExpanded()){
8920                     this.onTriggerClick();
8921                 }else{
8922                     this.inKeyMode = true;
8923                     this.selectNext();
8924                 }
8925             },
8926
8927             "enter" : function(e){
8928                 this.onViewClick();
8929                 //return true;
8930             },
8931
8932             "esc" : function(e){
8933                 this.collapse();
8934             },
8935
8936             "tab" : function(e){
8937                 this.collapse();
8938                 
8939                 if(this.fireEvent("specialkey", this, e)){
8940                     this.onViewClick(false);
8941                 }
8942                 
8943                 return true;
8944             },
8945
8946             scope : this,
8947
8948             doRelay : function(foo, bar, hname){
8949                 if(hname == 'down' || this.scope.isExpanded()){
8950                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8951                 }
8952                 return true;
8953             },
8954
8955             forceKeyDown: true
8956         });
8957         
8958         
8959         this.queryDelay = Math.max(this.queryDelay || 10,
8960                 this.mode == 'local' ? 10 : 250);
8961         
8962         
8963         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8964         
8965         if(this.typeAhead){
8966             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8967         }
8968         if(this.editable !== false){
8969             this.inputEl().on("keyup", this.onKeyUp, this);
8970         }
8971         if(this.forceSelection){
8972             this.on('blur', this.doForce, this);
8973         }
8974         
8975         if(this.multiple){
8976             this.choices = this.el.select('ul.select2-choices', true).first();
8977             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8978         }
8979     },
8980
8981     onDestroy : function(){
8982         if(this.view){
8983             this.view.setStore(null);
8984             this.view.el.removeAllListeners();
8985             this.view.el.remove();
8986             this.view.purgeListeners();
8987         }
8988         if(this.list){
8989             this.list.dom.innerHTML  = '';
8990         }
8991         if(this.store){
8992             this.store.un('beforeload', this.onBeforeLoad, this);
8993             this.store.un('load', this.onLoad, this);
8994             this.store.un('loadexception', this.onLoadException, this);
8995         }
8996         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8997     },
8998
8999     // private
9000     fireKey : function(e){
9001         if(e.isNavKeyPress() && !this.list.isVisible()){
9002             this.fireEvent("specialkey", this, e);
9003         }
9004     },
9005
9006     // private
9007     onResize: function(w, h){
9008 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9009 //        
9010 //        if(typeof w != 'number'){
9011 //            // we do not handle it!?!?
9012 //            return;
9013 //        }
9014 //        var tw = this.trigger.getWidth();
9015 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9016 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9017 //        var x = w - tw;
9018 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9019 //            
9020 //        //this.trigger.setStyle('left', x+'px');
9021 //        
9022 //        if(this.list && this.listWidth === undefined){
9023 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9024 //            this.list.setWidth(lw);
9025 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9026 //        }
9027         
9028     
9029         
9030     },
9031
9032     /**
9033      * Allow or prevent the user from directly editing the field text.  If false is passed,
9034      * the user will only be able to select from the items defined in the dropdown list.  This method
9035      * is the runtime equivalent of setting the 'editable' config option at config time.
9036      * @param {Boolean} value True to allow the user to directly edit the field text
9037      */
9038     setEditable : function(value){
9039         if(value == this.editable){
9040             return;
9041         }
9042         this.editable = value;
9043         if(!value){
9044             this.inputEl().dom.setAttribute('readOnly', true);
9045             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9046             this.inputEl().addClass('x-combo-noedit');
9047         }else{
9048             this.inputEl().dom.setAttribute('readOnly', false);
9049             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9050             this.inputEl().removeClass('x-combo-noedit');
9051         }
9052     },
9053
9054     // private
9055     
9056     onBeforeLoad : function(combo,opts){
9057         if(!this.hasFocus){
9058             return;
9059         }
9060          if (!opts.add) {
9061             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9062          }
9063         this.restrictHeight();
9064         this.selectedIndex = -1;
9065     },
9066
9067     // private
9068     onLoad : function(){
9069         
9070         this.hasQuery = false;
9071         
9072         if(!this.hasFocus){
9073             return;
9074         }
9075         
9076         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9077             this.loading.hide();
9078         }
9079         
9080         if(this.store.getCount() > 0){
9081             this.expand();
9082             this.restrictHeight();
9083             if(this.lastQuery == this.allQuery){
9084                 if(this.editable){
9085                     this.inputEl().dom.select();
9086                 }
9087                 if(!this.selectByValue(this.value, true)){
9088                     this.select(0, true);
9089                 }
9090             }else{
9091                 this.selectNext();
9092                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9093                     this.taTask.delay(this.typeAheadDelay);
9094                 }
9095             }
9096         }else{
9097             this.onEmptyResults();
9098         }
9099         
9100         //this.el.focus();
9101     },
9102     // private
9103     onLoadException : function()
9104     {
9105         this.hasQuery = false;
9106         
9107         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9108             this.loading.hide();
9109         }
9110         
9111         this.collapse();
9112         Roo.log(this.store.reader.jsonData);
9113         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9114             // fixme
9115             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9116         }
9117         
9118         
9119     },
9120     // private
9121     onTypeAhead : function(){
9122         if(this.store.getCount() > 0){
9123             var r = this.store.getAt(0);
9124             var newValue = r.data[this.displayField];
9125             var len = newValue.length;
9126             var selStart = this.getRawValue().length;
9127             
9128             if(selStart != len){
9129                 this.setRawValue(newValue);
9130                 this.selectText(selStart, newValue.length);
9131             }
9132         }
9133     },
9134
9135     // private
9136     onSelect : function(record, index){
9137         
9138         if(this.fireEvent('beforeselect', this, record, index) !== false){
9139         
9140             this.setFromData(index > -1 ? record.data : false);
9141             
9142             this.collapse();
9143             this.fireEvent('select', this, record, index);
9144         }
9145     },
9146
9147     /**
9148      * Returns the currently selected field value or empty string if no value is set.
9149      * @return {String} value The selected value
9150      */
9151     getValue : function(){
9152         
9153         if(this.multiple){
9154             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9155         }
9156         
9157         if(this.valueField){
9158             return typeof this.value != 'undefined' ? this.value : '';
9159         }else{
9160             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9161         }
9162     },
9163
9164     /**
9165      * Clears any text/value currently set in the field
9166      */
9167     clearValue : function(){
9168         if(this.hiddenField){
9169             this.hiddenField.dom.value = '';
9170         }
9171         this.value = '';
9172         this.setRawValue('');
9173         this.lastSelectionText = '';
9174         
9175     },
9176
9177     /**
9178      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9179      * will be displayed in the field.  If the value does not match the data value of an existing item,
9180      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9181      * Otherwise the field will be blank (although the value will still be set).
9182      * @param {String} value The value to match
9183      */
9184     setValue : function(v){
9185         if(this.multiple){
9186             this.syncValue();
9187             return;
9188         }
9189         
9190         var text = v;
9191         if(this.valueField){
9192             var r = this.findRecord(this.valueField, v);
9193             if(r){
9194                 text = r.data[this.displayField];
9195             }else if(this.valueNotFoundText !== undefined){
9196                 text = this.valueNotFoundText;
9197             }
9198         }
9199         this.lastSelectionText = text;
9200         if(this.hiddenField){
9201             this.hiddenField.dom.value = v;
9202         }
9203         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9204         this.value = v;
9205     },
9206     /**
9207      * @property {Object} the last set data for the element
9208      */
9209     
9210     lastData : false,
9211     /**
9212      * Sets the value of the field based on a object which is related to the record format for the store.
9213      * @param {Object} value the value to set as. or false on reset?
9214      */
9215     setFromData : function(o){
9216         
9217         if(this.multiple){
9218             this.addItem(o);
9219             return;
9220         }
9221             
9222         var dv = ''; // display value
9223         var vv = ''; // value value..
9224         this.lastData = o;
9225         if (this.displayField) {
9226             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9227         } else {
9228             // this is an error condition!!!
9229             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9230         }
9231         
9232         if(this.valueField){
9233             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9234         }
9235         
9236         if(this.hiddenField){
9237             this.hiddenField.dom.value = vv;
9238             
9239             this.lastSelectionText = dv;
9240             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9241             this.value = vv;
9242             return;
9243         }
9244         // no hidden field.. - we store the value in 'value', but still display
9245         // display field!!!!
9246         this.lastSelectionText = dv;
9247         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9248         this.value = vv;
9249         
9250         
9251     },
9252     // private
9253     reset : function(){
9254         // overridden so that last data is reset..
9255         this.setValue(this.originalValue);
9256         this.clearInvalid();
9257         this.lastData = false;
9258         if (this.view) {
9259             this.view.clearSelections();
9260         }
9261     },
9262     // private
9263     findRecord : function(prop, value){
9264         var record;
9265         if(this.store.getCount() > 0){
9266             this.store.each(function(r){
9267                 if(r.data[prop] == value){
9268                     record = r;
9269                     return false;
9270                 }
9271                 return true;
9272             });
9273         }
9274         return record;
9275     },
9276     
9277     getName: function()
9278     {
9279         // returns hidden if it's set..
9280         if (!this.rendered) {return ''};
9281         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9282         
9283     },
9284     // private
9285     onViewMove : function(e, t){
9286         this.inKeyMode = false;
9287     },
9288
9289     // private
9290     onViewOver : function(e, t){
9291         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9292             return;
9293         }
9294         var item = this.view.findItemFromChild(t);
9295         if(item){
9296             var index = this.view.indexOf(item);
9297             this.select(index, false);
9298         }
9299     },
9300
9301     // private
9302     onViewClick : function(doFocus)
9303     {
9304         var index = this.view.getSelectedIndexes()[0];
9305         var r = this.store.getAt(index);
9306         if(r){
9307             this.onSelect(r, index);
9308         }
9309         if(doFocus !== false && !this.blockFocus){
9310             this.inputEl().focus();
9311         }
9312     },
9313
9314     // private
9315     restrictHeight : function(){
9316         //this.innerList.dom.style.height = '';
9317         //var inner = this.innerList.dom;
9318         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9319         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9320         //this.list.beginUpdate();
9321         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9322         this.list.alignTo(this.inputEl(), this.listAlign);
9323         //this.list.endUpdate();
9324     },
9325
9326     // private
9327     onEmptyResults : function(){
9328         this.collapse();
9329     },
9330
9331     /**
9332      * Returns true if the dropdown list is expanded, else false.
9333      */
9334     isExpanded : function(){
9335         return this.list.isVisible();
9336     },
9337
9338     /**
9339      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9340      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9341      * @param {String} value The data value of the item to select
9342      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9343      * selected item if it is not currently in view (defaults to true)
9344      * @return {Boolean} True if the value matched an item in the list, else false
9345      */
9346     selectByValue : function(v, scrollIntoView){
9347         if(v !== undefined && v !== null){
9348             var r = this.findRecord(this.valueField || this.displayField, v);
9349             if(r){
9350                 this.select(this.store.indexOf(r), scrollIntoView);
9351                 return true;
9352             }
9353         }
9354         return false;
9355     },
9356
9357     /**
9358      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9359      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9360      * @param {Number} index The zero-based index of the list item to select
9361      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9362      * selected item if it is not currently in view (defaults to true)
9363      */
9364     select : function(index, scrollIntoView){
9365         this.selectedIndex = index;
9366         this.view.select(index);
9367         if(scrollIntoView !== false){
9368             var el = this.view.getNode(index);
9369             if(el){
9370                 //this.innerList.scrollChildIntoView(el, false);
9371                 
9372             }
9373         }
9374     },
9375
9376     // private
9377     selectNext : function(){
9378         var ct = this.store.getCount();
9379         if(ct > 0){
9380             if(this.selectedIndex == -1){
9381                 this.select(0);
9382             }else if(this.selectedIndex < ct-1){
9383                 this.select(this.selectedIndex+1);
9384             }
9385         }
9386     },
9387
9388     // private
9389     selectPrev : function(){
9390         var ct = this.store.getCount();
9391         if(ct > 0){
9392             if(this.selectedIndex == -1){
9393                 this.select(0);
9394             }else if(this.selectedIndex != 0){
9395                 this.select(this.selectedIndex-1);
9396             }
9397         }
9398     },
9399
9400     // private
9401     onKeyUp : function(e){
9402         if(this.editable !== false && !e.isSpecialKey()){
9403             this.lastKey = e.getKey();
9404             this.dqTask.delay(this.queryDelay);
9405         }
9406     },
9407
9408     // private
9409     validateBlur : function(){
9410         return !this.list || !this.list.isVisible();   
9411     },
9412
9413     // private
9414     initQuery : function(){
9415         this.doQuery(this.getRawValue());
9416     },
9417
9418     // private
9419     doForce : function(){
9420         if(this.el.dom.value.length > 0){
9421             this.el.dom.value =
9422                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9423              
9424         }
9425     },
9426
9427     /**
9428      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9429      * query allowing the query action to be canceled if needed.
9430      * @param {String} query The SQL query to execute
9431      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9432      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9433      * saved in the current store (defaults to false)
9434      */
9435     doQuery : function(q, forceAll){
9436         
9437         if(q === undefined || q === null){
9438             q = '';
9439         }
9440         var qe = {
9441             query: q,
9442             forceAll: forceAll,
9443             combo: this,
9444             cancel:false
9445         };
9446         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9447             return false;
9448         }
9449         q = qe.query;
9450         
9451         forceAll = qe.forceAll;
9452         if(forceAll === true || (q.length >= this.minChars)){
9453             
9454             this.hasQuery = true;
9455             
9456             if(this.lastQuery != q || this.alwaysQuery){
9457                 this.lastQuery = q;
9458                 if(this.mode == 'local'){
9459                     this.selectedIndex = -1;
9460                     if(forceAll){
9461                         this.store.clearFilter();
9462                     }else{
9463                         this.store.filter(this.displayField, q);
9464                     }
9465                     this.onLoad();
9466                 }else{
9467                     this.store.baseParams[this.queryParam] = q;
9468                     
9469                     var options = {params : this.getParams(q)};
9470                     
9471                     if(this.loadNext){
9472                         options.add = true;
9473                         options.params.start = this.page * this.pageSize;
9474                     }
9475                     
9476                     this.store.load(options);
9477                     this.expand();
9478                 }
9479             }else{
9480                 this.selectedIndex = -1;
9481                 this.onLoad();   
9482             }
9483         }
9484         
9485         this.loadNext = false;
9486     },
9487
9488     // private
9489     getParams : function(q){
9490         var p = {};
9491         //p[this.queryParam] = q;
9492         
9493         if(this.pageSize){
9494             p.start = 0;
9495             p.limit = this.pageSize;
9496         }
9497         return p;
9498     },
9499
9500     /**
9501      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9502      */
9503     collapse : function(){
9504         if(!this.isExpanded()){
9505             return;
9506         }
9507         
9508         this.list.hide();
9509         Roo.get(document).un('mousedown', this.collapseIf, this);
9510         Roo.get(document).un('mousewheel', this.collapseIf, this);
9511         if (!this.editable) {
9512             Roo.get(document).un('keydown', this.listKeyPress, this);
9513         }
9514         this.fireEvent('collapse', this);
9515     },
9516
9517     // private
9518     collapseIf : function(e){
9519         var in_combo  = e.within(this.el);
9520         var in_list =  e.within(this.list);
9521         
9522         if (in_combo || in_list) {
9523             //e.stopPropagation();
9524             return;
9525         }
9526
9527         this.collapse();
9528         
9529     },
9530
9531     /**
9532      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9533      */
9534     expand : function(){
9535        
9536         if(this.isExpanded() || !this.hasFocus){
9537             return;
9538         }
9539          Roo.log('expand');
9540         this.list.alignTo(this.inputEl(), this.listAlign);
9541         this.list.show();
9542         Roo.get(document).on('mousedown', this.collapseIf, this);
9543         Roo.get(document).on('mousewheel', this.collapseIf, this);
9544         if (!this.editable) {
9545             Roo.get(document).on('keydown', this.listKeyPress, this);
9546         }
9547         
9548         this.fireEvent('expand', this);
9549     },
9550
9551     // private
9552     // Implements the default empty TriggerField.onTriggerClick function
9553     onTriggerClick : function()
9554     {
9555         Roo.log('trigger click');
9556         
9557         if(this.disabled){
9558             return;
9559         }
9560         
9561         this.page = 0;
9562         this.loadNext = false;
9563         
9564         if(this.isExpanded()){
9565             this.collapse();
9566             if (!this.blockFocus) {
9567                 this.inputEl().focus();
9568             }
9569             
9570         }else {
9571             this.hasFocus = true;
9572             if(this.triggerAction == 'all') {
9573                 this.doQuery(this.allQuery, true);
9574             } else {
9575                 this.doQuery(this.getRawValue());
9576             }
9577             if (!this.blockFocus) {
9578                 this.inputEl().focus();
9579             }
9580         }
9581     },
9582     listKeyPress : function(e)
9583     {
9584         //Roo.log('listkeypress');
9585         // scroll to first matching element based on key pres..
9586         if (e.isSpecialKey()) {
9587             return false;
9588         }
9589         var k = String.fromCharCode(e.getKey()).toUpperCase();
9590         //Roo.log(k);
9591         var match  = false;
9592         var csel = this.view.getSelectedNodes();
9593         var cselitem = false;
9594         if (csel.length) {
9595             var ix = this.view.indexOf(csel[0]);
9596             cselitem  = this.store.getAt(ix);
9597             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9598                 cselitem = false;
9599             }
9600             
9601         }
9602         
9603         this.store.each(function(v) { 
9604             if (cselitem) {
9605                 // start at existing selection.
9606                 if (cselitem.id == v.id) {
9607                     cselitem = false;
9608                 }
9609                 return true;
9610             }
9611                 
9612             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9613                 match = this.store.indexOf(v);
9614                 return false;
9615             }
9616             return true;
9617         }, this);
9618         
9619         if (match === false) {
9620             return true; // no more action?
9621         }
9622         // scroll to?
9623         this.view.select(match);
9624         var sn = Roo.get(this.view.getSelectedNodes()[0])
9625         //sn.scrollIntoView(sn.dom.parentNode, false);
9626     },
9627     
9628     onViewScroll : function(e, t){
9629         
9630         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9631             return;
9632         }
9633         
9634         this.hasQuery = true;
9635         
9636         this.loading = this.list.select('.loading', true).first();
9637         
9638         if(this.loading === null){
9639             this.list.createChild({
9640                 tag: 'div',
9641                 cls: 'loading select2-more-results select2-active',
9642                 html: 'Loading more results...'
9643             })
9644             
9645             this.loading = this.list.select('.loading', true).first();
9646             
9647             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9648             
9649             this.loading.hide();
9650         }
9651         
9652         this.loading.show();
9653         
9654         var _combo = this;
9655         
9656         this.page++;
9657         this.loadNext = true;
9658         
9659         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9660         
9661         return;
9662     },
9663     
9664     addItem : function(o)
9665     {   
9666         var dv = ''; // display value
9667         
9668         if (this.displayField) {
9669             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9670         } else {
9671             // this is an error condition!!!
9672             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9673         }
9674         
9675         if(!dv.length){
9676             return;
9677         }
9678         
9679         var choice = this.choices.createChild({
9680             tag: 'li',
9681             cls: 'select2-search-choice',
9682             cn: [
9683                 {
9684                     tag: 'div',
9685                     html: dv
9686                 },
9687                 {
9688                     tag: 'a',
9689                     href: '#',
9690                     cls: 'select2-search-choice-close',
9691                     tabindex: '-1'
9692                 }
9693             ]
9694             
9695         }, this.searchField);
9696         
9697         var close = choice.select('a.select2-search-choice-close', true).first()
9698         
9699         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9700         
9701         this.item.push(o);
9702         this.lastData = o;
9703         
9704         this.syncValue();
9705         
9706         this.inputEl().dom.value = '';
9707         
9708     },
9709     
9710     onRemoveItem : function(e, _self, o)
9711     {
9712         Roo.log('remove item');
9713         var index = this.item.indexOf(o.data) * 1;
9714         
9715         if( index < 0){
9716             Roo.log('not this item?!');
9717             return;
9718         }
9719         
9720         this.item.splice(index, 1);
9721         o.item.remove();
9722         
9723         this.syncValue();
9724         
9725         this.fireEvent('remove', this);
9726         
9727     },
9728     
9729     syncValue : function()
9730     {
9731         if(!this.item.length){
9732             this.clearValue();
9733             return;
9734         }
9735             
9736         var value = [];
9737         var _this = this;
9738         Roo.each(this.item, function(i){
9739             if(_this.valueField){
9740                 value.push(i[_this.valueField]);
9741                 return;
9742             }
9743
9744             value.push(i);
9745         });
9746
9747         this.value = value.join(',');
9748
9749         if(this.hiddenField){
9750             this.hiddenField.dom.value = this.value;
9751         }
9752     },
9753     
9754     clearItem : function()
9755     {
9756         if(!this.multiple){
9757             return;
9758         }
9759         
9760         this.item = [];
9761         
9762         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9763            c.remove();
9764         });
9765         
9766         this.syncValue();
9767     }
9768     
9769     
9770
9771     /** 
9772     * @cfg {Boolean} grow 
9773     * @hide 
9774     */
9775     /** 
9776     * @cfg {Number} growMin 
9777     * @hide 
9778     */
9779     /** 
9780     * @cfg {Number} growMax 
9781     * @hide 
9782     */
9783     /**
9784      * @hide
9785      * @method autoSize
9786      */
9787 });
9788 /*
9789  * Based on:
9790  * Ext JS Library 1.1.1
9791  * Copyright(c) 2006-2007, Ext JS, LLC.
9792  *
9793  * Originally Released Under LGPL - original licence link has changed is not relivant.
9794  *
9795  * Fork - LGPL
9796  * <script type="text/javascript">
9797  */
9798
9799 /**
9800  * @class Roo.View
9801  * @extends Roo.util.Observable
9802  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9803  * This class also supports single and multi selection modes. <br>
9804  * Create a data model bound view:
9805  <pre><code>
9806  var store = new Roo.data.Store(...);
9807
9808  var view = new Roo.View({
9809     el : "my-element",
9810     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9811  
9812     singleSelect: true,
9813     selectedClass: "ydataview-selected",
9814     store: store
9815  });
9816
9817  // listen for node click?
9818  view.on("click", function(vw, index, node, e){
9819  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9820  });
9821
9822  // load XML data
9823  dataModel.load("foobar.xml");
9824  </code></pre>
9825  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9826  * <br><br>
9827  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9828  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9829  * 
9830  * Note: old style constructor is still suported (container, template, config)
9831  * 
9832  * @constructor
9833  * Create a new View
9834  * @param {Object} config The config object
9835  * 
9836  */
9837 Roo.View = function(config, depreciated_tpl, depreciated_config){
9838     
9839     if (typeof(depreciated_tpl) == 'undefined') {
9840         // new way.. - universal constructor.
9841         Roo.apply(this, config);
9842         this.el  = Roo.get(this.el);
9843     } else {
9844         // old format..
9845         this.el  = Roo.get(config);
9846         this.tpl = depreciated_tpl;
9847         Roo.apply(this, depreciated_config);
9848     }
9849     this.wrapEl  = this.el.wrap().wrap();
9850     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9851     
9852     
9853     if(typeof(this.tpl) == "string"){
9854         this.tpl = new Roo.Template(this.tpl);
9855     } else {
9856         // support xtype ctors..
9857         this.tpl = new Roo.factory(this.tpl, Roo);
9858     }
9859     
9860     
9861     this.tpl.compile();
9862    
9863   
9864     
9865      
9866     /** @private */
9867     this.addEvents({
9868         /**
9869          * @event beforeclick
9870          * Fires before a click is processed. Returns false to cancel the default action.
9871          * @param {Roo.View} this
9872          * @param {Number} index The index of the target node
9873          * @param {HTMLElement} node The target node
9874          * @param {Roo.EventObject} e The raw event object
9875          */
9876             "beforeclick" : true,
9877         /**
9878          * @event click
9879          * Fires when a template node is clicked.
9880          * @param {Roo.View} this
9881          * @param {Number} index The index of the target node
9882          * @param {HTMLElement} node The target node
9883          * @param {Roo.EventObject} e The raw event object
9884          */
9885             "click" : true,
9886         /**
9887          * @event dblclick
9888          * Fires when a template node is double clicked.
9889          * @param {Roo.View} this
9890          * @param {Number} index The index of the target node
9891          * @param {HTMLElement} node The target node
9892          * @param {Roo.EventObject} e The raw event object
9893          */
9894             "dblclick" : true,
9895         /**
9896          * @event contextmenu
9897          * Fires when a template node is right clicked.
9898          * @param {Roo.View} this
9899          * @param {Number} index The index of the target node
9900          * @param {HTMLElement} node The target node
9901          * @param {Roo.EventObject} e The raw event object
9902          */
9903             "contextmenu" : true,
9904         /**
9905          * @event selectionchange
9906          * Fires when the selected nodes change.
9907          * @param {Roo.View} this
9908          * @param {Array} selections Array of the selected nodes
9909          */
9910             "selectionchange" : true,
9911     
9912         /**
9913          * @event beforeselect
9914          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9915          * @param {Roo.View} this
9916          * @param {HTMLElement} node The node to be selected
9917          * @param {Array} selections Array of currently selected nodes
9918          */
9919             "beforeselect" : true,
9920         /**
9921          * @event preparedata
9922          * Fires on every row to render, to allow you to change the data.
9923          * @param {Roo.View} this
9924          * @param {Object} data to be rendered (change this)
9925          */
9926           "preparedata" : true
9927           
9928           
9929         });
9930
9931
9932
9933     this.el.on({
9934         "click": this.onClick,
9935         "dblclick": this.onDblClick,
9936         "contextmenu": this.onContextMenu,
9937         scope:this
9938     });
9939
9940     this.selections = [];
9941     this.nodes = [];
9942     this.cmp = new Roo.CompositeElementLite([]);
9943     if(this.store){
9944         this.store = Roo.factory(this.store, Roo.data);
9945         this.setStore(this.store, true);
9946     }
9947     
9948     if ( this.footer && this.footer.xtype) {
9949            
9950          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9951         
9952         this.footer.dataSource = this.store
9953         this.footer.container = fctr;
9954         this.footer = Roo.factory(this.footer, Roo);
9955         fctr.insertFirst(this.el);
9956         
9957         // this is a bit insane - as the paging toolbar seems to detach the el..
9958 //        dom.parentNode.parentNode.parentNode
9959          // they get detached?
9960     }
9961     
9962     
9963     Roo.View.superclass.constructor.call(this);
9964     
9965     
9966 };
9967
9968 Roo.extend(Roo.View, Roo.util.Observable, {
9969     
9970      /**
9971      * @cfg {Roo.data.Store} store Data store to load data from.
9972      */
9973     store : false,
9974     
9975     /**
9976      * @cfg {String|Roo.Element} el The container element.
9977      */
9978     el : '',
9979     
9980     /**
9981      * @cfg {String|Roo.Template} tpl The template used by this View 
9982      */
9983     tpl : false,
9984     /**
9985      * @cfg {String} dataName the named area of the template to use as the data area
9986      *                          Works with domtemplates roo-name="name"
9987      */
9988     dataName: false,
9989     /**
9990      * @cfg {String} selectedClass The css class to add to selected nodes
9991      */
9992     selectedClass : "x-view-selected",
9993      /**
9994      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9995      */
9996     emptyText : "",
9997     
9998     /**
9999      * @cfg {String} text to display on mask (default Loading)
10000      */
10001     mask : false,
10002     /**
10003      * @cfg {Boolean} multiSelect Allow multiple selection
10004      */
10005     multiSelect : false,
10006     /**
10007      * @cfg {Boolean} singleSelect Allow single selection
10008      */
10009     singleSelect:  false,
10010     
10011     /**
10012      * @cfg {Boolean} toggleSelect - selecting 
10013      */
10014     toggleSelect : false,
10015     
10016     /**
10017      * Returns the element this view is bound to.
10018      * @return {Roo.Element}
10019      */
10020     getEl : function(){
10021         return this.wrapEl;
10022     },
10023     
10024     
10025
10026     /**
10027      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10028      */
10029     refresh : function(){
10030         Roo.log('refresh');
10031         var t = this.tpl;
10032         
10033         // if we are using something like 'domtemplate', then
10034         // the what gets used is:
10035         // t.applySubtemplate(NAME, data, wrapping data..)
10036         // the outer template then get' applied with
10037         //     the store 'extra data'
10038         // and the body get's added to the
10039         //      roo-name="data" node?
10040         //      <span class='roo-tpl-{name}'></span> ?????
10041         
10042         
10043         
10044         this.clearSelections();
10045         this.el.update("");
10046         var html = [];
10047         var records = this.store.getRange();
10048         if(records.length < 1) {
10049             
10050             // is this valid??  = should it render a template??
10051             
10052             this.el.update(this.emptyText);
10053             return;
10054         }
10055         var el = this.el;
10056         if (this.dataName) {
10057             this.el.update(t.apply(this.store.meta)); //????
10058             el = this.el.child('.roo-tpl-' + this.dataName);
10059         }
10060         
10061         for(var i = 0, len = records.length; i < len; i++){
10062             var data = this.prepareData(records[i].data, i, records[i]);
10063             this.fireEvent("preparedata", this, data, i, records[i]);
10064             html[html.length] = Roo.util.Format.trim(
10065                 this.dataName ?
10066                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10067                     t.apply(data)
10068             );
10069         }
10070         
10071         
10072         
10073         el.update(html.join(""));
10074         this.nodes = el.dom.childNodes;
10075         this.updateIndexes(0);
10076     },
10077     
10078
10079     /**
10080      * Function to override to reformat the data that is sent to
10081      * the template for each node.
10082      * DEPRICATED - use the preparedata event handler.
10083      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10084      * a JSON object for an UpdateManager bound view).
10085      */
10086     prepareData : function(data, index, record)
10087     {
10088         this.fireEvent("preparedata", this, data, index, record);
10089         return data;
10090     },
10091
10092     onUpdate : function(ds, record){
10093          Roo.log('on update');   
10094         this.clearSelections();
10095         var index = this.store.indexOf(record);
10096         var n = this.nodes[index];
10097         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10098         n.parentNode.removeChild(n);
10099         this.updateIndexes(index, index);
10100     },
10101
10102     
10103     
10104 // --------- FIXME     
10105     onAdd : function(ds, records, index)
10106     {
10107         Roo.log(['on Add', ds, records, index] );        
10108         this.clearSelections();
10109         if(this.nodes.length == 0){
10110             this.refresh();
10111             return;
10112         }
10113         var n = this.nodes[index];
10114         for(var i = 0, len = records.length; i < len; i++){
10115             var d = this.prepareData(records[i].data, i, records[i]);
10116             if(n){
10117                 this.tpl.insertBefore(n, d);
10118             }else{
10119                 
10120                 this.tpl.append(this.el, d);
10121             }
10122         }
10123         this.updateIndexes(index);
10124     },
10125
10126     onRemove : function(ds, record, index){
10127         Roo.log('onRemove');
10128         this.clearSelections();
10129         var el = this.dataName  ?
10130             this.el.child('.roo-tpl-' + this.dataName) :
10131             this.el; 
10132         
10133         el.dom.removeChild(this.nodes[index]);
10134         this.updateIndexes(index);
10135     },
10136
10137     /**
10138      * Refresh an individual node.
10139      * @param {Number} index
10140      */
10141     refreshNode : function(index){
10142         this.onUpdate(this.store, this.store.getAt(index));
10143     },
10144
10145     updateIndexes : function(startIndex, endIndex){
10146         var ns = this.nodes;
10147         startIndex = startIndex || 0;
10148         endIndex = endIndex || ns.length - 1;
10149         for(var i = startIndex; i <= endIndex; i++){
10150             ns[i].nodeIndex = i;
10151         }
10152     },
10153
10154     /**
10155      * Changes the data store this view uses and refresh the view.
10156      * @param {Store} store
10157      */
10158     setStore : function(store, initial){
10159         if(!initial && this.store){
10160             this.store.un("datachanged", this.refresh);
10161             this.store.un("add", this.onAdd);
10162             this.store.un("remove", this.onRemove);
10163             this.store.un("update", this.onUpdate);
10164             this.store.un("clear", this.refresh);
10165             this.store.un("beforeload", this.onBeforeLoad);
10166             this.store.un("load", this.onLoad);
10167             this.store.un("loadexception", this.onLoad);
10168         }
10169         if(store){
10170           
10171             store.on("datachanged", this.refresh, this);
10172             store.on("add", this.onAdd, this);
10173             store.on("remove", this.onRemove, this);
10174             store.on("update", this.onUpdate, this);
10175             store.on("clear", this.refresh, this);
10176             store.on("beforeload", this.onBeforeLoad, this);
10177             store.on("load", this.onLoad, this);
10178             store.on("loadexception", this.onLoad, this);
10179         }
10180         
10181         if(store){
10182             this.refresh();
10183         }
10184     },
10185     /**
10186      * onbeforeLoad - masks the loading area.
10187      *
10188      */
10189     onBeforeLoad : function(store,opts)
10190     {
10191          Roo.log('onBeforeLoad');   
10192         if (!opts.add) {
10193             this.el.update("");
10194         }
10195         this.el.mask(this.mask ? this.mask : "Loading" ); 
10196     },
10197     onLoad : function ()
10198     {
10199         this.el.unmask();
10200     },
10201     
10202
10203     /**
10204      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10205      * @param {HTMLElement} node
10206      * @return {HTMLElement} The template node
10207      */
10208     findItemFromChild : function(node){
10209         var el = this.dataName  ?
10210             this.el.child('.roo-tpl-' + this.dataName,true) :
10211             this.el.dom; 
10212         
10213         if(!node || node.parentNode == el){
10214                     return node;
10215             }
10216             var p = node.parentNode;
10217             while(p && p != el){
10218             if(p.parentNode == el){
10219                 return p;
10220             }
10221             p = p.parentNode;
10222         }
10223             return null;
10224     },
10225
10226     /** @ignore */
10227     onClick : function(e){
10228         var item = this.findItemFromChild(e.getTarget());
10229         if(item){
10230             var index = this.indexOf(item);
10231             if(this.onItemClick(item, index, e) !== false){
10232                 this.fireEvent("click", this, index, item, e);
10233             }
10234         }else{
10235             this.clearSelections();
10236         }
10237     },
10238
10239     /** @ignore */
10240     onContextMenu : function(e){
10241         var item = this.findItemFromChild(e.getTarget());
10242         if(item){
10243             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10244         }
10245     },
10246
10247     /** @ignore */
10248     onDblClick : function(e){
10249         var item = this.findItemFromChild(e.getTarget());
10250         if(item){
10251             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10252         }
10253     },
10254
10255     onItemClick : function(item, index, e)
10256     {
10257         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10258             return false;
10259         }
10260         if (this.toggleSelect) {
10261             var m = this.isSelected(item) ? 'unselect' : 'select';
10262             Roo.log(m);
10263             var _t = this;
10264             _t[m](item, true, false);
10265             return true;
10266         }
10267         if(this.multiSelect || this.singleSelect){
10268             if(this.multiSelect && e.shiftKey && this.lastSelection){
10269                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10270             }else{
10271                 this.select(item, this.multiSelect && e.ctrlKey);
10272                 this.lastSelection = item;
10273             }
10274             e.preventDefault();
10275         }
10276         return true;
10277     },
10278
10279     /**
10280      * Get the number of selected nodes.
10281      * @return {Number}
10282      */
10283     getSelectionCount : function(){
10284         return this.selections.length;
10285     },
10286
10287     /**
10288      * Get the currently selected nodes.
10289      * @return {Array} An array of HTMLElements
10290      */
10291     getSelectedNodes : function(){
10292         return this.selections;
10293     },
10294
10295     /**
10296      * Get the indexes of the selected nodes.
10297      * @return {Array}
10298      */
10299     getSelectedIndexes : function(){
10300         var indexes = [], s = this.selections;
10301         for(var i = 0, len = s.length; i < len; i++){
10302             indexes.push(s[i].nodeIndex);
10303         }
10304         return indexes;
10305     },
10306
10307     /**
10308      * Clear all selections
10309      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10310      */
10311     clearSelections : function(suppressEvent){
10312         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10313             this.cmp.elements = this.selections;
10314             this.cmp.removeClass(this.selectedClass);
10315             this.selections = [];
10316             if(!suppressEvent){
10317                 this.fireEvent("selectionchange", this, this.selections);
10318             }
10319         }
10320     },
10321
10322     /**
10323      * Returns true if the passed node is selected
10324      * @param {HTMLElement/Number} node The node or node index
10325      * @return {Boolean}
10326      */
10327     isSelected : function(node){
10328         var s = this.selections;
10329         if(s.length < 1){
10330             return false;
10331         }
10332         node = this.getNode(node);
10333         return s.indexOf(node) !== -1;
10334     },
10335
10336     /**
10337      * Selects nodes.
10338      * @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
10339      * @param {Boolean} keepExisting (optional) true to keep existing selections
10340      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10341      */
10342     select : function(nodeInfo, keepExisting, suppressEvent){
10343         if(nodeInfo instanceof Array){
10344             if(!keepExisting){
10345                 this.clearSelections(true);
10346             }
10347             for(var i = 0, len = nodeInfo.length; i < len; i++){
10348                 this.select(nodeInfo[i], true, true);
10349             }
10350             return;
10351         } 
10352         var node = this.getNode(nodeInfo);
10353         if(!node || this.isSelected(node)){
10354             return; // already selected.
10355         }
10356         if(!keepExisting){
10357             this.clearSelections(true);
10358         }
10359         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10360             Roo.fly(node).addClass(this.selectedClass);
10361             this.selections.push(node);
10362             if(!suppressEvent){
10363                 this.fireEvent("selectionchange", this, this.selections);
10364             }
10365         }
10366         
10367         
10368     },
10369       /**
10370      * Unselects nodes.
10371      * @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
10372      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10373      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10374      */
10375     unselect : function(nodeInfo, keepExisting, suppressEvent)
10376     {
10377         if(nodeInfo instanceof Array){
10378             Roo.each(this.selections, function(s) {
10379                 this.unselect(s, nodeInfo);
10380             }, this);
10381             return;
10382         }
10383         var node = this.getNode(nodeInfo);
10384         if(!node || !this.isSelected(node)){
10385             Roo.log("not selected");
10386             return; // not selected.
10387         }
10388         // fireevent???
10389         var ns = [];
10390         Roo.each(this.selections, function(s) {
10391             if (s == node ) {
10392                 Roo.fly(node).removeClass(this.selectedClass);
10393
10394                 return;
10395             }
10396             ns.push(s);
10397         },this);
10398         
10399         this.selections= ns;
10400         this.fireEvent("selectionchange", this, this.selections);
10401     },
10402
10403     /**
10404      * Gets a template node.
10405      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10406      * @return {HTMLElement} The node or null if it wasn't found
10407      */
10408     getNode : function(nodeInfo){
10409         if(typeof nodeInfo == "string"){
10410             return document.getElementById(nodeInfo);
10411         }else if(typeof nodeInfo == "number"){
10412             return this.nodes[nodeInfo];
10413         }
10414         return nodeInfo;
10415     },
10416
10417     /**
10418      * Gets a range template nodes.
10419      * @param {Number} startIndex
10420      * @param {Number} endIndex
10421      * @return {Array} An array of nodes
10422      */
10423     getNodes : function(start, end){
10424         var ns = this.nodes;
10425         start = start || 0;
10426         end = typeof end == "undefined" ? ns.length - 1 : end;
10427         var nodes = [];
10428         if(start <= end){
10429             for(var i = start; i <= end; i++){
10430                 nodes.push(ns[i]);
10431             }
10432         } else{
10433             for(var i = start; i >= end; i--){
10434                 nodes.push(ns[i]);
10435             }
10436         }
10437         return nodes;
10438     },
10439
10440     /**
10441      * Finds the index of the passed node
10442      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10443      * @return {Number} The index of the node or -1
10444      */
10445     indexOf : function(node){
10446         node = this.getNode(node);
10447         if(typeof node.nodeIndex == "number"){
10448             return node.nodeIndex;
10449         }
10450         var ns = this.nodes;
10451         for(var i = 0, len = ns.length; i < len; i++){
10452             if(ns[i] == node){
10453                 return i;
10454             }
10455         }
10456         return -1;
10457     }
10458 });
10459 /*
10460  * - LGPL
10461  *
10462  * based on jquery fullcalendar
10463  * 
10464  */
10465
10466 Roo.bootstrap = Roo.bootstrap || {};
10467 /**
10468  * @class Roo.bootstrap.Calendar
10469  * @extends Roo.bootstrap.Component
10470  * Bootstrap Calendar class
10471  * @cfg {Boolean} loadMask (true|false) default false
10472  * @cfg {Object} header generate the user specific header of the calendar, default false
10473
10474  * @constructor
10475  * Create a new Container
10476  * @param {Object} config The config object
10477  */
10478
10479
10480
10481 Roo.bootstrap.Calendar = function(config){
10482     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10483      this.addEvents({
10484         /**
10485              * @event select
10486              * Fires when a date is selected
10487              * @param {DatePicker} this
10488              * @param {Date} date The selected date
10489              */
10490         'select': true,
10491         /**
10492              * @event monthchange
10493              * Fires when the displayed month changes 
10494              * @param {DatePicker} this
10495              * @param {Date} date The selected month
10496              */
10497         'monthchange': true,
10498         /**
10499              * @event evententer
10500              * Fires when mouse over an event
10501              * @param {Calendar} this
10502              * @param {event} Event
10503              */
10504         'evententer': true,
10505         /**
10506              * @event eventleave
10507              * Fires when the mouse leaves an
10508              * @param {Calendar} this
10509              * @param {event}
10510              */
10511         'eventleave': true,
10512         /**
10513              * @event eventclick
10514              * Fires when the mouse click an
10515              * @param {Calendar} this
10516              * @param {event}
10517              */
10518         'eventclick': true
10519         
10520     });
10521
10522 };
10523
10524 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10525     
10526      /**
10527      * @cfg {Number} startDay
10528      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10529      */
10530     startDay : 0,
10531     
10532     loadMask : false,
10533     
10534     header : false,
10535       
10536     getAutoCreate : function(){
10537         
10538         
10539         var fc_button = function(name, corner, style, content ) {
10540             return Roo.apply({},{
10541                 tag : 'span',
10542                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10543                          (corner.length ?
10544                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10545                             ''
10546                         ),
10547                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10548                 unselectable: 'on'
10549             });
10550         };
10551         
10552         var header = {};
10553         
10554         if(!this.header){
10555             header = {
10556                 tag : 'table',
10557                 cls : 'fc-header',
10558                 style : 'width:100%',
10559                 cn : [
10560                     {
10561                         tag: 'tr',
10562                         cn : [
10563                             {
10564                                 tag : 'td',
10565                                 cls : 'fc-header-left',
10566                                 cn : [
10567                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
10568                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
10569                                     { tag: 'span', cls: 'fc-header-space' },
10570                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
10571
10572
10573                                 ]
10574                             },
10575
10576                             {
10577                                 tag : 'td',
10578                                 cls : 'fc-header-center',
10579                                 cn : [
10580                                     {
10581                                         tag: 'span',
10582                                         cls: 'fc-header-title',
10583                                         cn : {
10584                                             tag: 'H2',
10585                                             html : 'month / year'
10586                                         }
10587                                     }
10588
10589                                 ]
10590                             },
10591                             {
10592                                 tag : 'td',
10593                                 cls : 'fc-header-right',
10594                                 cn : [
10595                               /*      fc_button('month', 'left', '', 'month' ),
10596                                     fc_button('week', '', '', 'week' ),
10597                                     fc_button('day', 'right', '', 'day' )
10598                                 */    
10599
10600                                 ]
10601                             }
10602
10603                         ]
10604                     }
10605                 ]
10606             };
10607         }
10608         
10609         header = this.header;
10610         
10611        
10612         var cal_heads = function() {
10613             var ret = [];
10614             // fixme - handle this.
10615             
10616             for (var i =0; i < Date.dayNames.length; i++) {
10617                 var d = Date.dayNames[i];
10618                 ret.push({
10619                     tag: 'th',
10620                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10621                     html : d.substring(0,3)
10622                 });
10623                 
10624             }
10625             ret[0].cls += ' fc-first';
10626             ret[6].cls += ' fc-last';
10627             return ret;
10628         };
10629         var cal_cell = function(n) {
10630             return  {
10631                 tag: 'td',
10632                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10633                 cn : [
10634                     {
10635                         cn : [
10636                             {
10637                                 cls: 'fc-day-number',
10638                                 html: 'D'
10639                             },
10640                             {
10641                                 cls: 'fc-day-content',
10642                              
10643                                 cn : [
10644                                      {
10645                                         style: 'position: relative;' // height: 17px;
10646                                     }
10647                                 ]
10648                             }
10649                             
10650                             
10651                         ]
10652                     }
10653                 ]
10654                 
10655             }
10656         };
10657         var cal_rows = function() {
10658             
10659             var ret = []
10660             for (var r = 0; r < 6; r++) {
10661                 var row= {
10662                     tag : 'tr',
10663                     cls : 'fc-week',
10664                     cn : []
10665                 };
10666                 
10667                 for (var i =0; i < Date.dayNames.length; i++) {
10668                     var d = Date.dayNames[i];
10669                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10670
10671                 }
10672                 row.cn[0].cls+=' fc-first';
10673                 row.cn[0].cn[0].style = 'min-height:90px';
10674                 row.cn[6].cls+=' fc-last';
10675                 ret.push(row);
10676                 
10677             }
10678             ret[0].cls += ' fc-first';
10679             ret[4].cls += ' fc-prev-last';
10680             ret[5].cls += ' fc-last';
10681             return ret;
10682             
10683         };
10684         
10685         var cal_table = {
10686             tag: 'table',
10687             cls: 'fc-border-separate',
10688             style : 'width:100%',
10689             cellspacing  : 0,
10690             cn : [
10691                 { 
10692                     tag: 'thead',
10693                     cn : [
10694                         { 
10695                             tag: 'tr',
10696                             cls : 'fc-first fc-last',
10697                             cn : cal_heads()
10698                         }
10699                     ]
10700                 },
10701                 { 
10702                     tag: 'tbody',
10703                     cn : cal_rows()
10704                 }
10705                   
10706             ]
10707         };
10708          
10709          var cfg = {
10710             cls : 'fc fc-ltr',
10711             cn : [
10712                 header,
10713                 {
10714                     cls : 'fc-content',
10715                     style : "position: relative;",
10716                     cn : [
10717                         {
10718                             cls : 'fc-view fc-view-month fc-grid',
10719                             style : 'position: relative',
10720                             unselectable : 'on',
10721                             cn : [
10722                                 {
10723                                     cls : 'fc-event-container',
10724                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10725                                 },
10726                                 cal_table
10727                             ]
10728                         }
10729                     ]
10730     
10731                 }
10732            ] 
10733             
10734         };
10735         
10736          
10737         
10738         return cfg;
10739     },
10740     
10741     
10742     initEvents : function()
10743     {
10744         if(!this.store){
10745             throw "can not find store for calendar";
10746         }
10747         
10748         var mark = {
10749             tag: "div",
10750             cls:"x-dlg-mask",
10751             style: "text-align:center",
10752             cn: [
10753                 {
10754                     tag: "div",
10755                     style: "background-color:white;width:50%;margin:250 auto",
10756                     cn: [
10757                         {
10758                             tag: "img",
10759                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10760                         },
10761                         {
10762                             tag: "span",
10763                             html: "Loading"
10764                         }
10765                         
10766                     ]
10767                 }
10768             ]
10769         }
10770         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10771         
10772         var size = this.el.select('.fc-content', true).first().getSize();
10773         this.maskEl.setSize(size.width, size.height);
10774         this.maskEl.enableDisplayMode("block");
10775         if(!this.loadMask){
10776             this.maskEl.hide();
10777         }
10778         
10779         this.store = Roo.factory(this.store, Roo.data);
10780         this.store.on('load', this.onLoad, this);
10781         this.store.on('beforeload', this.onBeforeLoad, this);
10782         
10783         this.resize();
10784         
10785         this.cells = this.el.select('.fc-day',true);
10786         //Roo.log(this.cells);
10787         this.textNodes = this.el.query('.fc-day-number');
10788         this.cells.addClassOnOver('fc-state-hover');
10789         
10790         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10791         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10792         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10793         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10794         
10795         this.on('monthchange', this.onMonthChange, this);
10796         
10797         this.update(new Date().clearTime());
10798     },
10799     
10800     resize : function() {
10801         var sz  = this.el.getSize();
10802         
10803         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10804         this.el.select('.fc-day-content div',true).setHeight(34);
10805     },
10806     
10807     
10808     // private
10809     showPrevMonth : function(e){
10810         this.update(this.activeDate.add("mo", -1));
10811     },
10812     showToday : function(e){
10813         this.update(new Date().clearTime());
10814     },
10815     // private
10816     showNextMonth : function(e){
10817         this.update(this.activeDate.add("mo", 1));
10818     },
10819
10820     // private
10821     showPrevYear : function(){
10822         this.update(this.activeDate.add("y", -1));
10823     },
10824
10825     // private
10826     showNextYear : function(){
10827         this.update(this.activeDate.add("y", 1));
10828     },
10829
10830     
10831    // private
10832     update : function(date)
10833     {
10834         var vd = this.activeDate;
10835         this.activeDate = date;
10836 //        if(vd && this.el){
10837 //            var t = date.getTime();
10838 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10839 //                Roo.log('using add remove');
10840 //                
10841 //                this.fireEvent('monthchange', this, date);
10842 //                
10843 //                this.cells.removeClass("fc-state-highlight");
10844 //                this.cells.each(function(c){
10845 //                   if(c.dateValue == t){
10846 //                       c.addClass("fc-state-highlight");
10847 //                       setTimeout(function(){
10848 //                            try{c.dom.firstChild.focus();}catch(e){}
10849 //                       }, 50);
10850 //                       return false;
10851 //                   }
10852 //                   return true;
10853 //                });
10854 //                return;
10855 //            }
10856 //        }
10857         
10858         var days = date.getDaysInMonth();
10859         
10860         var firstOfMonth = date.getFirstDateOfMonth();
10861         var startingPos = firstOfMonth.getDay()-this.startDay;
10862         
10863         if(startingPos < this.startDay){
10864             startingPos += 7;
10865         }
10866         
10867         var pm = date.add(Date.MONTH, -1);
10868         var prevStart = pm.getDaysInMonth()-startingPos;
10869 //        
10870         this.cells = this.el.select('.fc-day',true);
10871         this.textNodes = this.el.query('.fc-day-number');
10872         this.cells.addClassOnOver('fc-state-hover');
10873         
10874         var cells = this.cells.elements;
10875         var textEls = this.textNodes;
10876         
10877         Roo.each(cells, function(cell){
10878             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10879         });
10880         
10881         days += startingPos;
10882
10883         // convert everything to numbers so it's fast
10884         var day = 86400000;
10885         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10886         //Roo.log(d);
10887         //Roo.log(pm);
10888         //Roo.log(prevStart);
10889         
10890         var today = new Date().clearTime().getTime();
10891         var sel = date.clearTime().getTime();
10892         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10893         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10894         var ddMatch = this.disabledDatesRE;
10895         var ddText = this.disabledDatesText;
10896         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10897         var ddaysText = this.disabledDaysText;
10898         var format = this.format;
10899         
10900         var setCellClass = function(cal, cell){
10901             
10902             //Roo.log('set Cell Class');
10903             cell.title = "";
10904             var t = d.getTime();
10905             
10906             //Roo.log(d);
10907             
10908             cell.dateValue = t;
10909             if(t == today){
10910                 cell.className += " fc-today";
10911                 cell.className += " fc-state-highlight";
10912                 cell.title = cal.todayText;
10913             }
10914             if(t == sel){
10915                 // disable highlight in other month..
10916                 //cell.className += " fc-state-highlight";
10917                 
10918             }
10919             // disabling
10920             if(t < min) {
10921                 cell.className = " fc-state-disabled";
10922                 cell.title = cal.minText;
10923                 return;
10924             }
10925             if(t > max) {
10926                 cell.className = " fc-state-disabled";
10927                 cell.title = cal.maxText;
10928                 return;
10929             }
10930             if(ddays){
10931                 if(ddays.indexOf(d.getDay()) != -1){
10932                     cell.title = ddaysText;
10933                     cell.className = " fc-state-disabled";
10934                 }
10935             }
10936             if(ddMatch && format){
10937                 var fvalue = d.dateFormat(format);
10938                 if(ddMatch.test(fvalue)){
10939                     cell.title = ddText.replace("%0", fvalue);
10940                     cell.className = " fc-state-disabled";
10941                 }
10942             }
10943             
10944             if (!cell.initialClassName) {
10945                 cell.initialClassName = cell.dom.className;
10946             }
10947             
10948             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10949         };
10950
10951         var i = 0;
10952         
10953         for(; i < startingPos; i++) {
10954             textEls[i].innerHTML = (++prevStart);
10955             d.setDate(d.getDate()+1);
10956             
10957             cells[i].className = "fc-past fc-other-month";
10958             setCellClass(this, cells[i]);
10959         }
10960         
10961         var intDay = 0;
10962         
10963         for(; i < days; i++){
10964             intDay = i - startingPos + 1;
10965             textEls[i].innerHTML = (intDay);
10966             d.setDate(d.getDate()+1);
10967             
10968             cells[i].className = ''; // "x-date-active";
10969             setCellClass(this, cells[i]);
10970         }
10971         var extraDays = 0;
10972         
10973         for(; i < 42; i++) {
10974             textEls[i].innerHTML = (++extraDays);
10975             d.setDate(d.getDate()+1);
10976             
10977             cells[i].className = "fc-future fc-other-month";
10978             setCellClass(this, cells[i]);
10979         }
10980         
10981         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10982         
10983         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10984         
10985         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10986         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10987         
10988         if(totalRows != 6){
10989             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10990             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10991         }
10992         
10993         this.fireEvent('monthchange', this, date);
10994         
10995         
10996         /*
10997         if(!this.internalRender){
10998             var main = this.el.dom.firstChild;
10999             var w = main.offsetWidth;
11000             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11001             Roo.fly(main).setWidth(w);
11002             this.internalRender = true;
11003             // opera does not respect the auto grow header center column
11004             // then, after it gets a width opera refuses to recalculate
11005             // without a second pass
11006             if(Roo.isOpera && !this.secondPass){
11007                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11008                 this.secondPass = true;
11009                 this.update.defer(10, this, [date]);
11010             }
11011         }
11012         */
11013         
11014     },
11015     
11016     findCell : function(dt) {
11017         dt = dt.clearTime().getTime();
11018         var ret = false;
11019         this.cells.each(function(c){
11020             //Roo.log("check " +c.dateValue + '?=' + dt);
11021             if(c.dateValue == dt){
11022                 ret = c;
11023                 return false;
11024             }
11025             return true;
11026         });
11027         
11028         return ret;
11029     },
11030     
11031     findCells : function(ev) {
11032         var s = ev.start.clone().clearTime().getTime();
11033        // Roo.log(s);
11034         var e= ev.end.clone().clearTime().getTime();
11035        // Roo.log(e);
11036         var ret = [];
11037         this.cells.each(function(c){
11038              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11039             
11040             if(c.dateValue > e){
11041                 return ;
11042             }
11043             if(c.dateValue < s){
11044                 return ;
11045             }
11046             ret.push(c);
11047         });
11048         
11049         return ret;    
11050     },
11051     
11052     findBestRow: function(cells)
11053     {
11054         var ret = 0;
11055         
11056         for (var i =0 ; i < cells.length;i++) {
11057             ret  = Math.max(cells[i].rows || 0,ret);
11058         }
11059         return ret;
11060         
11061     },
11062     
11063     
11064     addItem : function(ev)
11065     {
11066         // look for vertical location slot in
11067         var cells = this.findCells(ev);
11068         
11069         ev.row = this.findBestRow(cells);
11070         
11071         // work out the location.
11072         
11073         var crow = false;
11074         var rows = [];
11075         for(var i =0; i < cells.length; i++) {
11076             if (!crow) {
11077                 crow = {
11078                     start : cells[i],
11079                     end :  cells[i]
11080                 };
11081                 continue;
11082             }
11083             if (crow.start.getY() == cells[i].getY()) {
11084                 // on same row.
11085                 crow.end = cells[i];
11086                 continue;
11087             }
11088             // different row.
11089             rows.push(crow);
11090             crow = {
11091                 start: cells[i],
11092                 end : cells[i]
11093             };
11094             
11095         }
11096         
11097         rows.push(crow);
11098         ev.els = [];
11099         ev.rows = rows;
11100         ev.cells = cells;
11101         for (var i = 0; i < cells.length;i++) {
11102             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11103             
11104         }
11105         
11106         this.calevents.push(ev);
11107     },
11108     
11109     clearEvents: function() {
11110         
11111         if(!this.calevents){
11112             return;
11113         }
11114         
11115         Roo.each(this.cells.elements, function(c){
11116             c.rows = 0;
11117         });
11118         
11119         Roo.each(this.calevents, function(e) {
11120             Roo.each(e.els, function(el) {
11121                 el.un('mouseenter' ,this.onEventEnter, this);
11122                 el.un('mouseleave' ,this.onEventLeave, this);
11123                 el.remove();
11124             },this);
11125         },this);
11126         
11127     },
11128     
11129     renderEvents: function()
11130     {   
11131         // first make sure there is enough space..
11132         
11133         this.cells.each(function(c) {
11134         
11135             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11136         });
11137         
11138         for (var e = 0; e < this.calevents.length; e++) {
11139             var ev = this.calevents[e];
11140             var cells = ev.cells;
11141             var rows = ev.rows;
11142             
11143             for(var i =0; i < rows.length; i++) {
11144                 
11145                  
11146                 // how many rows should it span..
11147                 
11148                 var  cfg = {
11149                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11150                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11151                     
11152                     unselectable : "on",
11153                     cn : [
11154                         {
11155                             cls: 'fc-event-inner',
11156                             cn : [
11157 //                                {
11158 //                                  tag:'span',
11159 //                                  cls: 'fc-event-time',
11160 //                                  html : cells.length > 1 ? '' : ev.time
11161 //                                },
11162                                 {
11163                                   tag:'span',
11164                                   cls: 'fc-event-title',
11165                                   html : String.format('{0}', ev.title)
11166                                 }
11167                                 
11168                                 
11169                             ]
11170                         },
11171                         {
11172                             cls: 'ui-resizable-handle ui-resizable-e',
11173                             html : '&nbsp;&nbsp;&nbsp'
11174                         }
11175                         
11176                     ]
11177                 };
11178                 if (i == 0) {
11179                     cfg.cls += ' fc-event-start';
11180                 }
11181                 if ((i+1) == rows.length) {
11182                     cfg.cls += ' fc-event-end';
11183                 }
11184                 
11185                 var ctr = this.el.select('.fc-event-container',true).first();
11186                 var cg = ctr.createChild(cfg);
11187                 
11188                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11189                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11190                 cg.on('click', this.onEventClick, this, ev);
11191                 
11192                 ev.els.push(cg);
11193                 
11194                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11195                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11196                 //Roo.log(cg);
11197                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11198                 cg.setWidth(ebox.right - sbox.x -2);
11199             }
11200             
11201             
11202         }
11203         
11204     },
11205     
11206     onEventEnter: function (e, el,event,d) {
11207         this.fireEvent('evententer', this, el, event);
11208     },
11209     
11210     onEventLeave: function (e, el,event,d) {
11211         this.fireEvent('eventleave', this, el, event);
11212     },
11213     
11214     onEventClick: function (e, el,event,d) {
11215         this.fireEvent('eventclick', this, el, event);
11216     },
11217     
11218     onMonthChange: function () {
11219         this.store.load();
11220     },
11221     
11222     onLoad: function () 
11223     {   
11224         this.calevents = [];
11225         var cal = this;
11226         
11227         if(this.store.getCount() > 0){
11228             this.store.data.each(function(d){
11229                cal.addItem({
11230                     id : d.data.id,
11231                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11232                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11233                     time : d.data.start_time,
11234                     title : d.data.title,
11235                     description : d.data.description,
11236                     venue : d.data.venue
11237                 });
11238             });
11239         }
11240         
11241         this.renderEvents();
11242         
11243         if(this.loadMask){
11244             this.maskEl.hide();
11245         }
11246     },
11247     
11248     onBeforeLoad: function()
11249     {
11250         this.clearEvents();
11251         
11252         if(this.loadMask){
11253             this.maskEl.show();
11254         }
11255     }
11256 });
11257
11258  
11259  /*
11260  * - LGPL
11261  *
11262  * element
11263  * 
11264  */
11265
11266 /**
11267  * @class Roo.bootstrap.Popover
11268  * @extends Roo.bootstrap.Component
11269  * Bootstrap Popover class
11270  * @cfg {String} html contents of the popover   (or false to use children..)
11271  * @cfg {String} title of popover (or false to hide)
11272  * @cfg {String} placement how it is placed
11273  * @cfg {String} trigger click || hover (or false to trigger manually)
11274  * @cfg {String} over what (parent or false to trigger manually.)
11275  * 
11276  * @constructor
11277  * Create a new Popover
11278  * @param {Object} config The config object
11279  */
11280
11281 Roo.bootstrap.Popover = function(config){
11282     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11283 };
11284
11285 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11286     
11287     title: 'Fill in a title',
11288     html: false,
11289     
11290     placement : 'right',
11291     trigger : 'hover', // hover
11292     
11293     over: 'parent',
11294     
11295     can_build_overlaid : false,
11296     
11297     getChildContainer : function()
11298     {
11299         return this.el.select('.popover-content',true).first();
11300     },
11301     
11302     getAutoCreate : function(){
11303          Roo.log('make popover?');
11304         var cfg = {
11305            cls : 'popover roo-dynamic',
11306            style: 'display:block',
11307            cn : [
11308                 {
11309                     cls : 'arrow'
11310                 },
11311                 {
11312                     cls : 'popover-inner',
11313                     cn : [
11314                         {
11315                             tag: 'h3',
11316                             cls: 'popover-title',
11317                             html : this.title
11318                         },
11319                         {
11320                             cls : 'popover-content',
11321                             html : this.html
11322                         }
11323                     ]
11324                     
11325                 }
11326            ]
11327         };
11328         
11329         return cfg;
11330     },
11331     setTitle: function(str)
11332     {
11333         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11334     },
11335     setContent: function(str)
11336     {
11337         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11338     },
11339     // as it get's added to the bottom of the page.
11340     onRender : function(ct, position)
11341     {
11342         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11343         if(!this.el){
11344             var cfg = Roo.apply({},  this.getAutoCreate());
11345             cfg.id = Roo.id();
11346             
11347             if (this.cls) {
11348                 cfg.cls += ' ' + this.cls;
11349             }
11350             if (this.style) {
11351                 cfg.style = this.style;
11352             }
11353             Roo.log("adding to ")
11354             this.el = Roo.get(document.body).createChild(cfg, position);
11355             Roo.log(this.el);
11356         }
11357         this.initEvents();
11358     },
11359     
11360     initEvents : function()
11361     {
11362         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11363         this.el.enableDisplayMode('block');
11364         this.el.hide();
11365         if (this.over === false) {
11366             return; 
11367         }
11368         if (this.triggers === false) {
11369             return;
11370         }
11371         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11372         var triggers = this.trigger ? this.trigger.split(' ') : [];
11373         Roo.each(triggers, function(trigger) {
11374         
11375             if (trigger == 'click') {
11376                 on_el.on('click', this.toggle, this);
11377             } else if (trigger != 'manual') {
11378                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11379                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11380       
11381                 on_el.on(eventIn  ,this.enter, this);
11382                 on_el.on(eventOut, this.leave, this);
11383             }
11384         }, this);
11385         
11386     },
11387     
11388     
11389     // private
11390     timeout : null,
11391     hoverState : null,
11392     
11393     toggle : function () {
11394         this.hoverState == 'in' ? this.leave() : this.enter();
11395     },
11396     
11397     enter : function () {
11398        
11399     
11400         clearTimeout(this.timeout);
11401     
11402         this.hoverState = 'in'
11403     
11404         if (!this.delay || !this.delay.show) {
11405             this.show();
11406             return 
11407         }
11408         var _t = this;
11409         this.timeout = setTimeout(function () {
11410             if (_t.hoverState == 'in') {
11411                 _t.show();
11412             }
11413         }, this.delay.show)
11414     },
11415     leave : function() {
11416         clearTimeout(this.timeout);
11417     
11418         this.hoverState = 'out'
11419     
11420         if (!this.delay || !this.delay.hide) {
11421             this.hide();
11422             return 
11423         }
11424         var _t = this;
11425         this.timeout = setTimeout(function () {
11426             if (_t.hoverState == 'out') {
11427                 _t.hide();
11428             }
11429         }, this.delay.hide)
11430     },
11431     
11432     show : function (on_el)
11433     {
11434         if (!on_el) {
11435             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11436         }
11437         // set content.
11438         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11439         if (this.html !== false) {
11440             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11441         }
11442         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11443         if (!this.title.length) {
11444             this.el.select('.popover-title',true).hide();
11445         }
11446         
11447         var placement = typeof this.placement == 'function' ?
11448             this.placement.call(this, this.el, on_el) :
11449             this.placement;
11450             
11451         var autoToken = /\s?auto?\s?/i;
11452         var autoPlace = autoToken.test(placement);
11453         if (autoPlace) {
11454             placement = placement.replace(autoToken, '') || 'top';
11455         }
11456         
11457         //this.el.detach()
11458         //this.el.setXY([0,0]);
11459         this.el.show();
11460         this.el.dom.style.display='block';
11461         this.el.addClass(placement);
11462         
11463         //this.el.appendTo(on_el);
11464         
11465         var p = this.getPosition();
11466         var box = this.el.getBox();
11467         
11468         if (autoPlace) {
11469             // fixme..
11470         }
11471         var align = Roo.bootstrap.Popover.alignment[placement]
11472         this.el.alignTo(on_el, align[0],align[1]);
11473         //var arrow = this.el.select('.arrow',true).first();
11474         //arrow.set(align[2], 
11475         
11476         this.el.addClass('in');
11477         this.hoverState = null;
11478         
11479         if (this.el.hasClass('fade')) {
11480             // fade it?
11481         }
11482         
11483     },
11484     hide : function()
11485     {
11486         this.el.setXY([0,0]);
11487         this.el.removeClass('in');
11488         this.el.hide();
11489         
11490     }
11491     
11492 });
11493
11494 Roo.bootstrap.Popover.alignment = {
11495     'left' : ['r-l', [-10,0], 'right'],
11496     'right' : ['l-r', [10,0], 'left'],
11497     'bottom' : ['t-b', [0,10], 'top'],
11498     'top' : [ 'b-t', [0,-10], 'bottom']
11499 };
11500
11501  /*
11502  * - LGPL
11503  *
11504  * Progress
11505  * 
11506  */
11507
11508 /**
11509  * @class Roo.bootstrap.Progress
11510  * @extends Roo.bootstrap.Component
11511  * Bootstrap Progress class
11512  * @cfg {Boolean} striped striped of the progress bar
11513  * @cfg {Boolean} active animated of the progress bar
11514  * 
11515  * 
11516  * @constructor
11517  * Create a new Progress
11518  * @param {Object} config The config object
11519  */
11520
11521 Roo.bootstrap.Progress = function(config){
11522     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11523 };
11524
11525 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
11526     
11527     striped : false,
11528     active: false,
11529     
11530     getAutoCreate : function(){
11531         var cfg = {
11532             tag: 'div',
11533             cls: 'progress'
11534         };
11535         
11536         
11537         if(this.striped){
11538             cfg.cls += ' progress-striped';
11539         }
11540       
11541         if(this.active){
11542             cfg.cls += ' active';
11543         }
11544         
11545         
11546         return cfg;
11547     }
11548    
11549 });
11550
11551  
11552
11553  /*
11554  * - LGPL
11555  *
11556  * ProgressBar
11557  * 
11558  */
11559
11560 /**
11561  * @class Roo.bootstrap.ProgressBar
11562  * @extends Roo.bootstrap.Component
11563  * Bootstrap ProgressBar class
11564  * @cfg {Number} aria_valuenow aria-value now
11565  * @cfg {Number} aria_valuemin aria-value min
11566  * @cfg {Number} aria_valuemax aria-value max
11567  * @cfg {String} label label for the progress bar
11568  * @cfg {String} panel (success | info | warning | danger )
11569  * @cfg {String} role role of the progress bar
11570  * @cfg {String} sr_only text
11571  * 
11572  * 
11573  * @constructor
11574  * Create a new ProgressBar
11575  * @param {Object} config The config object
11576  */
11577
11578 Roo.bootstrap.ProgressBar = function(config){
11579     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11580 };
11581
11582 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
11583     
11584     aria_valuenow : 0,
11585     aria_valuemin : 0,
11586     aria_valuemax : 100,
11587     label : false,
11588     panel : false,
11589     role : false,
11590     sr_only: false,
11591     
11592     getAutoCreate : function()
11593     {
11594         
11595         var cfg = {
11596             tag: 'div',
11597             cls: 'progress-bar',
11598             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11599         };
11600         
11601         if(this.sr_only){
11602             cfg.cn = {
11603                 tag: 'span',
11604                 cls: 'sr-only',
11605                 html: this.sr_only
11606             }
11607         }
11608         
11609         if(this.role){
11610             cfg.role = this.role;
11611         }
11612         
11613         if(this.aria_valuenow){
11614             cfg['aria-valuenow'] = this.aria_valuenow;
11615         }
11616         
11617         if(this.aria_valuemin){
11618             cfg['aria-valuemin'] = this.aria_valuemin;
11619         }
11620         
11621         if(this.aria_valuemax){
11622             cfg['aria-valuemax'] = this.aria_valuemax;
11623         }
11624         
11625         if(this.label && !this.sr_only){
11626             cfg.html = this.label;
11627         }
11628         
11629         if(this.panel){
11630             cfg.cls += ' progress-bar-' + this.panel;
11631         }
11632         
11633         return cfg;
11634     },
11635     
11636     update : function(aria_valuenow)
11637     {
11638         this.aria_valuenow = aria_valuenow;
11639         
11640         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11641     }
11642    
11643 });
11644
11645  
11646
11647  /*
11648  * - LGPL
11649  *
11650  * TabPanel
11651  * 
11652  */
11653
11654 /**
11655  * @class Roo.bootstrap.TabPanel
11656  * @extends Roo.bootstrap.Component
11657  * Bootstrap TabPanel class
11658  * @cfg {Boolean} active panel active
11659  * @cfg {String} html panel content
11660  * @cfg {String} tabId tab relate id
11661  * @cfg {String} navId The navbar which triggers show hide
11662  * 
11663  * 
11664  * @constructor
11665  * Create a new TabPanel
11666  * @param {Object} config The config object
11667  */
11668
11669 Roo.bootstrap.TabPanel = function(config){
11670     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11671      this.addEvents({
11672         /**
11673              * @event changed
11674              * Fires when the active status changes
11675              * @param {Roo.bootstrap.TabPanel} this
11676              * @param {Boolean} state the new state
11677             
11678          */
11679         'changed': true
11680      });
11681 };
11682
11683 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11684     
11685     active: false,
11686     html: false,
11687     tabId: false,
11688     navId : false,
11689     
11690     getAutoCreate : function(){
11691         var cfg = {
11692             tag: 'div',
11693             cls: 'tab-pane',
11694             html: this.html || ''
11695         };
11696         
11697         if(this.active){
11698             cfg.cls += ' active';
11699         }
11700         
11701         if(this.tabId){
11702             cfg.tabId = this.tabId;
11703         }
11704         
11705         return cfg;
11706     },
11707     onRender : function(ct, position)
11708     {
11709        // Roo.log("Call onRender: " + this.xtype);
11710         
11711         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11712         
11713         if (this.navId && this.tabId) {
11714             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11715             if (!item) {
11716                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11717             } else {
11718                 item.on('changed', function(item, state) {
11719                     this.setActive(state);
11720                 }, this);
11721             }
11722         }
11723         
11724     },
11725     setActive: function(state)
11726     {
11727         Roo.log("panel - set active " + this.tabId + "=" + state);
11728         
11729         this.active = state;
11730         if (!state) {
11731             this.el.removeClass('active');
11732             
11733         } else  if (!this.el.hasClass('active')) {
11734             this.el.addClass('active');
11735         }
11736         this.fireEvent('changed', this, state);
11737     }
11738     
11739     
11740 });
11741  
11742
11743  
11744
11745  /*
11746  * - LGPL
11747  *
11748  * DateField
11749  * 
11750  */
11751
11752 /**
11753  * @class Roo.bootstrap.DateField
11754  * @extends Roo.bootstrap.Input
11755  * Bootstrap DateField class
11756  * @cfg {Number} weekStart default 0
11757  * @cfg {Number} weekStart default 0
11758  * @cfg {Number} viewMode default empty, (months|years)
11759  * @cfg {Number} minViewMode default empty, (months|years)
11760  * @cfg {Number} startDate default -Infinity
11761  * @cfg {Number} endDate default Infinity
11762  * @cfg {Boolean} todayHighlight default false
11763  * @cfg {Boolean} todayBtn default false
11764  * @cfg {Boolean} calendarWeeks default false
11765  * @cfg {Object} daysOfWeekDisabled default empty
11766  * 
11767  * @cfg {Boolean} keyboardNavigation default true
11768  * @cfg {String} language default en
11769  * 
11770  * @constructor
11771  * Create a new DateField
11772  * @param {Object} config The config object
11773  */
11774
11775 Roo.bootstrap.DateField = function(config){
11776     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11777      this.addEvents({
11778             /**
11779              * @event show
11780              * Fires when this field show.
11781              * @param {Roo.bootstrap.DateField} this
11782              * @param {Mixed} date The date value
11783              */
11784             show : true,
11785             /**
11786              * @event show
11787              * Fires when this field hide.
11788              * @param {Roo.bootstrap.DateField} this
11789              * @param {Mixed} date The date value
11790              */
11791             hide : true,
11792             /**
11793              * @event select
11794              * Fires when select a date.
11795              * @param {Roo.bootstrap.DateField} this
11796              * @param {Mixed} date The date value
11797              */
11798             select : true
11799         });
11800 };
11801
11802 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11803     
11804     /**
11805      * @cfg {String} format
11806      * The default date format string which can be overriden for localization support.  The format must be
11807      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11808      */
11809     format : "m/d/y",
11810     /**
11811      * @cfg {String} altFormats
11812      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11813      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11814      */
11815     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11816     
11817     weekStart : 0,
11818     
11819     viewMode : '',
11820     
11821     minViewMode : '',
11822     
11823     todayHighlight : false,
11824     
11825     todayBtn: false,
11826     
11827     language: 'en',
11828     
11829     keyboardNavigation: true,
11830     
11831     calendarWeeks: false,
11832     
11833     startDate: -Infinity,
11834     
11835     endDate: Infinity,
11836     
11837     daysOfWeekDisabled: [],
11838     
11839     _events: [],
11840     
11841     UTCDate: function()
11842     {
11843         return new Date(Date.UTC.apply(Date, arguments));
11844     },
11845     
11846     UTCToday: function()
11847     {
11848         var today = new Date();
11849         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11850     },
11851     
11852     getDate: function() {
11853             var d = this.getUTCDate();
11854             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11855     },
11856     
11857     getUTCDate: function() {
11858             return this.date;
11859     },
11860     
11861     setDate: function(d) {
11862             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11863     },
11864     
11865     setUTCDate: function(d) {
11866             this.date = d;
11867             this.setValue(this.formatDate(this.date));
11868     },
11869         
11870     onRender: function(ct, position)
11871     {
11872         
11873         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11874         
11875         this.language = this.language || 'en';
11876         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11877         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11878         
11879         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11880         this.format = this.format || 'm/d/y';
11881         this.isInline = false;
11882         this.isInput = true;
11883         this.component = this.el.select('.add-on', true).first() || false;
11884         this.component = (this.component && this.component.length === 0) ? false : this.component;
11885         this.hasInput = this.component && this.inputEL().length;
11886         
11887         if (typeof(this.minViewMode === 'string')) {
11888             switch (this.minViewMode) {
11889                 case 'months':
11890                     this.minViewMode = 1;
11891                     break;
11892                 case 'years':
11893                     this.minViewMode = 2;
11894                     break;
11895                 default:
11896                     this.minViewMode = 0;
11897                     break;
11898             }
11899         }
11900         
11901         if (typeof(this.viewMode === 'string')) {
11902             switch (this.viewMode) {
11903                 case 'months':
11904                     this.viewMode = 1;
11905                     break;
11906                 case 'years':
11907                     this.viewMode = 2;
11908                     break;
11909                 default:
11910                     this.viewMode = 0;
11911                     break;
11912             }
11913         }
11914                 
11915         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11916         
11917         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11918         
11919         this.picker().on('mousedown', this.onMousedown, this);
11920         this.picker().on('click', this.onClick, this);
11921         
11922         this.picker().addClass('datepicker-dropdown');
11923         
11924         this.startViewMode = this.viewMode;
11925         
11926         
11927         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11928             if(!this.calendarWeeks){
11929                 v.remove();
11930                 return;
11931             };
11932             
11933             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11934             v.attr('colspan', function(i, val){
11935                 return parseInt(val) + 1;
11936             });
11937         })
11938                         
11939         
11940         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11941         
11942         this.setStartDate(this.startDate);
11943         this.setEndDate(this.endDate);
11944         
11945         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11946         
11947         this.fillDow();
11948         this.fillMonths();
11949         this.update();
11950         this.showMode();
11951         
11952         if(this.isInline) {
11953             this.show();
11954         }
11955     },
11956     
11957     picker : function()
11958     {
11959         return this.el.select('.datepicker', true).first();
11960     },
11961     
11962     fillDow: function()
11963     {
11964         var dowCnt = this.weekStart;
11965         
11966         var dow = {
11967             tag: 'tr',
11968             cn: [
11969                 
11970             ]
11971         };
11972         
11973         if(this.calendarWeeks){
11974             dow.cn.push({
11975                 tag: 'th',
11976                 cls: 'cw',
11977                 html: '&nbsp;'
11978             })
11979         }
11980         
11981         while (dowCnt < this.weekStart + 7) {
11982             dow.cn.push({
11983                 tag: 'th',
11984                 cls: 'dow',
11985                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11986             });
11987         }
11988         
11989         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11990     },
11991     
11992     fillMonths: function()
11993     {    
11994         var i = 0
11995         var months = this.picker().select('>.datepicker-months td', true).first();
11996         
11997         months.dom.innerHTML = '';
11998         
11999         while (i < 12) {
12000             var month = {
12001                 tag: 'span',
12002                 cls: 'month',
12003                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12004             }
12005             
12006             months.createChild(month);
12007         }
12008         
12009     },
12010     
12011     update: function(){
12012         
12013         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12014         
12015         if (this.date < this.startDate) {
12016             this.viewDate = new Date(this.startDate);
12017         } else if (this.date > this.endDate) {
12018             this.viewDate = new Date(this.endDate);
12019         } else {
12020             this.viewDate = new Date(this.date);
12021         }
12022         
12023         this.fill();
12024     },
12025     
12026     fill: function() {
12027         var d = new Date(this.viewDate),
12028                 year = d.getUTCFullYear(),
12029                 month = d.getUTCMonth(),
12030                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12031                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12032                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12033                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12034                 currentDate = this.date && this.date.valueOf(),
12035                 today = this.UTCToday();
12036         
12037         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12038         
12039 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12040         
12041 //        this.picker.select('>tfoot th.today').
12042 //                                              .text(dates[this.language].today)
12043 //                                              .toggle(this.todayBtn !== false);
12044     
12045         this.updateNavArrows();
12046         this.fillMonths();
12047                                                 
12048         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12049         
12050         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12051          
12052         prevMonth.setUTCDate(day);
12053         
12054         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12055         
12056         var nextMonth = new Date(prevMonth);
12057         
12058         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12059         
12060         nextMonth = nextMonth.valueOf();
12061         
12062         var fillMonths = false;
12063         
12064         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12065         
12066         while(prevMonth.valueOf() < nextMonth) {
12067             var clsName = '';
12068             
12069             if (prevMonth.getUTCDay() === this.weekStart) {
12070                 if(fillMonths){
12071                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12072                 }
12073                     
12074                 fillMonths = {
12075                     tag: 'tr',
12076                     cn: []
12077                 };
12078                 
12079                 if(this.calendarWeeks){
12080                     // ISO 8601: First week contains first thursday.
12081                     // ISO also states week starts on Monday, but we can be more abstract here.
12082                     var
12083                     // Start of current week: based on weekstart/current date
12084                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12085                     // Thursday of this week
12086                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12087                     // First Thursday of year, year from thursday
12088                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12089                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12090                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12091                     
12092                     fillMonths.cn.push({
12093                         tag: 'td',
12094                         cls: 'cw',
12095                         html: calWeek
12096                     });
12097                 }
12098             }
12099             
12100             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12101                 clsName += ' old';
12102             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12103                 clsName += ' new';
12104             }
12105             if (this.todayHighlight &&
12106                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12107                 prevMonth.getUTCMonth() == today.getMonth() &&
12108                 prevMonth.getUTCDate() == today.getDate()) {
12109                 clsName += ' today';
12110             }
12111             
12112             if (currentDate && prevMonth.valueOf() === currentDate) {
12113                 clsName += ' active';
12114             }
12115             
12116             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12117                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12118                     clsName += ' disabled';
12119             }
12120             
12121             fillMonths.cn.push({
12122                 tag: 'td',
12123                 cls: 'day ' + clsName,
12124                 html: prevMonth.getDate()
12125             })
12126             
12127             prevMonth.setDate(prevMonth.getDate()+1);
12128         }
12129           
12130         var currentYear = this.date && this.date.getUTCFullYear();
12131         var currentMonth = this.date && this.date.getUTCMonth();
12132         
12133         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12134         
12135         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12136             v.removeClass('active');
12137             
12138             if(currentYear === year && k === currentMonth){
12139                 v.addClass('active');
12140             }
12141             
12142             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12143                 v.addClass('disabled');
12144             }
12145             
12146         });
12147         
12148         
12149         year = parseInt(year/10, 10) * 10;
12150         
12151         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12152         
12153         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12154         
12155         year -= 1;
12156         for (var i = -1; i < 11; i++) {
12157             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12158                 tag: 'span',
12159                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12160                 html: year
12161             })
12162             
12163             year += 1;
12164         }
12165     },
12166     
12167     showMode: function(dir) {
12168         if (dir) {
12169             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12170         }
12171         Roo.each(this.picker().select('>div',true).elements, function(v){
12172             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12173             v.hide();
12174         });
12175         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12176     },
12177     
12178     place: function()
12179     {
12180         if(this.isInline) return;
12181         
12182         this.picker().removeClass(['bottom', 'top']);
12183         
12184         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12185             /*
12186              * place to the top of element!
12187              *
12188              */
12189             
12190             this.picker().addClass('top');
12191             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12192             
12193             return;
12194         }
12195         
12196         this.picker().addClass('bottom');
12197         
12198         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12199     },
12200     
12201     parseDate : function(value){
12202         if(!value || value instanceof Date){
12203             return value;
12204         }
12205         var v = Date.parseDate(value, this.format);
12206         if (!v && this.useIso) {
12207             v = Date.parseDate(value, 'Y-m-d');
12208         }
12209         if(!v && this.altFormats){
12210             if(!this.altFormatsArray){
12211                 this.altFormatsArray = this.altFormats.split("|");
12212             }
12213             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12214                 v = Date.parseDate(value, this.altFormatsArray[i]);
12215             }
12216         }
12217         return v;
12218     },
12219     
12220     formatDate : function(date, fmt){
12221         return (!date || !(date instanceof Date)) ?
12222         date : date.dateFormat(fmt || this.format);
12223     },
12224     
12225     onFocus : function()
12226     {
12227         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12228         this.show();
12229     },
12230     
12231     onBlur : function()
12232     {
12233         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12234         this.hide();
12235     },
12236     
12237     show : function()
12238     {
12239         this.picker().show();
12240         this.update();
12241         this.place();
12242         
12243         this.fireEvent('show', this, this.date);
12244     },
12245     
12246     hide : function()
12247     {
12248         if(this.isInline) return;
12249         this.picker().hide();
12250         this.viewMode = this.startViewMode;
12251         this.showMode();
12252         
12253         this.fireEvent('hide', this, this.date);
12254         
12255     },
12256     
12257     onMousedown: function(e){
12258         e.stopPropagation();
12259         e.preventDefault();
12260     },
12261     
12262     keyup: function(e){
12263         Roo.bootstrap.DateField.superclass.keyup.call(this);
12264         this.update();
12265         
12266     },
12267
12268     setValue: function(v){
12269         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12270         
12271         this.fireEvent('select', this, this.date);
12272         
12273     },
12274     
12275     fireKey: function(e){
12276         if (!this.picker().isVisible()){
12277             if (e.keyCode == 27) // allow escape to hide and re-show picker
12278                 this.show();
12279             return;
12280         }
12281         var dateChanged = false,
12282         dir, day, month,
12283         newDate, newViewDate;
12284         switch(e.keyCode){
12285             case 27: // escape
12286                 this.hide();
12287                 e.preventDefault();
12288                 break;
12289             case 37: // left
12290             case 39: // right
12291                 if (!this.keyboardNavigation) break;
12292                 dir = e.keyCode == 37 ? -1 : 1;
12293                 
12294                 if (e.ctrlKey){
12295                     newDate = this.moveYear(this.date, dir);
12296                     newViewDate = this.moveYear(this.viewDate, dir);
12297                 } else if (e.shiftKey){
12298                     newDate = this.moveMonth(this.date, dir);
12299                     newViewDate = this.moveMonth(this.viewDate, dir);
12300                 } else {
12301                     newDate = new Date(this.date);
12302                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12303                     newViewDate = new Date(this.viewDate);
12304                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12305                 }
12306                 if (this.dateWithinRange(newDate)){
12307                     this.date = newDate;
12308                     this.viewDate = newViewDate;
12309                     this.setValue(this.formatDate(this.date));
12310                     this.update();
12311                     e.preventDefault();
12312                     dateChanged = true;
12313                 }
12314                 break;
12315             case 38: // up
12316             case 40: // down
12317                 if (!this.keyboardNavigation) break;
12318                 dir = e.keyCode == 38 ? -1 : 1;
12319                 if (e.ctrlKey){
12320                     newDate = this.moveYear(this.date, dir);
12321                     newViewDate = this.moveYear(this.viewDate, dir);
12322                 } else if (e.shiftKey){
12323                     newDate = this.moveMonth(this.date, dir);
12324                     newViewDate = this.moveMonth(this.viewDate, dir);
12325                 } else {
12326                     newDate = new Date(this.date);
12327                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12328                     newViewDate = new Date(this.viewDate);
12329                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12330                 }
12331                 if (this.dateWithinRange(newDate)){
12332                     this.date = newDate;
12333                     this.viewDate = newViewDate;
12334                     this.setValue(this.formatDate(this.date));
12335                     this.update();
12336                     e.preventDefault();
12337                     dateChanged = true;
12338                 }
12339                 break;
12340             case 13: // enter
12341                 this.setValue(this.formatDate(this.date));
12342                 this.hide();
12343                 e.preventDefault();
12344                 break;
12345             case 9: // tab
12346                 this.setValue(this.formatDate(this.date));
12347                 this.hide();
12348                 break;
12349         }
12350     },
12351     
12352     
12353     onClick: function(e) {
12354         e.stopPropagation();
12355         e.preventDefault();
12356         
12357         var target = e.getTarget();
12358         
12359         if(target.nodeName.toLowerCase() === 'i'){
12360             target = Roo.get(target).dom.parentNode;
12361         }
12362         
12363         var nodeName = target.nodeName;
12364         var className = target.className;
12365         var html = target.innerHTML;
12366         
12367         switch(nodeName.toLowerCase()) {
12368             case 'th':
12369                 switch(className) {
12370                     case 'switch':
12371                         this.showMode(1);
12372                         break;
12373                     case 'prev':
12374                     case 'next':
12375                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12376                         switch(this.viewMode){
12377                                 case 0:
12378                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12379                                         break;
12380                                 case 1:
12381                                 case 2:
12382                                         this.viewDate = this.moveYear(this.viewDate, dir);
12383                                         break;
12384                         }
12385                         this.fill();
12386                         break;
12387                     case 'today':
12388                         var date = new Date();
12389                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12390                         this.fill()
12391                         this.setValue(this.formatDate(this.date));
12392                         this.hide();
12393                         break;
12394                 }
12395                 break;
12396             case 'span':
12397                 if (className.indexOf('disabled') === -1) {
12398                     this.viewDate.setUTCDate(1);
12399                     if (className.indexOf('month') !== -1) {
12400                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12401                     } else {
12402                         var year = parseInt(html, 10) || 0;
12403                         this.viewDate.setUTCFullYear(year);
12404                         
12405                     }
12406                     this.showMode(-1);
12407                     this.fill();
12408                 }
12409                 break;
12410                 
12411             case 'td':
12412                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12413                     var day = parseInt(html, 10) || 1;
12414                     var year = this.viewDate.getUTCFullYear(),
12415                         month = this.viewDate.getUTCMonth();
12416
12417                     if (className.indexOf('old') !== -1) {
12418                         if(month === 0 ){
12419                             month = 11;
12420                             year -= 1;
12421                         }else{
12422                             month -= 1;
12423                         }
12424                     } else if (className.indexOf('new') !== -1) {
12425                         if (month == 11) {
12426                             month = 0;
12427                             year += 1;
12428                         } else {
12429                             month += 1;
12430                         }
12431                     }
12432                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12433                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12434                     this.fill();
12435                     this.setValue(this.formatDate(this.date));
12436                     this.hide();
12437                 }
12438                 break;
12439         }
12440     },
12441     
12442     setStartDate: function(startDate){
12443         this.startDate = startDate || -Infinity;
12444         if (this.startDate !== -Infinity) {
12445             this.startDate = this.parseDate(this.startDate);
12446         }
12447         this.update();
12448         this.updateNavArrows();
12449     },
12450
12451     setEndDate: function(endDate){
12452         this.endDate = endDate || Infinity;
12453         if (this.endDate !== Infinity) {
12454             this.endDate = this.parseDate(this.endDate);
12455         }
12456         this.update();
12457         this.updateNavArrows();
12458     },
12459     
12460     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12461         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12462         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12463             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12464         }
12465         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12466             return parseInt(d, 10);
12467         });
12468         this.update();
12469         this.updateNavArrows();
12470     },
12471     
12472     updateNavArrows: function() {
12473         var d = new Date(this.viewDate),
12474         year = d.getUTCFullYear(),
12475         month = d.getUTCMonth();
12476         
12477         Roo.each(this.picker().select('.prev', true).elements, function(v){
12478             v.show();
12479             switch (this.viewMode) {
12480                 case 0:
12481
12482                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12483                         v.hide();
12484                     }
12485                     break;
12486                 case 1:
12487                 case 2:
12488                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12489                         v.hide();
12490                     }
12491                     break;
12492             }
12493         });
12494         
12495         Roo.each(this.picker().select('.next', true).elements, function(v){
12496             v.show();
12497             switch (this.viewMode) {
12498                 case 0:
12499
12500                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12501                         v.hide();
12502                     }
12503                     break;
12504                 case 1:
12505                 case 2:
12506                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12507                         v.hide();
12508                     }
12509                     break;
12510             }
12511         })
12512     },
12513     
12514     moveMonth: function(date, dir){
12515         if (!dir) return date;
12516         var new_date = new Date(date.valueOf()),
12517         day = new_date.getUTCDate(),
12518         month = new_date.getUTCMonth(),
12519         mag = Math.abs(dir),
12520         new_month, test;
12521         dir = dir > 0 ? 1 : -1;
12522         if (mag == 1){
12523             test = dir == -1
12524             // If going back one month, make sure month is not current month
12525             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12526             ? function(){
12527                 return new_date.getUTCMonth() == month;
12528             }
12529             // If going forward one month, make sure month is as expected
12530             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12531             : function(){
12532                 return new_date.getUTCMonth() != new_month;
12533             };
12534             new_month = month + dir;
12535             new_date.setUTCMonth(new_month);
12536             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12537             if (new_month < 0 || new_month > 11)
12538                 new_month = (new_month + 12) % 12;
12539         } else {
12540             // For magnitudes >1, move one month at a time...
12541             for (var i=0; i<mag; i++)
12542                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12543                 new_date = this.moveMonth(new_date, dir);
12544             // ...then reset the day, keeping it in the new month
12545             new_month = new_date.getUTCMonth();
12546             new_date.setUTCDate(day);
12547             test = function(){
12548                 return new_month != new_date.getUTCMonth();
12549             };
12550         }
12551         // Common date-resetting loop -- if date is beyond end of month, make it
12552         // end of month
12553         while (test()){
12554             new_date.setUTCDate(--day);
12555             new_date.setUTCMonth(new_month);
12556         }
12557         return new_date;
12558     },
12559
12560     moveYear: function(date, dir){
12561         return this.moveMonth(date, dir*12);
12562     },
12563
12564     dateWithinRange: function(date){
12565         return date >= this.startDate && date <= this.endDate;
12566     },
12567
12568     
12569     remove: function() {
12570         this.picker().remove();
12571     }
12572    
12573 });
12574
12575 Roo.apply(Roo.bootstrap.DateField,  {
12576     
12577     head : {
12578         tag: 'thead',
12579         cn: [
12580         {
12581             tag: 'tr',
12582             cn: [
12583             {
12584                 tag: 'th',
12585                 cls: 'prev',
12586                 html: '<i class="icon-arrow-left"/>'
12587             },
12588             {
12589                 tag: 'th',
12590                 cls: 'switch',
12591                 colspan: '5'
12592             },
12593             {
12594                 tag: 'th',
12595                 cls: 'next',
12596                 html: '<i class="icon-arrow-right"/>'
12597             }
12598
12599             ]
12600         }
12601         ]
12602     },
12603     
12604     content : {
12605         tag: 'tbody',
12606         cn: [
12607         {
12608             tag: 'tr',
12609             cn: [
12610             {
12611                 tag: 'td',
12612                 colspan: '7'
12613             }
12614             ]
12615         }
12616         ]
12617     },
12618     
12619     footer : {
12620         tag: 'tfoot',
12621         cn: [
12622         {
12623             tag: 'tr',
12624             cn: [
12625             {
12626                 tag: 'th',
12627                 colspan: '7',
12628                 cls: 'today'
12629             }
12630                     
12631             ]
12632         }
12633         ]
12634     },
12635     
12636     dates:{
12637         en: {
12638             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12639             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12640             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12641             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12642             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12643             today: "Today"
12644         }
12645     },
12646     
12647     modes: [
12648     {
12649         clsName: 'days',
12650         navFnc: 'Month',
12651         navStep: 1
12652     },
12653     {
12654         clsName: 'months',
12655         navFnc: 'FullYear',
12656         navStep: 1
12657     },
12658     {
12659         clsName: 'years',
12660         navFnc: 'FullYear',
12661         navStep: 10
12662     }]
12663 });
12664
12665 Roo.apply(Roo.bootstrap.DateField,  {
12666   
12667     template : {
12668         tag: 'div',
12669         cls: 'datepicker dropdown-menu',
12670         cn: [
12671         {
12672             tag: 'div',
12673             cls: 'datepicker-days',
12674             cn: [
12675             {
12676                 tag: 'table',
12677                 cls: 'table-condensed',
12678                 cn:[
12679                 Roo.bootstrap.DateField.head,
12680                 {
12681                     tag: 'tbody'
12682                 },
12683                 Roo.bootstrap.DateField.footer
12684                 ]
12685             }
12686             ]
12687         },
12688         {
12689             tag: 'div',
12690             cls: 'datepicker-months',
12691             cn: [
12692             {
12693                 tag: 'table',
12694                 cls: 'table-condensed',
12695                 cn:[
12696                 Roo.bootstrap.DateField.head,
12697                 Roo.bootstrap.DateField.content,
12698                 Roo.bootstrap.DateField.footer
12699                 ]
12700             }
12701             ]
12702         },
12703         {
12704             tag: 'div',
12705             cls: 'datepicker-years',
12706             cn: [
12707             {
12708                 tag: 'table',
12709                 cls: 'table-condensed',
12710                 cn:[
12711                 Roo.bootstrap.DateField.head,
12712                 Roo.bootstrap.DateField.content,
12713                 Roo.bootstrap.DateField.footer
12714                 ]
12715             }
12716             ]
12717         }
12718         ]
12719     }
12720 });
12721
12722  
12723
12724  /*
12725  * - LGPL
12726  *
12727  * TimeField
12728  * 
12729  */
12730
12731 /**
12732  * @class Roo.bootstrap.TimeField
12733  * @extends Roo.bootstrap.Input
12734  * Bootstrap DateField class
12735  * 
12736  * 
12737  * @constructor
12738  * Create a new TimeField
12739  * @param {Object} config The config object
12740  */
12741
12742 Roo.bootstrap.TimeField = function(config){
12743     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12744     this.addEvents({
12745             /**
12746              * @event show
12747              * Fires when this field show.
12748              * @param {Roo.bootstrap.DateField} this
12749              * @param {Mixed} date The date value
12750              */
12751             show : true,
12752             /**
12753              * @event show
12754              * Fires when this field hide.
12755              * @param {Roo.bootstrap.DateField} this
12756              * @param {Mixed} date The date value
12757              */
12758             hide : true,
12759             /**
12760              * @event select
12761              * Fires when select a date.
12762              * @param {Roo.bootstrap.DateField} this
12763              * @param {Mixed} date The date value
12764              */
12765             select : true
12766         });
12767 };
12768
12769 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12770     
12771     /**
12772      * @cfg {String} format
12773      * The default time format string which can be overriden for localization support.  The format must be
12774      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12775      */
12776     format : "H:i",
12777        
12778     onRender: function(ct, position)
12779     {
12780         
12781         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12782                 
12783         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12784         
12785         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12786         
12787         this.pop = this.picker().select('>.datepicker-time',true).first();
12788         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12789         
12790         this.picker().on('mousedown', this.onMousedown, this);
12791         this.picker().on('click', this.onClick, this);
12792         
12793         this.picker().addClass('datepicker-dropdown');
12794     
12795         this.fillTime();
12796         this.update();
12797             
12798         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12799         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12800         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12801         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12802         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12803         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12804
12805     },
12806     
12807     fireKey: function(e){
12808         if (!this.picker().isVisible()){
12809             if (e.keyCode == 27) // allow escape to hide and re-show picker
12810                 this.show();
12811             return;
12812         }
12813
12814         e.preventDefault();
12815         
12816         switch(e.keyCode){
12817             case 27: // escape
12818                 this.hide();
12819                 break;
12820             case 37: // left
12821             case 39: // right
12822                 this.onTogglePeriod();
12823                 break;
12824             case 38: // up
12825                 this.onIncrementMinutes();
12826                 break;
12827             case 40: // down
12828                 this.onDecrementMinutes();
12829                 break;
12830             case 13: // enter
12831             case 9: // tab
12832                 this.setTime();
12833                 break;
12834         }
12835     },
12836     
12837     onClick: function(e) {
12838         e.stopPropagation();
12839         e.preventDefault();
12840     },
12841     
12842     picker : function()
12843     {
12844         return this.el.select('.datepicker', true).first();
12845     },
12846     
12847     fillTime: function()
12848     {    
12849         var time = this.pop.select('tbody', true).first();
12850         
12851         time.dom.innerHTML = '';
12852         
12853         time.createChild({
12854             tag: 'tr',
12855             cn: [
12856                 {
12857                     tag: 'td',
12858                     cn: [
12859                         {
12860                             tag: 'a',
12861                             href: '#',
12862                             cls: 'btn',
12863                             cn: [
12864                                 {
12865                                     tag: 'span',
12866                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12867                                 }
12868                             ]
12869                         } 
12870                     ]
12871                 },
12872                 {
12873                     tag: 'td',
12874                     cls: 'separator'
12875                 },
12876                 {
12877                     tag: 'td',
12878                     cn: [
12879                         {
12880                             tag: 'a',
12881                             href: '#',
12882                             cls: 'btn',
12883                             cn: [
12884                                 {
12885                                     tag: 'span',
12886                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12887                                 }
12888                             ]
12889                         }
12890                     ]
12891                 },
12892                 {
12893                     tag: 'td',
12894                     cls: 'separator'
12895                 }
12896             ]
12897         });
12898         
12899         time.createChild({
12900             tag: 'tr',
12901             cn: [
12902                 {
12903                     tag: 'td',
12904                     cn: [
12905                         {
12906                             tag: 'span',
12907                             cls: 'timepicker-hour',
12908                             html: '00'
12909                         }  
12910                     ]
12911                 },
12912                 {
12913                     tag: 'td',
12914                     cls: 'separator',
12915                     html: ':'
12916                 },
12917                 {
12918                     tag: 'td',
12919                     cn: [
12920                         {
12921                             tag: 'span',
12922                             cls: 'timepicker-minute',
12923                             html: '00'
12924                         }  
12925                     ]
12926                 },
12927                 {
12928                     tag: 'td',
12929                     cls: 'separator'
12930                 },
12931                 {
12932                     tag: 'td',
12933                     cn: [
12934                         {
12935                             tag: 'button',
12936                             type: 'button',
12937                             cls: 'btn btn-primary period',
12938                             html: 'AM'
12939                             
12940                         }
12941                     ]
12942                 }
12943             ]
12944         });
12945         
12946         time.createChild({
12947             tag: 'tr',
12948             cn: [
12949                 {
12950                     tag: 'td',
12951                     cn: [
12952                         {
12953                             tag: 'a',
12954                             href: '#',
12955                             cls: 'btn',
12956                             cn: [
12957                                 {
12958                                     tag: 'span',
12959                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12960                                 }
12961                             ]
12962                         }
12963                     ]
12964                 },
12965                 {
12966                     tag: 'td',
12967                     cls: 'separator'
12968                 },
12969                 {
12970                     tag: 'td',
12971                     cn: [
12972                         {
12973                             tag: 'a',
12974                             href: '#',
12975                             cls: 'btn',
12976                             cn: [
12977                                 {
12978                                     tag: 'span',
12979                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12980                                 }
12981                             ]
12982                         }
12983                     ]
12984                 },
12985                 {
12986                     tag: 'td',
12987                     cls: 'separator'
12988                 }
12989             ]
12990         });
12991         
12992     },
12993     
12994     update: function()
12995     {
12996         
12997         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12998         
12999         this.fill();
13000     },
13001     
13002     fill: function() 
13003     {
13004         var hours = this.time.getHours();
13005         var minutes = this.time.getMinutes();
13006         var period = 'AM';
13007         
13008         if(hours > 11){
13009             period = 'PM';
13010         }
13011         
13012         if(hours == 0){
13013             hours = 12;
13014         }
13015         
13016         
13017         if(hours > 12){
13018             hours = hours - 12;
13019         }
13020         
13021         if(hours < 10){
13022             hours = '0' + hours;
13023         }
13024         
13025         if(minutes < 10){
13026             minutes = '0' + minutes;
13027         }
13028         
13029         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13030         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13031         this.pop.select('button', true).first().dom.innerHTML = period;
13032         
13033     },
13034     
13035     place: function()
13036     {   
13037         this.picker().removeClass(['bottom', 'top']);
13038         
13039         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13040             /*
13041              * place to the top of element!
13042              *
13043              */
13044             
13045             this.picker().addClass('top');
13046             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13047             
13048             return;
13049         }
13050         
13051         this.picker().addClass('bottom');
13052         
13053         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13054     },
13055   
13056     onFocus : function()
13057     {
13058         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13059         this.show();
13060     },
13061     
13062     onBlur : function()
13063     {
13064         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13065         this.hide();
13066     },
13067     
13068     show : function()
13069     {
13070         this.picker().show();
13071         this.pop.show();
13072         this.update();
13073         this.place();
13074         
13075         this.fireEvent('show', this, this.date);
13076     },
13077     
13078     hide : function()
13079     {
13080         this.picker().hide();
13081         this.pop.hide();
13082         
13083         this.fireEvent('hide', this, this.date);
13084     },
13085     
13086     setTime : function()
13087     {
13088         this.hide();
13089         this.setValue(this.time.format(this.format));
13090         
13091         this.fireEvent('select', this, this.date);
13092         
13093         
13094     },
13095     
13096     onMousedown: function(e){
13097         e.stopPropagation();
13098         e.preventDefault();
13099     },
13100     
13101     onIncrementHours: function()
13102     {
13103         Roo.log('onIncrementHours');
13104         this.time = this.time.add(Date.HOUR, 1);
13105         this.update();
13106         
13107     },
13108     
13109     onDecrementHours: function()
13110     {
13111         Roo.log('onDecrementHours');
13112         this.time = this.time.add(Date.HOUR, -1);
13113         this.update();
13114     },
13115     
13116     onIncrementMinutes: function()
13117     {
13118         Roo.log('onIncrementMinutes');
13119         this.time = this.time.add(Date.MINUTE, 1);
13120         this.update();
13121     },
13122     
13123     onDecrementMinutes: function()
13124     {
13125         Roo.log('onDecrementMinutes');
13126         this.time = this.time.add(Date.MINUTE, -1);
13127         this.update();
13128     },
13129     
13130     onTogglePeriod: function()
13131     {
13132         Roo.log('onTogglePeriod');
13133         this.time = this.time.add(Date.HOUR, 12);
13134         this.update();
13135     }
13136     
13137    
13138 });
13139
13140 Roo.apply(Roo.bootstrap.TimeField,  {
13141     
13142     content : {
13143         tag: 'tbody',
13144         cn: [
13145             {
13146                 tag: 'tr',
13147                 cn: [
13148                 {
13149                     tag: 'td',
13150                     colspan: '7'
13151                 }
13152                 ]
13153             }
13154         ]
13155     },
13156     
13157     footer : {
13158         tag: 'tfoot',
13159         cn: [
13160             {
13161                 tag: 'tr',
13162                 cn: [
13163                 {
13164                     tag: 'th',
13165                     colspan: '7',
13166                     cls: '',
13167                     cn: [
13168                         {
13169                             tag: 'button',
13170                             cls: 'btn btn-info ok',
13171                             html: 'OK'
13172                         }
13173                     ]
13174                 }
13175
13176                 ]
13177             }
13178         ]
13179     }
13180 });
13181
13182 Roo.apply(Roo.bootstrap.TimeField,  {
13183   
13184     template : {
13185         tag: 'div',
13186         cls: 'datepicker dropdown-menu',
13187         cn: [
13188             {
13189                 tag: 'div',
13190                 cls: 'datepicker-time',
13191                 cn: [
13192                 {
13193                     tag: 'table',
13194                     cls: 'table-condensed',
13195                     cn:[
13196                     Roo.bootstrap.TimeField.content,
13197                     Roo.bootstrap.TimeField.footer
13198                     ]
13199                 }
13200                 ]
13201             }
13202         ]
13203     }
13204 });
13205
13206  
13207
13208  /*
13209  * - LGPL
13210  *
13211  * CheckBox
13212  * 
13213  */
13214
13215 /**
13216  * @class Roo.bootstrap.CheckBox
13217  * @extends Roo.bootstrap.Input
13218  * Bootstrap CheckBox class
13219  * 
13220  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13221  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13222  * @cfg {String} boxLabel The text that appears beside the checkbox
13223  * @cfg {Boolean} checked initnal the element
13224  * 
13225  * @constructor
13226  * Create a new CheckBox
13227  * @param {Object} config The config object
13228  */
13229
13230 Roo.bootstrap.CheckBox = function(config){
13231     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13232    
13233         this.addEvents({
13234             /**
13235             * @event check
13236             * Fires when the element is checked or unchecked.
13237             * @param {Roo.bootstrap.CheckBox} this This input
13238             * @param {Boolean} checked The new checked value
13239             */
13240            check : true
13241         });
13242 };
13243
13244 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13245     
13246     inputType: 'checkbox',
13247     inputValue: 1,
13248     valueOff: 0,
13249     boxLabel: false,
13250     checked: false,
13251     
13252     getAutoCreate : function()
13253     {
13254         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13255         
13256         var id = Roo.id();
13257         
13258         var cfg = {};
13259         
13260         cfg.cls = 'form-group' //input-group
13261         
13262         var input =  {
13263             tag: 'input',
13264             id : id,
13265             type : this.inputType,
13266             value : (!this.checked) ? this.valueOff : this.inputValue,
13267             cls : 'form-box',
13268             placeholder : this.placeholder || ''
13269             
13270         };
13271         
13272         if (this.disabled) {
13273             input.disabled=true;
13274         }
13275         
13276         if(this.checked){
13277             input.checked = this.checked;
13278         }
13279         
13280         if (this.name) {
13281             input.name = this.name;
13282         }
13283         
13284         if (this.size) {
13285             input.cls += ' input-' + this.size;
13286         }
13287         
13288         var settings=this;
13289         ['xs','sm','md','lg'].map(function(size){
13290             if (settings[size]) {
13291                 cfg.cls += ' col-' + size + '-' + settings[size];
13292             }
13293         });
13294         
13295         var inputblock = input;
13296         
13297         if (this.before || this.after) {
13298             
13299             inputblock = {
13300                 cls : 'input-group',
13301                 cn :  [] 
13302             };
13303             if (this.before) {
13304                 inputblock.cn.push({
13305                     tag :'span',
13306                     cls : 'input-group-addon',
13307                     html : this.before
13308                 });
13309             }
13310             inputblock.cn.push(input);
13311             if (this.after) {
13312                 inputblock.cn.push({
13313                     tag :'span',
13314                     cls : 'input-group-addon',
13315                     html : this.after
13316                 });
13317             }
13318             
13319         };
13320         
13321         if (align ==='left' && this.fieldLabel.length) {
13322                 Roo.log("left and has label");
13323                 cfg.cn = [
13324                     
13325                     {
13326                         tag: 'label',
13327                         'for' :  id,
13328                         cls : 'control-label col-md-' + this.labelWidth,
13329                         html : this.fieldLabel
13330                         
13331                     },
13332                     {
13333                         cls : "col-md-" + (12 - this.labelWidth), 
13334                         cn: [
13335                             inputblock
13336                         ]
13337                     }
13338                     
13339                 ];
13340         } else if ( this.fieldLabel.length) {
13341                 Roo.log(" label");
13342                 cfg.cn = [
13343                    
13344                     {
13345                         tag: this.boxLabel ? 'span' : 'label',
13346                         'for': id,
13347                         cls: 'control-label box-input-label',
13348                         //cls : 'input-group-addon',
13349                         html : this.fieldLabel
13350                         
13351                     },
13352                     
13353                     inputblock
13354                     
13355                 ];
13356
13357         } else {
13358             
13359                    Roo.log(" no label && no align");
13360                 cfg.cn = [
13361                     
13362                         inputblock
13363                     
13364                 ];
13365                 
13366                 
13367         };
13368         
13369         if(this.boxLabel){
13370             cfg.cn.push({
13371                 tag: 'label',
13372                 'for': id,
13373                 cls: 'box-label',
13374                 html: this.boxLabel
13375             })
13376         }
13377         
13378         return cfg;
13379         
13380     },
13381     
13382     /**
13383      * return the real input element.
13384      */
13385     inputEl: function ()
13386     {
13387         return this.el.select('input.form-box',true).first();
13388     },
13389     
13390     initEvents : function()
13391     {
13392 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13393         
13394         this.inputEl().on('click', this.onClick,  this);
13395         
13396     },
13397     
13398     onClick : function()
13399     {   
13400         this.setChecked(!this.checked);
13401     },
13402     
13403     setChecked : function(state,suppressEvent)
13404     {
13405         this.checked = state;
13406         
13407         this.inputEl().dom.checked = state;
13408         
13409         if(suppressEvent !== true){
13410             this.fireEvent('check', this, state);
13411         }
13412         
13413         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13414         
13415     },
13416     
13417     setValue : function(v,suppressEvent)
13418     {
13419         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13420     }
13421     
13422 });
13423
13424  
13425 /*
13426  * - LGPL
13427  *
13428  * Radio
13429  * 
13430  */
13431
13432 /**
13433  * @class Roo.bootstrap.Radio
13434  * @extends Roo.bootstrap.CheckBox
13435  * Bootstrap Radio class
13436
13437  * @constructor
13438  * Create a new Radio
13439  * @param {Object} config The config object
13440  */
13441
13442 Roo.bootstrap.Radio = function(config){
13443     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13444    
13445 };
13446
13447 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13448     
13449     inputType: 'radio',
13450     inputValue: '',
13451     valueOff: '',
13452     
13453     getAutoCreate : function()
13454     {
13455         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13456         
13457         var id = Roo.id();
13458         
13459         var cfg = {};
13460         
13461         cfg.cls = 'form-group' //input-group
13462         
13463         var input =  {
13464             tag: 'input',
13465             id : id,
13466             type : this.inputType,
13467             value : (!this.checked) ? this.valueOff : this.inputValue,
13468             cls : 'form-box',
13469             placeholder : this.placeholder || ''
13470             
13471         };
13472         
13473         if (this.disabled) {
13474             input.disabled=true;
13475         }
13476         
13477         if(this.checked){
13478             input.checked = this.checked;
13479         }
13480         
13481         if (this.name) {
13482             input.name = this.name;
13483         }
13484         
13485         if (this.size) {
13486             input.cls += ' input-' + this.size;
13487         }
13488         
13489         var settings=this;
13490         ['xs','sm','md','lg'].map(function(size){
13491             if (settings[size]) {
13492                 cfg.cls += ' col-' + size + '-' + settings[size];
13493             }
13494         });
13495         
13496         var inputblock = input;
13497         
13498         if (this.before || this.after) {
13499             
13500             inputblock = {
13501                 cls : 'input-group',
13502                 cn :  [] 
13503             };
13504             if (this.before) {
13505                 inputblock.cn.push({
13506                     tag :'span',
13507                     cls : 'input-group-addon',
13508                     html : this.before
13509                 });
13510             }
13511             inputblock.cn.push(input);
13512             if (this.after) {
13513                 inputblock.cn.push({
13514                     tag :'span',
13515                     cls : 'input-group-addon',
13516                     html : this.after
13517                 });
13518             }
13519             
13520         };
13521         
13522         if (align ==='left' && this.fieldLabel.length) {
13523                 Roo.log("left and has label");
13524                 cfg.cn = [
13525                     
13526                     {
13527                         tag: 'label',
13528                         'for' :  id,
13529                         cls : 'control-label col-md-' + this.labelWidth,
13530                         html : this.fieldLabel
13531                         
13532                     },
13533                     {
13534                         cls : "col-md-" + (12 - this.labelWidth), 
13535                         cn: [
13536                             inputblock
13537                         ]
13538                     }
13539                     
13540                 ];
13541         } else if ( this.fieldLabel.length) {
13542                 Roo.log(" label");
13543                  cfg.cn = [
13544                    
13545                     {
13546                         tag: 'label',
13547                         'for': id,
13548                         cls: 'control-label box-input-label',
13549                         //cls : 'input-group-addon',
13550                         html : this.fieldLabel
13551                         
13552                     },
13553                     
13554                     inputblock
13555                     
13556                 ];
13557
13558         } else {
13559             
13560                    Roo.log(" no label && no align");
13561                 cfg.cn = [
13562                     
13563                         inputblock
13564                     
13565                 ];
13566                 
13567                 
13568         };
13569         
13570         if(this.boxLabel){
13571             cfg.cn.push({
13572                 tag: 'label',
13573                 'for': id,
13574                 cls: 'box-label',
13575                 html: this.boxLabel
13576             })
13577         }
13578         
13579         return cfg;
13580         
13581     },
13582    
13583     onClick : function()
13584     {   
13585         this.setChecked(true);
13586     },
13587     
13588     setChecked : function(state,suppressEvent)
13589     {
13590         if(state){
13591             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13592                 v.dom.checked = false;
13593             });
13594         }
13595         
13596         this.checked = state;
13597         this.inputEl().dom.checked = state;
13598         
13599         if(suppressEvent !== true){
13600             this.fireEvent('check', this, state);
13601         }
13602         
13603         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13604         
13605     },
13606     
13607     getGroupValue : function()
13608     {
13609         var value = ''
13610         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13611             if(v.dom.checked == true){
13612                 value = v.dom.value;
13613             }
13614         });
13615         
13616         return value;
13617     },
13618     
13619     /**
13620      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13621      * @return {Mixed} value The field value
13622      */
13623     getValue : function(){
13624         return this.getGroupValue();
13625     }
13626     
13627 });
13628
13629  
13630 //<script type="text/javascript">
13631
13632 /*
13633  * Based  Ext JS Library 1.1.1
13634  * Copyright(c) 2006-2007, Ext JS, LLC.
13635  * LGPL
13636  *
13637  */
13638  
13639 /**
13640  * @class Roo.HtmlEditorCore
13641  * @extends Roo.Component
13642  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13643  *
13644  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13645  */
13646
13647 Roo.HtmlEditorCore = function(config){
13648     
13649     
13650     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13651     this.addEvents({
13652         /**
13653          * @event initialize
13654          * Fires when the editor is fully initialized (including the iframe)
13655          * @param {Roo.HtmlEditorCore} this
13656          */
13657         initialize: true,
13658         /**
13659          * @event activate
13660          * Fires when the editor is first receives the focus. Any insertion must wait
13661          * until after this event.
13662          * @param {Roo.HtmlEditorCore} this
13663          */
13664         activate: true,
13665          /**
13666          * @event beforesync
13667          * Fires before the textarea is updated with content from the editor iframe. Return false
13668          * to cancel the sync.
13669          * @param {Roo.HtmlEditorCore} this
13670          * @param {String} html
13671          */
13672         beforesync: true,
13673          /**
13674          * @event beforepush
13675          * Fires before the iframe editor is updated with content from the textarea. Return false
13676          * to cancel the push.
13677          * @param {Roo.HtmlEditorCore} this
13678          * @param {String} html
13679          */
13680         beforepush: true,
13681          /**
13682          * @event sync
13683          * Fires when the textarea is updated with content from the editor iframe.
13684          * @param {Roo.HtmlEditorCore} this
13685          * @param {String} html
13686          */
13687         sync: true,
13688          /**
13689          * @event push
13690          * Fires when the iframe editor is updated with content from the textarea.
13691          * @param {Roo.HtmlEditorCore} this
13692          * @param {String} html
13693          */
13694         push: true,
13695         
13696         /**
13697          * @event editorevent
13698          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13699          * @param {Roo.HtmlEditorCore} this
13700          */
13701         editorevent: true
13702     });
13703      
13704 };
13705
13706
13707 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13708
13709
13710      /**
13711      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13712      */
13713     
13714     owner : false,
13715     
13716      /**
13717      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13718      *                        Roo.resizable.
13719      */
13720     resizable : false,
13721      /**
13722      * @cfg {Number} height (in pixels)
13723      */   
13724     height: 300,
13725    /**
13726      * @cfg {Number} width (in pixels)
13727      */   
13728     width: 500,
13729     
13730     /**
13731      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13732      * 
13733      */
13734     stylesheets: false,
13735     
13736     // id of frame..
13737     frameId: false,
13738     
13739     // private properties
13740     validationEvent : false,
13741     deferHeight: true,
13742     initialized : false,
13743     activated : false,
13744     sourceEditMode : false,
13745     onFocus : Roo.emptyFn,
13746     iframePad:3,
13747     hideMode:'offsets',
13748     
13749     clearUp: true,
13750     
13751      
13752     
13753
13754     /**
13755      * Protected method that will not generally be called directly. It
13756      * is called when the editor initializes the iframe with HTML contents. Override this method if you
13757      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13758      */
13759     getDocMarkup : function(){
13760         // body styles..
13761         var st = '';
13762         Roo.log(this.stylesheets);
13763         
13764         // inherit styels from page...?? 
13765         if (this.stylesheets === false) {
13766             
13767             Roo.get(document.head).select('style').each(function(node) {
13768                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13769             });
13770             
13771             Roo.get(document.head).select('link').each(function(node) { 
13772                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13773             });
13774             
13775         } else if (!this.stylesheets.length) {
13776                 // simple..
13777                 st = '<style type="text/css">' +
13778                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13779                    '</style>';
13780         } else {
13781             Roo.each(this.stylesheets, function(s) {
13782                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13783             });
13784             
13785         }
13786         
13787         st +=  '<style type="text/css">' +
13788             'IMG { cursor: pointer } ' +
13789         '</style>';
13790
13791         
13792         return '<html><head>' + st  +
13793             //<style type="text/css">' +
13794             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13795             //'</style>' +
13796             ' </head><body class="roo-htmleditor-body"></body></html>';
13797     },
13798
13799     // private
13800     onRender : function(ct, position)
13801     {
13802         var _t = this;
13803         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13804         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13805         
13806         
13807         this.el.dom.style.border = '0 none';
13808         this.el.dom.setAttribute('tabIndex', -1);
13809         this.el.addClass('x-hidden hide');
13810         
13811         
13812         
13813         if(Roo.isIE){ // fix IE 1px bogus margin
13814             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13815         }
13816        
13817         
13818         this.frameId = Roo.id();
13819         
13820          
13821         
13822         var iframe = this.owner.wrap.createChild({
13823             tag: 'iframe',
13824             cls: 'form-control', // bootstrap..
13825             id: this.frameId,
13826             name: this.frameId,
13827             frameBorder : 'no',
13828             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13829         }, this.el
13830         );
13831         
13832         
13833         this.iframe = iframe.dom;
13834
13835          this.assignDocWin();
13836         
13837         this.doc.designMode = 'on';
13838        
13839         this.doc.open();
13840         this.doc.write(this.getDocMarkup());
13841         this.doc.close();
13842
13843         
13844         var task = { // must defer to wait for browser to be ready
13845             run : function(){
13846                 //console.log("run task?" + this.doc.readyState);
13847                 this.assignDocWin();
13848                 if(this.doc.body || this.doc.readyState == 'complete'){
13849                     try {
13850                         this.doc.designMode="on";
13851                     } catch (e) {
13852                         return;
13853                     }
13854                     Roo.TaskMgr.stop(task);
13855                     this.initEditor.defer(10, this);
13856                 }
13857             },
13858             interval : 10,
13859             duration: 10000,
13860             scope: this
13861         };
13862         Roo.TaskMgr.start(task);
13863
13864         
13865          
13866     },
13867
13868     // private
13869     onResize : function(w, h)
13870     {
13871          Roo.log('resize: ' +w + ',' + h );
13872         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13873         if(!this.iframe){
13874             return;
13875         }
13876         if(typeof w == 'number'){
13877             
13878             this.iframe.style.width = w + 'px';
13879         }
13880         if(typeof h == 'number'){
13881             
13882             this.iframe.style.height = h + 'px';
13883             if(this.doc){
13884                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13885             }
13886         }
13887         
13888     },
13889
13890     /**
13891      * Toggles the editor between standard and source edit mode.
13892      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13893      */
13894     toggleSourceEdit : function(sourceEditMode){
13895         
13896         this.sourceEditMode = sourceEditMode === true;
13897         
13898         if(this.sourceEditMode){
13899  
13900             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13901             
13902         }else{
13903             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13904             //this.iframe.className = '';
13905             this.deferFocus();
13906         }
13907         //this.setSize(this.owner.wrap.getSize());
13908         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13909     },
13910
13911     
13912   
13913
13914     /**
13915      * Protected method that will not generally be called directly. If you need/want
13916      * custom HTML cleanup, this is the method you should override.
13917      * @param {String} html The HTML to be cleaned
13918      * return {String} The cleaned HTML
13919      */
13920     cleanHtml : function(html){
13921         html = String(html);
13922         if(html.length > 5){
13923             if(Roo.isSafari){ // strip safari nonsense
13924                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13925             }
13926         }
13927         if(html == '&nbsp;'){
13928             html = '';
13929         }
13930         return html;
13931     },
13932
13933     /**
13934      * HTML Editor -> Textarea
13935      * Protected method that will not generally be called directly. Syncs the contents
13936      * of the editor iframe with the textarea.
13937      */
13938     syncValue : function(){
13939         if(this.initialized){
13940             var bd = (this.doc.body || this.doc.documentElement);
13941             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13942             var html = bd.innerHTML;
13943             if(Roo.isSafari){
13944                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13945                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13946                 if(m && m[1]){
13947                     html = '<div style="'+m[0]+'">' + html + '</div>';
13948                 }
13949             }
13950             html = this.cleanHtml(html);
13951             // fix up the special chars.. normaly like back quotes in word...
13952             // however we do not want to do this with chinese..
13953             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13954                 var cc = b.charCodeAt();
13955                 if (
13956                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13957                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13958                     (cc >= 0xf900 && cc < 0xfb00 )
13959                 ) {
13960                         return b;
13961                 }
13962                 return "&#"+cc+";" 
13963             });
13964             if(this.owner.fireEvent('beforesync', this, html) !== false){
13965                 this.el.dom.value = html;
13966                 this.owner.fireEvent('sync', this, html);
13967             }
13968         }
13969     },
13970
13971     /**
13972      * Protected method that will not generally be called directly. Pushes the value of the textarea
13973      * into the iframe editor.
13974      */
13975     pushValue : function(){
13976         if(this.initialized){
13977             var v = this.el.dom.value.trim();
13978             
13979 //            if(v.length < 1){
13980 //                v = '&#160;';
13981 //            }
13982             
13983             if(this.owner.fireEvent('beforepush', this, v) !== false){
13984                 var d = (this.doc.body || this.doc.documentElement);
13985                 d.innerHTML = v;
13986                 this.cleanUpPaste();
13987                 this.el.dom.value = d.innerHTML;
13988                 this.owner.fireEvent('push', this, v);
13989             }
13990         }
13991     },
13992
13993     // private
13994     deferFocus : function(){
13995         this.focus.defer(10, this);
13996     },
13997
13998     // doc'ed in Field
13999     focus : function(){
14000         if(this.win && !this.sourceEditMode){
14001             this.win.focus();
14002         }else{
14003             this.el.focus();
14004         }
14005     },
14006     
14007     assignDocWin: function()
14008     {
14009         var iframe = this.iframe;
14010         
14011          if(Roo.isIE){
14012             this.doc = iframe.contentWindow.document;
14013             this.win = iframe.contentWindow;
14014         } else {
14015             if (!Roo.get(this.frameId)) {
14016                 return;
14017             }
14018             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14019             this.win = Roo.get(this.frameId).dom.contentWindow;
14020         }
14021     },
14022     
14023     // private
14024     initEditor : function(){
14025         //console.log("INIT EDITOR");
14026         this.assignDocWin();
14027         
14028         
14029         
14030         this.doc.designMode="on";
14031         this.doc.open();
14032         this.doc.write(this.getDocMarkup());
14033         this.doc.close();
14034         
14035         var dbody = (this.doc.body || this.doc.documentElement);
14036         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14037         // this copies styles from the containing element into thsi one..
14038         // not sure why we need all of this..
14039         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14040         ss['background-attachment'] = 'fixed'; // w3c
14041         dbody.bgProperties = 'fixed'; // ie
14042         Roo.DomHelper.applyStyles(dbody, ss);
14043         Roo.EventManager.on(this.doc, {
14044             //'mousedown': this.onEditorEvent,
14045             'mouseup': this.onEditorEvent,
14046             'dblclick': this.onEditorEvent,
14047             'click': this.onEditorEvent,
14048             'keyup': this.onEditorEvent,
14049             buffer:100,
14050             scope: this
14051         });
14052         if(Roo.isGecko){
14053             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14054         }
14055         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14056             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14057         }
14058         this.initialized = true;
14059
14060         this.owner.fireEvent('initialize', this);
14061         this.pushValue();
14062     },
14063
14064     // private
14065     onDestroy : function(){
14066         
14067         
14068         
14069         if(this.rendered){
14070             
14071             //for (var i =0; i < this.toolbars.length;i++) {
14072             //    // fixme - ask toolbars for heights?
14073             //    this.toolbars[i].onDestroy();
14074            // }
14075             
14076             //this.wrap.dom.innerHTML = '';
14077             //this.wrap.remove();
14078         }
14079     },
14080
14081     // private
14082     onFirstFocus : function(){
14083         
14084         this.assignDocWin();
14085         
14086         
14087         this.activated = true;
14088          
14089     
14090         if(Roo.isGecko){ // prevent silly gecko errors
14091             this.win.focus();
14092             var s = this.win.getSelection();
14093             if(!s.focusNode || s.focusNode.nodeType != 3){
14094                 var r = s.getRangeAt(0);
14095                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14096                 r.collapse(true);
14097                 this.deferFocus();
14098             }
14099             try{
14100                 this.execCmd('useCSS', true);
14101                 this.execCmd('styleWithCSS', false);
14102             }catch(e){}
14103         }
14104         this.owner.fireEvent('activate', this);
14105     },
14106
14107     // private
14108     adjustFont: function(btn){
14109         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14110         //if(Roo.isSafari){ // safari
14111         //    adjust *= 2;
14112        // }
14113         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14114         if(Roo.isSafari){ // safari
14115             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14116             v =  (v < 10) ? 10 : v;
14117             v =  (v > 48) ? 48 : v;
14118             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14119             
14120         }
14121         
14122         
14123         v = Math.max(1, v+adjust);
14124         
14125         this.execCmd('FontSize', v  );
14126     },
14127
14128     onEditorEvent : function(e){
14129         this.owner.fireEvent('editorevent', this, e);
14130       //  this.updateToolbar();
14131         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14132     },
14133
14134     insertTag : function(tg)
14135     {
14136         // could be a bit smarter... -> wrap the current selected tRoo..
14137         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14138             
14139             range = this.createRange(this.getSelection());
14140             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14141             wrappingNode.appendChild(range.extractContents());
14142             range.insertNode(wrappingNode);
14143
14144             return;
14145             
14146             
14147             
14148         }
14149         this.execCmd("formatblock",   tg);
14150         
14151     },
14152     
14153     insertText : function(txt)
14154     {
14155         
14156         
14157         var range = this.createRange();
14158         range.deleteContents();
14159                //alert(Sender.getAttribute('label'));
14160                
14161         range.insertNode(this.doc.createTextNode(txt));
14162     } ,
14163     
14164      
14165
14166     /**
14167      * Executes a Midas editor command on the editor document and performs necessary focus and
14168      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14169      * @param {String} cmd The Midas command
14170      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14171      */
14172     relayCmd : function(cmd, value){
14173         this.win.focus();
14174         this.execCmd(cmd, value);
14175         this.owner.fireEvent('editorevent', this);
14176         //this.updateToolbar();
14177         this.owner.deferFocus();
14178     },
14179
14180     /**
14181      * Executes a Midas editor command directly on the editor document.
14182      * For visual commands, you should use {@link #relayCmd} instead.
14183      * <b>This should only be called after the editor is initialized.</b>
14184      * @param {String} cmd The Midas command
14185      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14186      */
14187     execCmd : function(cmd, value){
14188         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14189         this.syncValue();
14190     },
14191  
14192  
14193    
14194     /**
14195      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14196      * to insert tRoo.
14197      * @param {String} text | dom node.. 
14198      */
14199     insertAtCursor : function(text)
14200     {
14201         
14202         
14203         
14204         if(!this.activated){
14205             return;
14206         }
14207         /*
14208         if(Roo.isIE){
14209             this.win.focus();
14210             var r = this.doc.selection.createRange();
14211             if(r){
14212                 r.collapse(true);
14213                 r.pasteHTML(text);
14214                 this.syncValue();
14215                 this.deferFocus();
14216             
14217             }
14218             return;
14219         }
14220         */
14221         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14222             this.win.focus();
14223             
14224             
14225             // from jquery ui (MIT licenced)
14226             var range, node;
14227             var win = this.win;
14228             
14229             if (win.getSelection && win.getSelection().getRangeAt) {
14230                 range = win.getSelection().getRangeAt(0);
14231                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14232                 range.insertNode(node);
14233             } else if (win.document.selection && win.document.selection.createRange) {
14234                 // no firefox support
14235                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14236                 win.document.selection.createRange().pasteHTML(txt);
14237             } else {
14238                 // no firefox support
14239                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14240                 this.execCmd('InsertHTML', txt);
14241             } 
14242             
14243             this.syncValue();
14244             
14245             this.deferFocus();
14246         }
14247     },
14248  // private
14249     mozKeyPress : function(e){
14250         if(e.ctrlKey){
14251             var c = e.getCharCode(), cmd;
14252           
14253             if(c > 0){
14254                 c = String.fromCharCode(c).toLowerCase();
14255                 switch(c){
14256                     case 'b':
14257                         cmd = 'bold';
14258                         break;
14259                     case 'i':
14260                         cmd = 'italic';
14261                         break;
14262                     
14263                     case 'u':
14264                         cmd = 'underline';
14265                         break;
14266                     
14267                     case 'v':
14268                         this.cleanUpPaste.defer(100, this);
14269                         return;
14270                         
14271                 }
14272                 if(cmd){
14273                     this.win.focus();
14274                     this.execCmd(cmd);
14275                     this.deferFocus();
14276                     e.preventDefault();
14277                 }
14278                 
14279             }
14280         }
14281     },
14282
14283     // private
14284     fixKeys : function(){ // load time branching for fastest keydown performance
14285         if(Roo.isIE){
14286             return function(e){
14287                 var k = e.getKey(), r;
14288                 if(k == e.TAB){
14289                     e.stopEvent();
14290                     r = this.doc.selection.createRange();
14291                     if(r){
14292                         r.collapse(true);
14293                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14294                         this.deferFocus();
14295                     }
14296                     return;
14297                 }
14298                 
14299                 if(k == e.ENTER){
14300                     r = this.doc.selection.createRange();
14301                     if(r){
14302                         var target = r.parentElement();
14303                         if(!target || target.tagName.toLowerCase() != 'li'){
14304                             e.stopEvent();
14305                             r.pasteHTML('<br />');
14306                             r.collapse(false);
14307                             r.select();
14308                         }
14309                     }
14310                 }
14311                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14312                     this.cleanUpPaste.defer(100, this);
14313                     return;
14314                 }
14315                 
14316                 
14317             };
14318         }else if(Roo.isOpera){
14319             return function(e){
14320                 var k = e.getKey();
14321                 if(k == e.TAB){
14322                     e.stopEvent();
14323                     this.win.focus();
14324                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14325                     this.deferFocus();
14326                 }
14327                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14328                     this.cleanUpPaste.defer(100, this);
14329                     return;
14330                 }
14331                 
14332             };
14333         }else if(Roo.isSafari){
14334             return function(e){
14335                 var k = e.getKey();
14336                 
14337                 if(k == e.TAB){
14338                     e.stopEvent();
14339                     this.execCmd('InsertText','\t');
14340                     this.deferFocus();
14341                     return;
14342                 }
14343                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14344                     this.cleanUpPaste.defer(100, this);
14345                     return;
14346                 }
14347                 
14348              };
14349         }
14350     }(),
14351     
14352     getAllAncestors: function()
14353     {
14354         var p = this.getSelectedNode();
14355         var a = [];
14356         if (!p) {
14357             a.push(p); // push blank onto stack..
14358             p = this.getParentElement();
14359         }
14360         
14361         
14362         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14363             a.push(p);
14364             p = p.parentNode;
14365         }
14366         a.push(this.doc.body);
14367         return a;
14368     },
14369     lastSel : false,
14370     lastSelNode : false,
14371     
14372     
14373     getSelection : function() 
14374     {
14375         this.assignDocWin();
14376         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14377     },
14378     
14379     getSelectedNode: function() 
14380     {
14381         // this may only work on Gecko!!!
14382         
14383         // should we cache this!!!!
14384         
14385         
14386         
14387          
14388         var range = this.createRange(this.getSelection()).cloneRange();
14389         
14390         if (Roo.isIE) {
14391             var parent = range.parentElement();
14392             while (true) {
14393                 var testRange = range.duplicate();
14394                 testRange.moveToElementText(parent);
14395                 if (testRange.inRange(range)) {
14396                     break;
14397                 }
14398                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14399                     break;
14400                 }
14401                 parent = parent.parentElement;
14402             }
14403             return parent;
14404         }
14405         
14406         // is ancestor a text element.
14407         var ac =  range.commonAncestorContainer;
14408         if (ac.nodeType == 3) {
14409             ac = ac.parentNode;
14410         }
14411         
14412         var ar = ac.childNodes;
14413          
14414         var nodes = [];
14415         var other_nodes = [];
14416         var has_other_nodes = false;
14417         for (var i=0;i<ar.length;i++) {
14418             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14419                 continue;
14420             }
14421             // fullly contained node.
14422             
14423             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14424                 nodes.push(ar[i]);
14425                 continue;
14426             }
14427             
14428             // probably selected..
14429             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14430                 other_nodes.push(ar[i]);
14431                 continue;
14432             }
14433             // outer..
14434             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14435                 continue;
14436             }
14437             
14438             
14439             has_other_nodes = true;
14440         }
14441         if (!nodes.length && other_nodes.length) {
14442             nodes= other_nodes;
14443         }
14444         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14445             return false;
14446         }
14447         
14448         return nodes[0];
14449     },
14450     createRange: function(sel)
14451     {
14452         // this has strange effects when using with 
14453         // top toolbar - not sure if it's a great idea.
14454         //this.editor.contentWindow.focus();
14455         if (typeof sel != "undefined") {
14456             try {
14457                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14458             } catch(e) {
14459                 return this.doc.createRange();
14460             }
14461         } else {
14462             return this.doc.createRange();
14463         }
14464     },
14465     getParentElement: function()
14466     {
14467         
14468         this.assignDocWin();
14469         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14470         
14471         var range = this.createRange(sel);
14472          
14473         try {
14474             var p = range.commonAncestorContainer;
14475             while (p.nodeType == 3) { // text node
14476                 p = p.parentNode;
14477             }
14478             return p;
14479         } catch (e) {
14480             return null;
14481         }
14482     
14483     },
14484     /***
14485      *
14486      * Range intersection.. the hard stuff...
14487      *  '-1' = before
14488      *  '0' = hits..
14489      *  '1' = after.
14490      *         [ -- selected range --- ]
14491      *   [fail]                        [fail]
14492      *
14493      *    basically..
14494      *      if end is before start or  hits it. fail.
14495      *      if start is after end or hits it fail.
14496      *
14497      *   if either hits (but other is outside. - then it's not 
14498      *   
14499      *    
14500      **/
14501     
14502     
14503     // @see http://www.thismuchiknow.co.uk/?p=64.
14504     rangeIntersectsNode : function(range, node)
14505     {
14506         var nodeRange = node.ownerDocument.createRange();
14507         try {
14508             nodeRange.selectNode(node);
14509         } catch (e) {
14510             nodeRange.selectNodeContents(node);
14511         }
14512     
14513         var rangeStartRange = range.cloneRange();
14514         rangeStartRange.collapse(true);
14515     
14516         var rangeEndRange = range.cloneRange();
14517         rangeEndRange.collapse(false);
14518     
14519         var nodeStartRange = nodeRange.cloneRange();
14520         nodeStartRange.collapse(true);
14521     
14522         var nodeEndRange = nodeRange.cloneRange();
14523         nodeEndRange.collapse(false);
14524     
14525         return rangeStartRange.compareBoundaryPoints(
14526                  Range.START_TO_START, nodeEndRange) == -1 &&
14527                rangeEndRange.compareBoundaryPoints(
14528                  Range.START_TO_START, nodeStartRange) == 1;
14529         
14530          
14531     },
14532     rangeCompareNode : function(range, node)
14533     {
14534         var nodeRange = node.ownerDocument.createRange();
14535         try {
14536             nodeRange.selectNode(node);
14537         } catch (e) {
14538             nodeRange.selectNodeContents(node);
14539         }
14540         
14541         
14542         range.collapse(true);
14543     
14544         nodeRange.collapse(true);
14545      
14546         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14547         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
14548          
14549         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14550         
14551         var nodeIsBefore   =  ss == 1;
14552         var nodeIsAfter    = ee == -1;
14553         
14554         if (nodeIsBefore && nodeIsAfter)
14555             return 0; // outer
14556         if (!nodeIsBefore && nodeIsAfter)
14557             return 1; //right trailed.
14558         
14559         if (nodeIsBefore && !nodeIsAfter)
14560             return 2;  // left trailed.
14561         // fully contined.
14562         return 3;
14563     },
14564
14565     // private? - in a new class?
14566     cleanUpPaste :  function()
14567     {
14568         // cleans up the whole document..
14569         Roo.log('cleanuppaste');
14570         
14571         this.cleanUpChildren(this.doc.body);
14572         var clean = this.cleanWordChars(this.doc.body.innerHTML);
14573         if (clean != this.doc.body.innerHTML) {
14574             this.doc.body.innerHTML = clean;
14575         }
14576         
14577     },
14578     
14579     cleanWordChars : function(input) {// change the chars to hex code
14580         var he = Roo.HtmlEditorCore;
14581         
14582         var output = input;
14583         Roo.each(he.swapCodes, function(sw) { 
14584             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14585             
14586             output = output.replace(swapper, sw[1]);
14587         });
14588         
14589         return output;
14590     },
14591     
14592     
14593     cleanUpChildren : function (n)
14594     {
14595         if (!n.childNodes.length) {
14596             return;
14597         }
14598         for (var i = n.childNodes.length-1; i > -1 ; i--) {
14599            this.cleanUpChild(n.childNodes[i]);
14600         }
14601     },
14602     
14603     
14604         
14605     
14606     cleanUpChild : function (node)
14607     {
14608         var ed = this;
14609         //console.log(node);
14610         if (node.nodeName == "#text") {
14611             // clean up silly Windows -- stuff?
14612             return; 
14613         }
14614         if (node.nodeName == "#comment") {
14615             node.parentNode.removeChild(node);
14616             // clean up silly Windows -- stuff?
14617             return; 
14618         }
14619         
14620         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14621             // remove node.
14622             node.parentNode.removeChild(node);
14623             return;
14624             
14625         }
14626         
14627         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14628         
14629         // remove <a name=....> as rendering on yahoo mailer is borked with this.
14630         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14631         
14632         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14633         //    remove_keep_children = true;
14634         //}
14635         
14636         if (remove_keep_children) {
14637             this.cleanUpChildren(node);
14638             // inserts everything just before this node...
14639             while (node.childNodes.length) {
14640                 var cn = node.childNodes[0];
14641                 node.removeChild(cn);
14642                 node.parentNode.insertBefore(cn, node);
14643             }
14644             node.parentNode.removeChild(node);
14645             return;
14646         }
14647         
14648         if (!node.attributes || !node.attributes.length) {
14649             this.cleanUpChildren(node);
14650             return;
14651         }
14652         
14653         function cleanAttr(n,v)
14654         {
14655             
14656             if (v.match(/^\./) || v.match(/^\//)) {
14657                 return;
14658             }
14659             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14660                 return;
14661             }
14662             if (v.match(/^#/)) {
14663                 return;
14664             }
14665 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14666             node.removeAttribute(n);
14667             
14668         }
14669         
14670         function cleanStyle(n,v)
14671         {
14672             if (v.match(/expression/)) { //XSS?? should we even bother..
14673                 node.removeAttribute(n);
14674                 return;
14675             }
14676             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14677             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14678             
14679             
14680             var parts = v.split(/;/);
14681             var clean = [];
14682             
14683             Roo.each(parts, function(p) {
14684                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14685                 if (!p.length) {
14686                     return true;
14687                 }
14688                 var l = p.split(':').shift().replace(/\s+/g,'');
14689                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14690                 
14691                 if ( cblack.indexOf(l) > -1) {
14692 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14693                     //node.removeAttribute(n);
14694                     return true;
14695                 }
14696                 //Roo.log()
14697                 // only allow 'c whitelisted system attributes'
14698                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14699 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14700                     //node.removeAttribute(n);
14701                     return true;
14702                 }
14703                 
14704                 
14705                  
14706                 
14707                 clean.push(p);
14708                 return true;
14709             });
14710             if (clean.length) { 
14711                 node.setAttribute(n, clean.join(';'));
14712             } else {
14713                 node.removeAttribute(n);
14714             }
14715             
14716         }
14717         
14718         
14719         for (var i = node.attributes.length-1; i > -1 ; i--) {
14720             var a = node.attributes[i];
14721             //console.log(a);
14722             
14723             if (a.name.toLowerCase().substr(0,2)=='on')  {
14724                 node.removeAttribute(a.name);
14725                 continue;
14726             }
14727             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14728                 node.removeAttribute(a.name);
14729                 continue;
14730             }
14731             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14732                 cleanAttr(a.name,a.value); // fixme..
14733                 continue;
14734             }
14735             if (a.name == 'style') {
14736                 cleanStyle(a.name,a.value);
14737                 continue;
14738             }
14739             /// clean up MS crap..
14740             // tecnically this should be a list of valid class'es..
14741             
14742             
14743             if (a.name == 'class') {
14744                 if (a.value.match(/^Mso/)) {
14745                     node.className = '';
14746                 }
14747                 
14748                 if (a.value.match(/body/)) {
14749                     node.className = '';
14750                 }
14751                 continue;
14752             }
14753             
14754             // style cleanup!?
14755             // class cleanup?
14756             
14757         }
14758         
14759         
14760         this.cleanUpChildren(node);
14761         
14762         
14763     }
14764     
14765     
14766     // hide stuff that is not compatible
14767     /**
14768      * @event blur
14769      * @hide
14770      */
14771     /**
14772      * @event change
14773      * @hide
14774      */
14775     /**
14776      * @event focus
14777      * @hide
14778      */
14779     /**
14780      * @event specialkey
14781      * @hide
14782      */
14783     /**
14784      * @cfg {String} fieldClass @hide
14785      */
14786     /**
14787      * @cfg {String} focusClass @hide
14788      */
14789     /**
14790      * @cfg {String} autoCreate @hide
14791      */
14792     /**
14793      * @cfg {String} inputType @hide
14794      */
14795     /**
14796      * @cfg {String} invalidClass @hide
14797      */
14798     /**
14799      * @cfg {String} invalidText @hide
14800      */
14801     /**
14802      * @cfg {String} msgFx @hide
14803      */
14804     /**
14805      * @cfg {String} validateOnBlur @hide
14806      */
14807 });
14808
14809 Roo.HtmlEditorCore.white = [
14810         'area', 'br', 'img', 'input', 'hr', 'wbr',
14811         
14812        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14813        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14814        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14815        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14816        'table',   'ul',         'xmp', 
14817        
14818        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14819       'thead',   'tr', 
14820      
14821       'dir', 'menu', 'ol', 'ul', 'dl',
14822        
14823       'embed',  'object'
14824 ];
14825
14826
14827 Roo.HtmlEditorCore.black = [
14828     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14829         'applet', // 
14830         'base',   'basefont', 'bgsound', 'blink',  'body', 
14831         'frame',  'frameset', 'head',    'html',   'ilayer', 
14832         'iframe', 'layer',  'link',     'meta',    'object',   
14833         'script', 'style' ,'title',  'xml' // clean later..
14834 ];
14835 Roo.HtmlEditorCore.clean = [
14836     'script', 'style', 'title', 'xml'
14837 ];
14838 Roo.HtmlEditorCore.remove = [
14839     'font'
14840 ];
14841 // attributes..
14842
14843 Roo.HtmlEditorCore.ablack = [
14844     'on'
14845 ];
14846     
14847 Roo.HtmlEditorCore.aclean = [ 
14848     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14849 ];
14850
14851 // protocols..
14852 Roo.HtmlEditorCore.pwhite= [
14853         'http',  'https',  'mailto'
14854 ];
14855
14856 // white listed style attributes.
14857 Roo.HtmlEditorCore.cwhite= [
14858       //  'text-align', /// default is to allow most things..
14859       
14860          
14861 //        'font-size'//??
14862 ];
14863
14864 // black listed style attributes.
14865 Roo.HtmlEditorCore.cblack= [
14866       //  'font-size' -- this can be set by the project 
14867 ];
14868
14869
14870 Roo.HtmlEditorCore.swapCodes   =[ 
14871     [    8211, "--" ], 
14872     [    8212, "--" ], 
14873     [    8216,  "'" ],  
14874     [    8217, "'" ],  
14875     [    8220, '"' ],  
14876     [    8221, '"' ],  
14877     [    8226, "*" ],  
14878     [    8230, "..." ]
14879 ]; 
14880
14881     /*
14882  * - LGPL
14883  *
14884  * HtmlEditor
14885  * 
14886  */
14887
14888 /**
14889  * @class Roo.bootstrap.HtmlEditor
14890  * @extends Roo.bootstrap.TextArea
14891  * Bootstrap HtmlEditor class
14892
14893  * @constructor
14894  * Create a new HtmlEditor
14895  * @param {Object} config The config object
14896  */
14897
14898 Roo.bootstrap.HtmlEditor = function(config){
14899     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14900     if (!this.toolbars) {
14901         this.toolbars = [];
14902     }
14903     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14904     this.addEvents({
14905             /**
14906              * @event initialize
14907              * Fires when the editor is fully initialized (including the iframe)
14908              * @param {HtmlEditor} this
14909              */
14910             initialize: true,
14911             /**
14912              * @event activate
14913              * Fires when the editor is first receives the focus. Any insertion must wait
14914              * until after this event.
14915              * @param {HtmlEditor} this
14916              */
14917             activate: true,
14918              /**
14919              * @event beforesync
14920              * Fires before the textarea is updated with content from the editor iframe. Return false
14921              * to cancel the sync.
14922              * @param {HtmlEditor} this
14923              * @param {String} html
14924              */
14925             beforesync: true,
14926              /**
14927              * @event beforepush
14928              * Fires before the iframe editor is updated with content from the textarea. Return false
14929              * to cancel the push.
14930              * @param {HtmlEditor} this
14931              * @param {String} html
14932              */
14933             beforepush: true,
14934              /**
14935              * @event sync
14936              * Fires when the textarea is updated with content from the editor iframe.
14937              * @param {HtmlEditor} this
14938              * @param {String} html
14939              */
14940             sync: true,
14941              /**
14942              * @event push
14943              * Fires when the iframe editor is updated with content from the textarea.
14944              * @param {HtmlEditor} this
14945              * @param {String} html
14946              */
14947             push: true,
14948              /**
14949              * @event editmodechange
14950              * Fires when the editor switches edit modes
14951              * @param {HtmlEditor} this
14952              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14953              */
14954             editmodechange: true,
14955             /**
14956              * @event editorevent
14957              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14958              * @param {HtmlEditor} this
14959              */
14960             editorevent: true,
14961             /**
14962              * @event firstfocus
14963              * Fires when on first focus - needed by toolbars..
14964              * @param {HtmlEditor} this
14965              */
14966             firstfocus: true,
14967             /**
14968              * @event autosave
14969              * Auto save the htmlEditor value as a file into Events
14970              * @param {HtmlEditor} this
14971              */
14972             autosave: true,
14973             /**
14974              * @event savedpreview
14975              * preview the saved version of htmlEditor
14976              * @param {HtmlEditor} this
14977              */
14978             savedpreview: true
14979         });
14980 };
14981
14982
14983 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14984     
14985     
14986       /**
14987      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14988      */
14989     toolbars : false,
14990    
14991      /**
14992      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14993      *                        Roo.resizable.
14994      */
14995     resizable : false,
14996      /**
14997      * @cfg {Number} height (in pixels)
14998      */   
14999     height: 300,
15000    /**
15001      * @cfg {Number} width (in pixels)
15002      */   
15003     width: false,
15004     
15005     /**
15006      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15007      * 
15008      */
15009     stylesheets: false,
15010     
15011     // id of frame..
15012     frameId: false,
15013     
15014     // private properties
15015     validationEvent : false,
15016     deferHeight: true,
15017     initialized : false,
15018     activated : false,
15019     
15020     onFocus : Roo.emptyFn,
15021     iframePad:3,
15022     hideMode:'offsets',
15023     
15024     
15025     tbContainer : false,
15026     
15027     toolbarContainer :function() {
15028         return this.wrap.select('.x-html-editor-tb',true).first();
15029     },
15030
15031     /**
15032      * Protected method that will not generally be called directly. It
15033      * is called when the editor creates its toolbar. Override this method if you need to
15034      * add custom toolbar buttons.
15035      * @param {HtmlEditor} editor
15036      */
15037     createToolbar : function(){
15038         
15039         Roo.log("create toolbars");
15040         
15041         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15042         this.toolbars[0].render(this.toolbarContainer());
15043         
15044         return;
15045         
15046 //        if (!editor.toolbars || !editor.toolbars.length) {
15047 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15048 //        }
15049 //        
15050 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15051 //            editor.toolbars[i] = Roo.factory(
15052 //                    typeof(editor.toolbars[i]) == 'string' ?
15053 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15054 //                Roo.bootstrap.HtmlEditor);
15055 //            editor.toolbars[i].init(editor);
15056 //        }
15057     },
15058
15059      
15060     // private
15061     onRender : function(ct, position)
15062     {
15063        // Roo.log("Call onRender: " + this.xtype);
15064         var _t = this;
15065         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15066       
15067         this.wrap = this.inputEl().wrap({
15068             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15069         });
15070         
15071         this.editorcore.onRender(ct, position);
15072          
15073         if (this.resizable) {
15074             this.resizeEl = new Roo.Resizable(this.wrap, {
15075                 pinned : true,
15076                 wrap: true,
15077                 dynamic : true,
15078                 minHeight : this.height,
15079                 height: this.height,
15080                 handles : this.resizable,
15081                 width: this.width,
15082                 listeners : {
15083                     resize : function(r, w, h) {
15084                         _t.onResize(w,h); // -something
15085                     }
15086                 }
15087             });
15088             
15089         }
15090         this.createToolbar(this);
15091        
15092         
15093         if(!this.width && this.resizable){
15094             this.setSize(this.wrap.getSize());
15095         }
15096         if (this.resizeEl) {
15097             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15098             // should trigger onReize..
15099         }
15100         
15101     },
15102
15103     // private
15104     onResize : function(w, h)
15105     {
15106         Roo.log('resize: ' +w + ',' + h );
15107         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15108         var ew = false;
15109         var eh = false;
15110         
15111         if(this.inputEl() ){
15112             if(typeof w == 'number'){
15113                 var aw = w - this.wrap.getFrameWidth('lr');
15114                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15115                 ew = aw;
15116             }
15117             if(typeof h == 'number'){
15118                  var tbh = -11;  // fixme it needs to tool bar size!
15119                 for (var i =0; i < this.toolbars.length;i++) {
15120                     // fixme - ask toolbars for heights?
15121                     tbh += this.toolbars[i].el.getHeight();
15122                     //if (this.toolbars[i].footer) {
15123                     //    tbh += this.toolbars[i].footer.el.getHeight();
15124                     //}
15125                 }
15126               
15127                 
15128                 
15129                 
15130                 
15131                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15132                 ah -= 5; // knock a few pixes off for look..
15133                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15134                 var eh = ah;
15135             }
15136         }
15137         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15138         this.editorcore.onResize(ew,eh);
15139         
15140     },
15141
15142     /**
15143      * Toggles the editor between standard and source edit mode.
15144      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15145      */
15146     toggleSourceEdit : function(sourceEditMode)
15147     {
15148         this.editorcore.toggleSourceEdit(sourceEditMode);
15149         
15150         if(this.editorcore.sourceEditMode){
15151             Roo.log('editor - showing textarea');
15152             
15153 //            Roo.log('in');
15154 //            Roo.log(this.syncValue());
15155             this.syncValue();
15156             this.inputEl().removeClass('hide');
15157             this.inputEl().dom.removeAttribute('tabIndex');
15158             this.inputEl().focus();
15159         }else{
15160             Roo.log('editor - hiding textarea');
15161 //            Roo.log('out')
15162 //            Roo.log(this.pushValue()); 
15163             this.pushValue();
15164             
15165             this.inputEl().addClass('hide');
15166             this.inputEl().dom.setAttribute('tabIndex', -1);
15167             //this.deferFocus();
15168         }
15169          
15170         if(this.resizable){
15171             this.setSize(this.wrap.getSize());
15172         }
15173         
15174         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15175     },
15176  
15177     // private (for BoxComponent)
15178     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15179
15180     // private (for BoxComponent)
15181     getResizeEl : function(){
15182         return this.wrap;
15183     },
15184
15185     // private (for BoxComponent)
15186     getPositionEl : function(){
15187         return this.wrap;
15188     },
15189
15190     // private
15191     initEvents : function(){
15192         this.originalValue = this.getValue();
15193     },
15194
15195 //    /**
15196 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15197 //     * @method
15198 //     */
15199 //    markInvalid : Roo.emptyFn,
15200 //    /**
15201 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15202 //     * @method
15203 //     */
15204 //    clearInvalid : Roo.emptyFn,
15205
15206     setValue : function(v){
15207         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15208         this.editorcore.pushValue();
15209     },
15210
15211      
15212     // private
15213     deferFocus : function(){
15214         this.focus.defer(10, this);
15215     },
15216
15217     // doc'ed in Field
15218     focus : function(){
15219         this.editorcore.focus();
15220         
15221     },
15222       
15223
15224     // private
15225     onDestroy : function(){
15226         
15227         
15228         
15229         if(this.rendered){
15230             
15231             for (var i =0; i < this.toolbars.length;i++) {
15232                 // fixme - ask toolbars for heights?
15233                 this.toolbars[i].onDestroy();
15234             }
15235             
15236             this.wrap.dom.innerHTML = '';
15237             this.wrap.remove();
15238         }
15239     },
15240
15241     // private
15242     onFirstFocus : function(){
15243         //Roo.log("onFirstFocus");
15244         this.editorcore.onFirstFocus();
15245          for (var i =0; i < this.toolbars.length;i++) {
15246             this.toolbars[i].onFirstFocus();
15247         }
15248         
15249     },
15250     
15251     // private
15252     syncValue : function()
15253     {   
15254         this.editorcore.syncValue();
15255     },
15256     
15257     pushValue : function()
15258     {   
15259         this.editorcore.pushValue();
15260     }
15261      
15262     
15263     // hide stuff that is not compatible
15264     /**
15265      * @event blur
15266      * @hide
15267      */
15268     /**
15269      * @event change
15270      * @hide
15271      */
15272     /**
15273      * @event focus
15274      * @hide
15275      */
15276     /**
15277      * @event specialkey
15278      * @hide
15279      */
15280     /**
15281      * @cfg {String} fieldClass @hide
15282      */
15283     /**
15284      * @cfg {String} focusClass @hide
15285      */
15286     /**
15287      * @cfg {String} autoCreate @hide
15288      */
15289     /**
15290      * @cfg {String} inputType @hide
15291      */
15292     /**
15293      * @cfg {String} invalidClass @hide
15294      */
15295     /**
15296      * @cfg {String} invalidText @hide
15297      */
15298     /**
15299      * @cfg {String} msgFx @hide
15300      */
15301     /**
15302      * @cfg {String} validateOnBlur @hide
15303      */
15304 });
15305  
15306     
15307    
15308    
15309    
15310       
15311
15312 /**
15313  * @class Roo.bootstrap.HtmlEditorToolbar1
15314  * Basic Toolbar
15315  * 
15316  * Usage:
15317  *
15318  new Roo.bootstrap.HtmlEditor({
15319     ....
15320     toolbars : [
15321         new Roo.bootstrap.HtmlEditorToolbar1({
15322             disable : { fonts: 1 , format: 1, ..., ... , ...],
15323             btns : [ .... ]
15324         })
15325     }
15326      
15327  * 
15328  * @cfg {Object} disable List of elements to disable..
15329  * @cfg {Array} btns List of additional buttons.
15330  * 
15331  * 
15332  * NEEDS Extra CSS? 
15333  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15334  */
15335  
15336 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15337 {
15338     
15339     Roo.apply(this, config);
15340     
15341     // default disabled, based on 'good practice'..
15342     this.disable = this.disable || {};
15343     Roo.applyIf(this.disable, {
15344         fontSize : true,
15345         colors : true,
15346         specialElements : true
15347     });
15348     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15349     
15350     this.editor = config.editor;
15351     this.editorcore = config.editor.editorcore;
15352     
15353     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15354     
15355     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15356     // dont call parent... till later.
15357 }
15358 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
15359     
15360     
15361     bar : true,
15362     
15363     editor : false,
15364     editorcore : false,
15365     
15366     
15367     formats : [
15368         "p" ,  
15369         "h1","h2","h3","h4","h5","h6", 
15370         "pre", "code", 
15371         "abbr", "acronym", "address", "cite", "samp", "var",
15372         'div','span'
15373     ],
15374     
15375     onRender : function(ct, position)
15376     {
15377        // Roo.log("Call onRender: " + this.xtype);
15378         
15379        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15380        Roo.log(this.el);
15381        this.el.dom.style.marginBottom = '0';
15382        var _this = this;
15383        var editorcore = this.editorcore;
15384        var editor= this.editor;
15385        
15386        var children = [];
15387        var btn = function(id,cmd , toggle, handler){
15388        
15389             var  event = toggle ? 'toggle' : 'click';
15390        
15391             var a = {
15392                 size : 'sm',
15393                 xtype: 'Button',
15394                 xns: Roo.bootstrap,
15395                 glyphicon : id,
15396                 cmd : id || cmd,
15397                 enableToggle:toggle !== false,
15398                 //html : 'submit'
15399                 pressed : toggle ? false : null,
15400                 listeners : {}
15401             }
15402             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15403                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15404             }
15405             children.push(a);
15406             return a;
15407        }
15408         
15409         var style = {
15410                 xtype: 'Button',
15411                 size : 'sm',
15412                 xns: Roo.bootstrap,
15413                 glyphicon : 'font',
15414                 //html : 'submit'
15415                 menu : {
15416                     xtype: 'Menu',
15417                     xns: Roo.bootstrap,
15418                     items:  []
15419                 }
15420         };
15421         Roo.each(this.formats, function(f) {
15422             style.menu.items.push({
15423                 xtype :'MenuItem',
15424                 xns: Roo.bootstrap,
15425                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15426                 tagname : f,
15427                 listeners : {
15428                     click : function()
15429                     {
15430                         editorcore.insertTag(this.tagname);
15431                         editor.focus();
15432                     }
15433                 }
15434                 
15435             });
15436         });
15437          children.push(style);   
15438             
15439             
15440         btn('bold',false,true);
15441         btn('italic',false,true);
15442         btn('align-left', 'justifyleft',true);
15443         btn('align-center', 'justifycenter',true);
15444         btn('align-right' , 'justifyright',true);
15445         btn('link', false, false, function(btn) {
15446             //Roo.log("create link?");
15447             var url = prompt(this.createLinkText, this.defaultLinkValue);
15448             if(url && url != 'http:/'+'/'){
15449                 this.editorcore.relayCmd('createlink', url);
15450             }
15451         }),
15452         btn('list','insertunorderedlist',true);
15453         btn('pencil', false,true, function(btn){
15454                 Roo.log(this);
15455                 
15456                 this.toggleSourceEdit(btn.pressed);
15457         });
15458         /*
15459         var cog = {
15460                 xtype: 'Button',
15461                 size : 'sm',
15462                 xns: Roo.bootstrap,
15463                 glyphicon : 'cog',
15464                 //html : 'submit'
15465                 menu : {
15466                     xtype: 'Menu',
15467                     xns: Roo.bootstrap,
15468                     items:  []
15469                 }
15470         };
15471         
15472         cog.menu.items.push({
15473             xtype :'MenuItem',
15474             xns: Roo.bootstrap,
15475             html : Clean styles,
15476             tagname : f,
15477             listeners : {
15478                 click : function()
15479                 {
15480                     editorcore.insertTag(this.tagname);
15481                     editor.focus();
15482                 }
15483             }
15484             
15485         });
15486        */
15487         
15488          
15489        this.xtype = 'Navbar';
15490         
15491         for(var i=0;i< children.length;i++) {
15492             
15493             this.buttons.add(this.addxtypeChild(children[i]));
15494             
15495         }
15496         
15497         editor.on('editorevent', this.updateToolbar, this);
15498     },
15499     onBtnClick : function(id)
15500     {
15501        this.editorcore.relayCmd(id);
15502        this.editorcore.focus();
15503     },
15504     
15505     /**
15506      * Protected method that will not generally be called directly. It triggers
15507      * a toolbar update by reading the markup state of the current selection in the editor.
15508      */
15509     updateToolbar: function(){
15510
15511         if(!this.editorcore.activated){
15512             this.editor.onFirstFocus(); // is this neeed?
15513             return;
15514         }
15515
15516         var btns = this.buttons; 
15517         var doc = this.editorcore.doc;
15518         btns.get('bold').setActive(doc.queryCommandState('bold'));
15519         btns.get('italic').setActive(doc.queryCommandState('italic'));
15520         //btns.get('underline').setActive(doc.queryCommandState('underline'));
15521         
15522         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15523         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15524         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15525         
15526         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15527         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15528          /*
15529         
15530         var ans = this.editorcore.getAllAncestors();
15531         if (this.formatCombo) {
15532             
15533             
15534             var store = this.formatCombo.store;
15535             this.formatCombo.setValue("");
15536             for (var i =0; i < ans.length;i++) {
15537                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15538                     // select it..
15539                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15540                     break;
15541                 }
15542             }
15543         }
15544         
15545         
15546         
15547         // hides menus... - so this cant be on a menu...
15548         Roo.bootstrap.MenuMgr.hideAll();
15549         */
15550         Roo.bootstrap.MenuMgr.hideAll();
15551         //this.editorsyncValue();
15552     },
15553     onFirstFocus: function() {
15554         this.buttons.each(function(item){
15555            item.enable();
15556         });
15557     },
15558     toggleSourceEdit : function(sourceEditMode){
15559         
15560           
15561         if(sourceEditMode){
15562             Roo.log("disabling buttons");
15563            this.buttons.each( function(item){
15564                 if(item.cmd != 'pencil'){
15565                     item.disable();
15566                 }
15567             });
15568           
15569         }else{
15570             Roo.log("enabling buttons");
15571             if(this.editorcore.initialized){
15572                 this.buttons.each( function(item){
15573                     item.enable();
15574                 });
15575             }
15576             
15577         }
15578         Roo.log("calling toggole on editor");
15579         // tell the editor that it's been pressed..
15580         this.editor.toggleSourceEdit(sourceEditMode);
15581        
15582     }
15583 });
15584
15585
15586
15587
15588
15589 /**
15590  * @class Roo.bootstrap.Table.AbstractSelectionModel
15591  * @extends Roo.util.Observable
15592  * Abstract base class for grid SelectionModels.  It provides the interface that should be
15593  * implemented by descendant classes.  This class should not be directly instantiated.
15594  * @constructor
15595  */
15596 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15597     this.locked = false;
15598     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15599 };
15600
15601
15602 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
15603     /** @ignore Called by the grid automatically. Do not call directly. */
15604     init : function(grid){
15605         this.grid = grid;
15606         this.initEvents();
15607     },
15608
15609     /**
15610      * Locks the selections.
15611      */
15612     lock : function(){
15613         this.locked = true;
15614     },
15615
15616     /**
15617      * Unlocks the selections.
15618      */
15619     unlock : function(){
15620         this.locked = false;
15621     },
15622
15623     /**
15624      * Returns true if the selections are locked.
15625      * @return {Boolean}
15626      */
15627     isLocked : function(){
15628         return this.locked;
15629     }
15630 });
15631 /**
15632  * @class Roo.bootstrap.Table.ColumnModel
15633  * @extends Roo.util.Observable
15634  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15635  * the columns in the table.
15636  
15637  * @constructor
15638  * @param {Object} config An Array of column config objects. See this class's
15639  * config objects for details.
15640 */
15641 Roo.bootstrap.Table.ColumnModel = function(config){
15642         /**
15643      * The config passed into the constructor
15644      */
15645     this.config = config;
15646     this.lookup = {};
15647
15648     // if no id, create one
15649     // if the column does not have a dataIndex mapping,
15650     // map it to the order it is in the config
15651     for(var i = 0, len = config.length; i < len; i++){
15652         var c = config[i];
15653         if(typeof c.dataIndex == "undefined"){
15654             c.dataIndex = i;
15655         }
15656         if(typeof c.renderer == "string"){
15657             c.renderer = Roo.util.Format[c.renderer];
15658         }
15659         if(typeof c.id == "undefined"){
15660             c.id = Roo.id();
15661         }
15662 //        if(c.editor && c.editor.xtype){
15663 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15664 //        }
15665 //        if(c.editor && c.editor.isFormField){
15666 //            c.editor = new Roo.grid.GridEditor(c.editor);
15667 //        }
15668
15669         this.lookup[c.id] = c;
15670     }
15671
15672     /**
15673      * The width of columns which have no width specified (defaults to 100)
15674      * @type Number
15675      */
15676     this.defaultWidth = 100;
15677
15678     /**
15679      * Default sortable of columns which have no sortable specified (defaults to false)
15680      * @type Boolean
15681      */
15682     this.defaultSortable = false;
15683
15684     this.addEvents({
15685         /**
15686              * @event widthchange
15687              * Fires when the width of a column changes.
15688              * @param {ColumnModel} this
15689              * @param {Number} columnIndex The column index
15690              * @param {Number} newWidth The new width
15691              */
15692             "widthchange": true,
15693         /**
15694              * @event headerchange
15695              * Fires when the text of a header changes.
15696              * @param {ColumnModel} this
15697              * @param {Number} columnIndex The column index
15698              * @param {Number} newText The new header text
15699              */
15700             "headerchange": true,
15701         /**
15702              * @event hiddenchange
15703              * Fires when a column is hidden or "unhidden".
15704              * @param {ColumnModel} this
15705              * @param {Number} columnIndex The column index
15706              * @param {Boolean} hidden true if hidden, false otherwise
15707              */
15708             "hiddenchange": true,
15709             /**
15710          * @event columnmoved
15711          * Fires when a column is moved.
15712          * @param {ColumnModel} this
15713          * @param {Number} oldIndex
15714          * @param {Number} newIndex
15715          */
15716         "columnmoved" : true,
15717         /**
15718          * @event columlockchange
15719          * Fires when a column's locked state is changed
15720          * @param {ColumnModel} this
15721          * @param {Number} colIndex
15722          * @param {Boolean} locked true if locked
15723          */
15724         "columnlockchange" : true
15725     });
15726     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15727 };
15728 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15729     /**
15730      * @cfg {String} header The header text to display in the Grid view.
15731      */
15732     /**
15733      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15734      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15735      * specified, the column's index is used as an index into the Record's data Array.
15736      */
15737     /**
15738      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15739      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15740      */
15741     /**
15742      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15743      * Defaults to the value of the {@link #defaultSortable} property.
15744      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15745      */
15746     /**
15747      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
15748      */
15749     /**
15750      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
15751      */
15752     /**
15753      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15754      */
15755     /**
15756      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15757      */
15758     /**
15759      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15760      * given the cell's data value. See {@link #setRenderer}. If not specified, the
15761      * default renderer uses the raw data value.
15762      */
15763     /**
15764      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
15765      */
15766
15767     /**
15768      * Returns the id of the column at the specified index.
15769      * @param {Number} index The column index
15770      * @return {String} the id
15771      */
15772     getColumnId : function(index){
15773         return this.config[index].id;
15774     },
15775
15776     /**
15777      * Returns the column for a specified id.
15778      * @param {String} id The column id
15779      * @return {Object} the column
15780      */
15781     getColumnById : function(id){
15782         return this.lookup[id];
15783     },
15784
15785     
15786     /**
15787      * Returns the column for a specified dataIndex.
15788      * @param {String} dataIndex The column dataIndex
15789      * @return {Object|Boolean} the column or false if not found
15790      */
15791     getColumnByDataIndex: function(dataIndex){
15792         var index = this.findColumnIndex(dataIndex);
15793         return index > -1 ? this.config[index] : false;
15794     },
15795     
15796     /**
15797      * Returns the index for a specified column id.
15798      * @param {String} id The column id
15799      * @return {Number} the index, or -1 if not found
15800      */
15801     getIndexById : function(id){
15802         for(var i = 0, len = this.config.length; i < len; i++){
15803             if(this.config[i].id == id){
15804                 return i;
15805             }
15806         }
15807         return -1;
15808     },
15809     
15810     /**
15811      * Returns the index for a specified column dataIndex.
15812      * @param {String} dataIndex The column dataIndex
15813      * @return {Number} the index, or -1 if not found
15814      */
15815     
15816     findColumnIndex : function(dataIndex){
15817         for(var i = 0, len = this.config.length; i < len; i++){
15818             if(this.config[i].dataIndex == dataIndex){
15819                 return i;
15820             }
15821         }
15822         return -1;
15823     },
15824     
15825     
15826     moveColumn : function(oldIndex, newIndex){
15827         var c = this.config[oldIndex];
15828         this.config.splice(oldIndex, 1);
15829         this.config.splice(newIndex, 0, c);
15830         this.dataMap = null;
15831         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15832     },
15833
15834     isLocked : function(colIndex){
15835         return this.config[colIndex].locked === true;
15836     },
15837
15838     setLocked : function(colIndex, value, suppressEvent){
15839         if(this.isLocked(colIndex) == value){
15840             return;
15841         }
15842         this.config[colIndex].locked = value;
15843         if(!suppressEvent){
15844             this.fireEvent("columnlockchange", this, colIndex, value);
15845         }
15846     },
15847
15848     getTotalLockedWidth : function(){
15849         var totalWidth = 0;
15850         for(var i = 0; i < this.config.length; i++){
15851             if(this.isLocked(i) && !this.isHidden(i)){
15852                 this.totalWidth += this.getColumnWidth(i);
15853             }
15854         }
15855         return totalWidth;
15856     },
15857
15858     getLockedCount : function(){
15859         for(var i = 0, len = this.config.length; i < len; i++){
15860             if(!this.isLocked(i)){
15861                 return i;
15862             }
15863         }
15864     },
15865
15866     /**
15867      * Returns the number of columns.
15868      * @return {Number}
15869      */
15870     getColumnCount : function(visibleOnly){
15871         if(visibleOnly === true){
15872             var c = 0;
15873             for(var i = 0, len = this.config.length; i < len; i++){
15874                 if(!this.isHidden(i)){
15875                     c++;
15876                 }
15877             }
15878             return c;
15879         }
15880         return this.config.length;
15881     },
15882
15883     /**
15884      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15885      * @param {Function} fn
15886      * @param {Object} scope (optional)
15887      * @return {Array} result
15888      */
15889     getColumnsBy : function(fn, scope){
15890         var r = [];
15891         for(var i = 0, len = this.config.length; i < len; i++){
15892             var c = this.config[i];
15893             if(fn.call(scope||this, c, i) === true){
15894                 r[r.length] = c;
15895             }
15896         }
15897         return r;
15898     },
15899
15900     /**
15901      * Returns true if the specified column is sortable.
15902      * @param {Number} col The column index
15903      * @return {Boolean}
15904      */
15905     isSortable : function(col){
15906         if(typeof this.config[col].sortable == "undefined"){
15907             return this.defaultSortable;
15908         }
15909         return this.config[col].sortable;
15910     },
15911
15912     /**
15913      * Returns the rendering (formatting) function defined for the column.
15914      * @param {Number} col The column index.
15915      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15916      */
15917     getRenderer : function(col){
15918         if(!this.config[col].renderer){
15919             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15920         }
15921         return this.config[col].renderer;
15922     },
15923
15924     /**
15925      * Sets the rendering (formatting) function for a column.
15926      * @param {Number} col The column index
15927      * @param {Function} fn The function to use to process the cell's raw data
15928      * to return HTML markup for the grid view. The render function is called with
15929      * the following parameters:<ul>
15930      * <li>Data value.</li>
15931      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15932      * <li>css A CSS style string to apply to the table cell.</li>
15933      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15934      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15935      * <li>Row index</li>
15936      * <li>Column index</li>
15937      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15938      */
15939     setRenderer : function(col, fn){
15940         this.config[col].renderer = fn;
15941     },
15942
15943     /**
15944      * Returns the width for the specified column.
15945      * @param {Number} col The column index
15946      * @return {Number}
15947      */
15948     getColumnWidth : function(col){
15949         return this.config[col].width * 1 || this.defaultWidth;
15950     },
15951
15952     /**
15953      * Sets the width for a column.
15954      * @param {Number} col The column index
15955      * @param {Number} width The new width
15956      */
15957     setColumnWidth : function(col, width, suppressEvent){
15958         this.config[col].width = width;
15959         this.totalWidth = null;
15960         if(!suppressEvent){
15961              this.fireEvent("widthchange", this, col, width);
15962         }
15963     },
15964
15965     /**
15966      * Returns the total width of all columns.
15967      * @param {Boolean} includeHidden True to include hidden column widths
15968      * @return {Number}
15969      */
15970     getTotalWidth : function(includeHidden){
15971         if(!this.totalWidth){
15972             this.totalWidth = 0;
15973             for(var i = 0, len = this.config.length; i < len; i++){
15974                 if(includeHidden || !this.isHidden(i)){
15975                     this.totalWidth += this.getColumnWidth(i);
15976                 }
15977             }
15978         }
15979         return this.totalWidth;
15980     },
15981
15982     /**
15983      * Returns the header for the specified column.
15984      * @param {Number} col The column index
15985      * @return {String}
15986      */
15987     getColumnHeader : function(col){
15988         return this.config[col].header;
15989     },
15990
15991     /**
15992      * Sets the header for a column.
15993      * @param {Number} col The column index
15994      * @param {String} header The new header
15995      */
15996     setColumnHeader : function(col, header){
15997         this.config[col].header = header;
15998         this.fireEvent("headerchange", this, col, header);
15999     },
16000
16001     /**
16002      * Returns the tooltip for the specified column.
16003      * @param {Number} col The column index
16004      * @return {String}
16005      */
16006     getColumnTooltip : function(col){
16007             return this.config[col].tooltip;
16008     },
16009     /**
16010      * Sets the tooltip for a column.
16011      * @param {Number} col The column index
16012      * @param {String} tooltip The new tooltip
16013      */
16014     setColumnTooltip : function(col, tooltip){
16015             this.config[col].tooltip = tooltip;
16016     },
16017
16018     /**
16019      * Returns the dataIndex for the specified column.
16020      * @param {Number} col The column index
16021      * @return {Number}
16022      */
16023     getDataIndex : function(col){
16024         return this.config[col].dataIndex;
16025     },
16026
16027     /**
16028      * Sets the dataIndex for a column.
16029      * @param {Number} col The column index
16030      * @param {Number} dataIndex The new dataIndex
16031      */
16032     setDataIndex : function(col, dataIndex){
16033         this.config[col].dataIndex = dataIndex;
16034     },
16035
16036     
16037     
16038     /**
16039      * Returns true if the cell is editable.
16040      * @param {Number} colIndex The column index
16041      * @param {Number} rowIndex The row index
16042      * @return {Boolean}
16043      */
16044     isCellEditable : function(colIndex, rowIndex){
16045         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16046     },
16047
16048     /**
16049      * Returns the editor defined for the cell/column.
16050      * return false or null to disable editing.
16051      * @param {Number} colIndex The column index
16052      * @param {Number} rowIndex The row index
16053      * @return {Object}
16054      */
16055     getCellEditor : function(colIndex, rowIndex){
16056         return this.config[colIndex].editor;
16057     },
16058
16059     /**
16060      * Sets if a column is editable.
16061      * @param {Number} col The column index
16062      * @param {Boolean} editable True if the column is editable
16063      */
16064     setEditable : function(col, editable){
16065         this.config[col].editable = editable;
16066     },
16067
16068
16069     /**
16070      * Returns true if the column is hidden.
16071      * @param {Number} colIndex The column index
16072      * @return {Boolean}
16073      */
16074     isHidden : function(colIndex){
16075         return this.config[colIndex].hidden;
16076     },
16077
16078
16079     /**
16080      * Returns true if the column width cannot be changed
16081      */
16082     isFixed : function(colIndex){
16083         return this.config[colIndex].fixed;
16084     },
16085
16086     /**
16087      * Returns true if the column can be resized
16088      * @return {Boolean}
16089      */
16090     isResizable : function(colIndex){
16091         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16092     },
16093     /**
16094      * Sets if a column is hidden.
16095      * @param {Number} colIndex The column index
16096      * @param {Boolean} hidden True if the column is hidden
16097      */
16098     setHidden : function(colIndex, hidden){
16099         this.config[colIndex].hidden = hidden;
16100         this.totalWidth = null;
16101         this.fireEvent("hiddenchange", this, colIndex, hidden);
16102     },
16103
16104     /**
16105      * Sets the editor for a column.
16106      * @param {Number} col The column index
16107      * @param {Object} editor The editor object
16108      */
16109     setEditor : function(col, editor){
16110         this.config[col].editor = editor;
16111     }
16112 });
16113
16114 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16115         if(typeof value == "string" && value.length < 1){
16116             return "&#160;";
16117         }
16118         return value;
16119 };
16120
16121 // Alias for backwards compatibility
16122 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16123
16124 /**
16125  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16126  * @class Roo.bootstrap.Table.RowSelectionModel
16127  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16128  * It supports multiple selections and keyboard selection/navigation. 
16129  * @constructor
16130  * @param {Object} config
16131  */
16132
16133 Roo.bootstrap.Table.RowSelectionModel = function(config){
16134     Roo.apply(this, config);
16135     this.selections = new Roo.util.MixedCollection(false, function(o){
16136         return o.id;
16137     });
16138
16139     this.last = false;
16140     this.lastActive = false;
16141
16142     this.addEvents({
16143         /**
16144              * @event selectionchange
16145              * Fires when the selection changes
16146              * @param {SelectionModel} this
16147              */
16148             "selectionchange" : true,
16149         /**
16150              * @event afterselectionchange
16151              * Fires after the selection changes (eg. by key press or clicking)
16152              * @param {SelectionModel} this
16153              */
16154             "afterselectionchange" : true,
16155         /**
16156              * @event beforerowselect
16157              * Fires when a row is selected being selected, return false to cancel.
16158              * @param {SelectionModel} this
16159              * @param {Number} rowIndex The selected index
16160              * @param {Boolean} keepExisting False if other selections will be cleared
16161              */
16162             "beforerowselect" : true,
16163         /**
16164              * @event rowselect
16165              * Fires when a row is selected.
16166              * @param {SelectionModel} this
16167              * @param {Number} rowIndex The selected index
16168              * @param {Roo.data.Record} r The record
16169              */
16170             "rowselect" : true,
16171         /**
16172              * @event rowdeselect
16173              * Fires when a row is deselected.
16174              * @param {SelectionModel} this
16175              * @param {Number} rowIndex The selected index
16176              */
16177         "rowdeselect" : true
16178     });
16179     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16180     this.locked = false;
16181 };
16182
16183 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16184     /**
16185      * @cfg {Boolean} singleSelect
16186      * True to allow selection of only one row at a time (defaults to false)
16187      */
16188     singleSelect : false,
16189
16190     // private
16191     initEvents : function(){
16192
16193         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16194             this.grid.on("mousedown", this.handleMouseDown, this);
16195         }else{ // allow click to work like normal
16196             this.grid.on("rowclick", this.handleDragableRowClick, this);
16197         }
16198
16199         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16200             "up" : function(e){
16201                 if(!e.shiftKey){
16202                     this.selectPrevious(e.shiftKey);
16203                 }else if(this.last !== false && this.lastActive !== false){
16204                     var last = this.last;
16205                     this.selectRange(this.last,  this.lastActive-1);
16206                     this.grid.getView().focusRow(this.lastActive);
16207                     if(last !== false){
16208                         this.last = last;
16209                     }
16210                 }else{
16211                     this.selectFirstRow();
16212                 }
16213                 this.fireEvent("afterselectionchange", this);
16214             },
16215             "down" : function(e){
16216                 if(!e.shiftKey){
16217                     this.selectNext(e.shiftKey);
16218                 }else if(this.last !== false && this.lastActive !== false){
16219                     var last = this.last;
16220                     this.selectRange(this.last,  this.lastActive+1);
16221                     this.grid.getView().focusRow(this.lastActive);
16222                     if(last !== false){
16223                         this.last = last;
16224                     }
16225                 }else{
16226                     this.selectFirstRow();
16227                 }
16228                 this.fireEvent("afterselectionchange", this);
16229             },
16230             scope: this
16231         });
16232
16233         var view = this.grid.view;
16234         view.on("refresh", this.onRefresh, this);
16235         view.on("rowupdated", this.onRowUpdated, this);
16236         view.on("rowremoved", this.onRemove, this);
16237     },
16238
16239     // private
16240     onRefresh : function(){
16241         var ds = this.grid.dataSource, i, v = this.grid.view;
16242         var s = this.selections;
16243         s.each(function(r){
16244             if((i = ds.indexOfId(r.id)) != -1){
16245                 v.onRowSelect(i);
16246             }else{
16247                 s.remove(r);
16248             }
16249         });
16250     },
16251
16252     // private
16253     onRemove : function(v, index, r){
16254         this.selections.remove(r);
16255     },
16256
16257     // private
16258     onRowUpdated : function(v, index, r){
16259         if(this.isSelected(r)){
16260             v.onRowSelect(index);
16261         }
16262     },
16263
16264     /**
16265      * Select records.
16266      * @param {Array} records The records to select
16267      * @param {Boolean} keepExisting (optional) True to keep existing selections
16268      */
16269     selectRecords : function(records, keepExisting){
16270         if(!keepExisting){
16271             this.clearSelections();
16272         }
16273         var ds = this.grid.dataSource;
16274         for(var i = 0, len = records.length; i < len; i++){
16275             this.selectRow(ds.indexOf(records[i]), true);
16276         }
16277     },
16278
16279     /**
16280      * Gets the number of selected rows.
16281      * @return {Number}
16282      */
16283     getCount : function(){
16284         return this.selections.length;
16285     },
16286
16287     /**
16288      * Selects the first row in the grid.
16289      */
16290     selectFirstRow : function(){
16291         this.selectRow(0);
16292     },
16293
16294     /**
16295      * Select the last row.
16296      * @param {Boolean} keepExisting (optional) True to keep existing selections
16297      */
16298     selectLastRow : function(keepExisting){
16299         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16300     },
16301
16302     /**
16303      * Selects the row immediately following the last selected row.
16304      * @param {Boolean} keepExisting (optional) True to keep existing selections
16305      */
16306     selectNext : function(keepExisting){
16307         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16308             this.selectRow(this.last+1, keepExisting);
16309             this.grid.getView().focusRow(this.last);
16310         }
16311     },
16312
16313     /**
16314      * Selects the row that precedes the last selected row.
16315      * @param {Boolean} keepExisting (optional) True to keep existing selections
16316      */
16317     selectPrevious : function(keepExisting){
16318         if(this.last){
16319             this.selectRow(this.last-1, keepExisting);
16320             this.grid.getView().focusRow(this.last);
16321         }
16322     },
16323
16324     /**
16325      * Returns the selected records
16326      * @return {Array} Array of selected records
16327      */
16328     getSelections : function(){
16329         return [].concat(this.selections.items);
16330     },
16331
16332     /**
16333      * Returns the first selected record.
16334      * @return {Record}
16335      */
16336     getSelected : function(){
16337         return this.selections.itemAt(0);
16338     },
16339
16340
16341     /**
16342      * Clears all selections.
16343      */
16344     clearSelections : function(fast){
16345         if(this.locked) return;
16346         if(fast !== true){
16347             var ds = this.grid.dataSource;
16348             var s = this.selections;
16349             s.each(function(r){
16350                 this.deselectRow(ds.indexOfId(r.id));
16351             }, this);
16352             s.clear();
16353         }else{
16354             this.selections.clear();
16355         }
16356         this.last = false;
16357     },
16358
16359
16360     /**
16361      * Selects all rows.
16362      */
16363     selectAll : function(){
16364         if(this.locked) return;
16365         this.selections.clear();
16366         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16367             this.selectRow(i, true);
16368         }
16369     },
16370
16371     /**
16372      * Returns True if there is a selection.
16373      * @return {Boolean}
16374      */
16375     hasSelection : function(){
16376         return this.selections.length > 0;
16377     },
16378
16379     /**
16380      * Returns True if the specified row is selected.
16381      * @param {Number/Record} record The record or index of the record to check
16382      * @return {Boolean}
16383      */
16384     isSelected : function(index){
16385         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16386         return (r && this.selections.key(r.id) ? true : false);
16387     },
16388
16389     /**
16390      * Returns True if the specified record id is selected.
16391      * @param {String} id The id of record to check
16392      * @return {Boolean}
16393      */
16394     isIdSelected : function(id){
16395         return (this.selections.key(id) ? true : false);
16396     },
16397
16398     // private
16399     handleMouseDown : function(e, t){
16400         var view = this.grid.getView(), rowIndex;
16401         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16402             return;
16403         };
16404         if(e.shiftKey && this.last !== false){
16405             var last = this.last;
16406             this.selectRange(last, rowIndex, e.ctrlKey);
16407             this.last = last; // reset the last
16408             view.focusRow(rowIndex);
16409         }else{
16410             var isSelected = this.isSelected(rowIndex);
16411             if(e.button !== 0 && isSelected){
16412                 view.focusRow(rowIndex);
16413             }else if(e.ctrlKey && isSelected){
16414                 this.deselectRow(rowIndex);
16415             }else if(!isSelected){
16416                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16417                 view.focusRow(rowIndex);
16418             }
16419         }
16420         this.fireEvent("afterselectionchange", this);
16421     },
16422     // private
16423     handleDragableRowClick :  function(grid, rowIndex, e) 
16424     {
16425         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16426             this.selectRow(rowIndex, false);
16427             grid.view.focusRow(rowIndex);
16428              this.fireEvent("afterselectionchange", this);
16429         }
16430     },
16431     
16432     /**
16433      * Selects multiple rows.
16434      * @param {Array} rows Array of the indexes of the row to select
16435      * @param {Boolean} keepExisting (optional) True to keep existing selections
16436      */
16437     selectRows : function(rows, keepExisting){
16438         if(!keepExisting){
16439             this.clearSelections();
16440         }
16441         for(var i = 0, len = rows.length; i < len; i++){
16442             this.selectRow(rows[i], true);
16443         }
16444     },
16445
16446     /**
16447      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16448      * @param {Number} startRow The index of the first row in the range
16449      * @param {Number} endRow The index of the last row in the range
16450      * @param {Boolean} keepExisting (optional) True to retain existing selections
16451      */
16452     selectRange : function(startRow, endRow, keepExisting){
16453         if(this.locked) return;
16454         if(!keepExisting){
16455             this.clearSelections();
16456         }
16457         if(startRow <= endRow){
16458             for(var i = startRow; i <= endRow; i++){
16459                 this.selectRow(i, true);
16460             }
16461         }else{
16462             for(var i = startRow; i >= endRow; i--){
16463                 this.selectRow(i, true);
16464             }
16465         }
16466     },
16467
16468     /**
16469      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16470      * @param {Number} startRow The index of the first row in the range
16471      * @param {Number} endRow The index of the last row in the range
16472      */
16473     deselectRange : function(startRow, endRow, preventViewNotify){
16474         if(this.locked) return;
16475         for(var i = startRow; i <= endRow; i++){
16476             this.deselectRow(i, preventViewNotify);
16477         }
16478     },
16479
16480     /**
16481      * Selects a row.
16482      * @param {Number} row The index of the row to select
16483      * @param {Boolean} keepExisting (optional) True to keep existing selections
16484      */
16485     selectRow : function(index, keepExisting, preventViewNotify){
16486         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16487         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16488             if(!keepExisting || this.singleSelect){
16489                 this.clearSelections();
16490             }
16491             var r = this.grid.dataSource.getAt(index);
16492             this.selections.add(r);
16493             this.last = this.lastActive = index;
16494             if(!preventViewNotify){
16495                 this.grid.getView().onRowSelect(index);
16496             }
16497             this.fireEvent("rowselect", this, index, r);
16498             this.fireEvent("selectionchange", this);
16499         }
16500     },
16501
16502     /**
16503      * Deselects a row.
16504      * @param {Number} row The index of the row to deselect
16505      */
16506     deselectRow : function(index, preventViewNotify){
16507         if(this.locked) return;
16508         if(this.last == index){
16509             this.last = false;
16510         }
16511         if(this.lastActive == index){
16512             this.lastActive = false;
16513         }
16514         var r = this.grid.dataSource.getAt(index);
16515         this.selections.remove(r);
16516         if(!preventViewNotify){
16517             this.grid.getView().onRowDeselect(index);
16518         }
16519         this.fireEvent("rowdeselect", this, index);
16520         this.fireEvent("selectionchange", this);
16521     },
16522
16523     // private
16524     restoreLast : function(){
16525         if(this._last){
16526             this.last = this._last;
16527         }
16528     },
16529
16530     // private
16531     acceptsNav : function(row, col, cm){
16532         return !cm.isHidden(col) && cm.isCellEditable(col, row);
16533     },
16534
16535     // private
16536     onEditorKey : function(field, e){
16537         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16538         if(k == e.TAB){
16539             e.stopEvent();
16540             ed.completeEdit();
16541             if(e.shiftKey){
16542                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16543             }else{
16544                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16545             }
16546         }else if(k == e.ENTER && !e.ctrlKey){
16547             e.stopEvent();
16548             ed.completeEdit();
16549             if(e.shiftKey){
16550                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16551             }else{
16552                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16553             }
16554         }else if(k == e.ESC){
16555             ed.cancelEdit();
16556         }
16557         if(newCell){
16558             g.startEditing(newCell[0], newCell[1]);
16559         }
16560     }
16561 });/*
16562  * - LGPL
16563  *
16564  * element
16565  * 
16566  */
16567
16568 /**
16569  * @class Roo.bootstrap.MessageBar
16570  * @extends Roo.bootstrap.Component
16571  * Bootstrap MessageBar class
16572  * @cfg {String} html contents of the MessageBar
16573  * @cfg {String} weight (info | success | warning | danger) default info
16574  * @cfg {String} beforeClass insert the bar before the given class
16575  * @cfg {Boolean} closable (true | false) default false
16576  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16577  * 
16578  * @constructor
16579  * Create a new Element
16580  * @param {Object} config The config object
16581  */
16582
16583 Roo.bootstrap.MessageBar = function(config){
16584     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16585 };
16586
16587 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
16588     
16589     html: '',
16590     weight: 'info',
16591     closable: false,
16592     fixed: false,
16593     beforeClass: 'bootstrap-sticky-wrap',
16594     
16595     getAutoCreate : function(){
16596         
16597         var cfg = {
16598             tag: 'div',
16599             cls: 'alert alert-dismissable alert-' + this.weight,
16600             cn: [
16601                 {
16602                     tag: 'span',
16603                     cls: 'message',
16604                     html: this.html || ''
16605                 }
16606             ]
16607         }
16608         
16609         if(this.fixed){
16610             cfg.cls += ' alert-messages-fixed';
16611         }
16612         
16613         if(this.closable){
16614             cfg.cn.push({
16615                 tag: 'button',
16616                 cls: 'close',
16617                 html: 'x'
16618             });
16619         }
16620         
16621         return cfg;
16622     },
16623     
16624     onRender : function(ct, position)
16625     {
16626         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16627         
16628         if(!this.el){
16629             var cfg = Roo.apply({},  this.getAutoCreate());
16630             cfg.id = Roo.id();
16631             
16632             if (this.cls) {
16633                 cfg.cls += ' ' + this.cls;
16634             }
16635             if (this.style) {
16636                 cfg.style = this.style;
16637             }
16638             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16639             
16640             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16641         }
16642         
16643         this.el.select('>button.close').on('click', this.hide, this);
16644         
16645     },
16646     
16647     show : function()
16648     {
16649         if (!this.rendered) {
16650             this.render();
16651         }
16652         
16653         this.el.show();
16654         
16655         this.fireEvent('show', this);
16656         
16657     },
16658     
16659     hide : function()
16660     {
16661         if (!this.rendered) {
16662             this.render();
16663         }
16664         
16665         this.el.hide();
16666         
16667         this.fireEvent('hide', this);
16668     },
16669     
16670     update : function()
16671     {
16672 //        var e = this.el.dom.firstChild;
16673 //        
16674 //        if(this.closable){
16675 //            e = e.nextSibling;
16676 //        }
16677 //        
16678 //        e.data = this.html || '';
16679
16680         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16681     }
16682    
16683 });
16684
16685  
16686
16687