Roo/bootstrap/Button.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472      
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = '<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: 'img-responsive',
984             html : null
985         }
986         
987         cfg.html = this.html || cfg.html;
988         
989         cfg.src = this.src || cfg.src;
990         
991         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
992             cfg.cls += ' img-' + this.border;
993         }
994         
995         if(this.alt){
996             cfg.alt = this.alt;
997         }
998         
999         if(this.href){
1000             var a = {
1001                 tag: 'a',
1002                 href: this.href,
1003                 cn: [
1004                     cfg
1005                 ]
1006             }
1007             
1008             if(this.target){
1009                 a.target = this.target;
1010             }
1011             
1012         }
1013         
1014         
1015         return (this.href) ? a : cfg;
1016     },
1017     
1018     initEvents: function() {
1019         
1020         if(!this.href){
1021             this.el.on('click', this.onClick, this);
1022         }
1023     },
1024     
1025     onClick : function(e)
1026     {
1027         Roo.log('img onclick');
1028         this.fireEvent('click', this, e);
1029     }
1030    
1031 });
1032
1033  /*
1034  * - LGPL
1035  *
1036  * header
1037  * 
1038  */
1039
1040 /**
1041  * @class Roo.bootstrap.Header
1042  * @extends Roo.bootstrap.Component
1043  * Bootstrap Header class
1044  * @cfg {String} html content of header
1045  * @cfg {Number} level (1|2|3|4|5|6) default 1
1046  * 
1047  * @constructor
1048  * Create a new Header
1049  * @param {Object} config The config object
1050  */
1051
1052
1053 Roo.bootstrap.Header  = function(config){
1054     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1058     
1059     //href : false,
1060     html : false,
1061     level : 1,
1062     
1063     
1064     
1065     getAutoCreate : function(){
1066         
1067         var cfg = {
1068             tag: 'h' + (1 *this.level),
1069             html: this.html || 'fill in html'
1070         } ;
1071         
1072         return cfg;
1073     }
1074    
1075 });
1076
1077  
1078
1079  /*
1080  * Based on:
1081  * Ext JS Library 1.1.1
1082  * Copyright(c) 2006-2007, Ext JS, LLC.
1083  *
1084  * Originally Released Under LGPL - original licence link has changed is not relivant.
1085  *
1086  * Fork - LGPL
1087  * <script type="text/javascript">
1088  */
1089  
1090 /**
1091  * @class Roo.bootstrap.MenuMgr
1092  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1093  * @singleton
1094  */
1095 Roo.bootstrap.MenuMgr = function(){
1096    var menus, active, groups = {}, attached = false, lastShow = new Date();
1097
1098    // private - called when first menu is created
1099    function init(){
1100        menus = {};
1101        active = new Roo.util.MixedCollection();
1102        Roo.get(document).addKeyListener(27, function(){
1103            if(active.length > 0){
1104                hideAll();
1105            }
1106        });
1107    }
1108
1109    // private
1110    function hideAll(){
1111        if(active && active.length > 0){
1112            var c = active.clone();
1113            c.each(function(m){
1114                m.hide();
1115            });
1116        }
1117    }
1118
1119    // private
1120    function onHide(m){
1121        active.remove(m);
1122        if(active.length < 1){
1123            Roo.get(document).un("mouseup", onMouseDown);
1124             
1125            attached = false;
1126        }
1127    }
1128
1129    // private
1130    function onShow(m){
1131        var last = active.last();
1132        lastShow = new Date();
1133        active.add(m);
1134        if(!attached){
1135           Roo.get(document).on("mouseup", onMouseDown);
1136            
1137            attached = true;
1138        }
1139        if(m.parentMenu){
1140           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1141           m.parentMenu.activeChild = m;
1142        }else if(last && last.isVisible()){
1143           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1144        }
1145    }
1146
1147    // private
1148    function onBeforeHide(m){
1149        if(m.activeChild){
1150            m.activeChild.hide();
1151        }
1152        if(m.autoHideTimer){
1153            clearTimeout(m.autoHideTimer);
1154            delete m.autoHideTimer;
1155        }
1156    }
1157
1158    // private
1159    function onBeforeShow(m){
1160        var pm = m.parentMenu;
1161        if(!pm && !m.allowOtherMenus){
1162            hideAll();
1163        }else if(pm && pm.activeChild && active != m){
1164            pm.activeChild.hide();
1165        }
1166    }
1167
1168    // private
1169    function onMouseDown(e){
1170         Roo.log("on MouseDown");
1171         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1172            hideAll();
1173         }
1174         
1175         
1176    }
1177
1178    // private
1179    function onBeforeCheck(mi, state){
1180        if(state){
1181            var g = groups[mi.group];
1182            for(var i = 0, l = g.length; i < l; i++){
1183                if(g[i] != mi){
1184                    g[i].setChecked(false);
1185                }
1186            }
1187        }
1188    }
1189
1190    return {
1191
1192        /**
1193         * Hides all menus that are currently visible
1194         */
1195        hideAll : function(){
1196             hideAll();  
1197        },
1198
1199        // private
1200        register : function(menu){
1201            if(!menus){
1202                init();
1203            }
1204            menus[menu.id] = menu;
1205            menu.on("beforehide", onBeforeHide);
1206            menu.on("hide", onHide);
1207            menu.on("beforeshow", onBeforeShow);
1208            menu.on("show", onShow);
1209            var g = menu.group;
1210            if(g && menu.events["checkchange"]){
1211                if(!groups[g]){
1212                    groups[g] = [];
1213                }
1214                groups[g].push(menu);
1215                menu.on("checkchange", onCheck);
1216            }
1217        },
1218
1219         /**
1220          * Returns a {@link Roo.menu.Menu} object
1221          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1222          * be used to generate and return a new Menu instance.
1223          */
1224        get : function(menu){
1225            if(typeof menu == "string"){ // menu id
1226                return menus[menu];
1227            }else if(menu.events){  // menu instance
1228                return menu;
1229            }
1230            /*else if(typeof menu.length == 'number'){ // array of menu items?
1231                return new Roo.bootstrap.Menu({items:menu});
1232            }else{ // otherwise, must be a config
1233                return new Roo.bootstrap.Menu(menu);
1234            }
1235            */
1236            return false;
1237        },
1238
1239        // private
1240        unregister : function(menu){
1241            delete menus[menu.id];
1242            menu.un("beforehide", onBeforeHide);
1243            menu.un("hide", onHide);
1244            menu.un("beforeshow", onBeforeShow);
1245            menu.un("show", onShow);
1246            var g = menu.group;
1247            if(g && menu.events["checkchange"]){
1248                groups[g].remove(menu);
1249                menu.un("checkchange", onCheck);
1250            }
1251        },
1252
1253        // private
1254        registerCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                if(!groups[g]){
1258                    groups[g] = [];
1259                }
1260                groups[g].push(menuItem);
1261                menuItem.on("beforecheckchange", onBeforeCheck);
1262            }
1263        },
1264
1265        // private
1266        unregisterCheckable : function(menuItem){
1267            var g = menuItem.group;
1268            if(g){
1269                groups[g].remove(menuItem);
1270                menuItem.un("beforecheckchange", onBeforeCheck);
1271            }
1272        }
1273    };
1274 }();/*
1275  * - LGPL
1276  *
1277  * menu
1278  * 
1279  */
1280
1281 /**
1282  * @class Roo.bootstrap.Menu
1283  * @extends Roo.bootstrap.Component
1284  * Bootstrap Menu class - container for MenuItems
1285  * @cfg {String} type type of menu
1286  * 
1287  * @constructor
1288  * Create a new Menu
1289  * @param {Object} config The config object
1290  */
1291
1292
1293 Roo.bootstrap.Menu = function(config){
1294     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1295     if (this.registerMenu) {
1296         Roo.bootstrap.MenuMgr.register(this);
1297     }
1298     this.addEvents({
1299         /**
1300          * @event beforeshow
1301          * Fires before this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         beforeshow : true,
1305         /**
1306          * @event beforehide
1307          * Fires before this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         beforehide : true,
1311         /**
1312          * @event show
1313          * Fires after this menu is displayed
1314          * @param {Roo.menu.Menu} this
1315          */
1316         show : true,
1317         /**
1318          * @event hide
1319          * Fires after this menu is hidden
1320          * @param {Roo.menu.Menu} this
1321          */
1322         hide : true,
1323         /**
1324          * @event click
1325          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1326          * @param {Roo.menu.Menu} this
1327          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1328          * @param {Roo.EventObject} e
1329          */
1330         click : true,
1331         /**
1332          * @event mouseover
1333          * Fires when the mouse is hovering over this menu
1334          * @param {Roo.menu.Menu} this
1335          * @param {Roo.EventObject} e
1336          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337          */
1338         mouseover : true,
1339         /**
1340          * @event mouseout
1341          * Fires when the mouse exits this menu
1342          * @param {Roo.menu.Menu} this
1343          * @param {Roo.EventObject} e
1344          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1345          */
1346         mouseout : true,
1347         /**
1348          * @event itemclick
1349          * Fires when a menu item contained in this menu is clicked
1350          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1351          * @param {Roo.EventObject} e
1352          */
1353         itemclick: true
1354     });
1355     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1356 };
1357
1358 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1359     
1360    /// html : false,
1361     //align : '',
1362     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1363     type: false,
1364     /**
1365      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1366      */
1367     registerMenu : true,
1368     
1369     menuItems :false, // stores the menu items..
1370     
1371     hidden:true,
1372     
1373     parentMenu : false,
1374     
1375     getChildContainer : function() {
1376         return this.el;  
1377     },
1378     
1379     getAutoCreate : function(){
1380          
1381         //if (['right'].indexOf(this.align)!==-1) {
1382         //    cfg.cn[1].cls += ' pull-right'
1383         //}
1384         var cfg = {
1385             tag : 'ul',
1386             cls : 'dropdown-menu' ,
1387             style : 'z-index:1000'
1388             
1389         }
1390         
1391         if (this.type === 'submenu') {
1392             cfg.cls = 'submenu active'
1393         }
1394         
1395         return cfg;
1396     },
1397     initEvents : function() {
1398         
1399        // Roo.log("ADD event");
1400        // Roo.log(this.triggerEl.dom);
1401         this.triggerEl.on('click', this.onTriggerPress, this);
1402         this.triggerEl.addClass('dropdown-toggle');
1403         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1404
1405         this.el.on("mouseover", this.onMouseOver, this);
1406         this.el.on("mouseout", this.onMouseOut, this);
1407         
1408         
1409     },
1410     findTargetItem : function(e){
1411         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1412         if(!t){
1413             return false;
1414         }
1415         //Roo.log(t);         Roo.log(t.id);
1416         if(t && t.id){
1417             //Roo.log(this.menuitems);
1418             return this.menuitems.get(t.id);
1419             
1420             //return this.items.get(t.menuItemId);
1421         }
1422         
1423         return false;
1424     },
1425     onClick : function(e){
1426         Roo.log("menu.onClick");
1427         var t = this.findTargetItem(e);
1428         if(!t){
1429             return;
1430         }
1431         Roo.log(e);
1432         /*
1433         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1434             if(t == this.activeItem && t.shouldDeactivate(e)){
1435                 this.activeItem.deactivate();
1436                 delete this.activeItem;
1437                 return;
1438             }
1439             if(t.canActivate){
1440                 this.setActiveItem(t, true);
1441             }
1442             return;
1443             
1444             
1445         }
1446         */
1447         Roo.log('pass click event');
1448         
1449         t.onClick(e);
1450         
1451         this.fireEvent("click", this, t, e);
1452         
1453         this.hide();
1454     },
1455      onMouseOver : function(e){
1456         var t  = this.findTargetItem(e);
1457         //Roo.log(t);
1458         //if(t){
1459         //    if(t.canActivate && !t.disabled){
1460         //        this.setActiveItem(t, true);
1461         //    }
1462         //}
1463         
1464         this.fireEvent("mouseover", this, e, t);
1465     },
1466     isVisible : function(){
1467         return !this.hidden;
1468     },
1469      onMouseOut : function(e){
1470         var t  = this.findTargetItem(e);
1471         
1472         //if(t ){
1473         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1474         //        this.activeItem.deactivate();
1475         //        delete this.activeItem;
1476         //    }
1477         //}
1478         this.fireEvent("mouseout", this, e, t);
1479     },
1480     
1481     
1482     /**
1483      * Displays this menu relative to another element
1484      * @param {String/HTMLElement/Roo.Element} element The element to align to
1485      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1486      * the element (defaults to this.defaultAlign)
1487      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1488      */
1489     show : function(el, pos, parentMenu){
1490         this.parentMenu = parentMenu;
1491         if(!this.el){
1492             this.render();
1493         }
1494         this.fireEvent("beforeshow", this);
1495         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1496     },
1497      /**
1498      * Displays this menu at a specific xy position
1499      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1500      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1501      */
1502     showAt : function(xy, parentMenu, /* private: */_e){
1503         this.parentMenu = parentMenu;
1504         if(!this.el){
1505             this.render();
1506         }
1507         if(_e !== false){
1508             this.fireEvent("beforeshow", this);
1509             
1510             //xy = this.el.adjustForConstraints(xy);
1511         }
1512         //this.el.setXY(xy);
1513         //this.el.show();
1514         this.hideMenuItems();
1515         this.hidden = false;
1516         this.triggerEl.addClass('open');
1517         this.focus();
1518         this.fireEvent("show", this);
1519     },
1520     
1521     focus : function(){
1522         return;
1523         if(!this.hidden){
1524             this.doFocus.defer(50, this);
1525         }
1526     },
1527
1528     doFocus : function(){
1529         if(!this.hidden){
1530             this.focusEl.focus();
1531         }
1532     },
1533
1534     /**
1535      * Hides this menu and optionally all parent menus
1536      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1537      */
1538     hide : function(deep){
1539         
1540         this.hideMenuItems();
1541         if(this.el && this.isVisible()){
1542             this.fireEvent("beforehide", this);
1543             if(this.activeItem){
1544                 this.activeItem.deactivate();
1545                 this.activeItem = null;
1546             }
1547             this.triggerEl.removeClass('open');;
1548             this.hidden = true;
1549             this.fireEvent("hide", this);
1550         }
1551         if(deep === true && this.parentMenu){
1552             this.parentMenu.hide(true);
1553         }
1554     },
1555     
1556     onTriggerPress  : function(e)
1557     {
1558         
1559         Roo.log('trigger press');
1560         //Roo.log(e.getTarget());
1561        // Roo.log(this.triggerEl.dom);
1562         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1563             return;
1564         }
1565         if (this.isVisible()) {
1566             Roo.log('hide');
1567             this.hide();
1568         } else {
1569             this.show(this.triggerEl, false, false);
1570         }
1571         
1572         
1573     },
1574     
1575          
1576        
1577     
1578     hideMenuItems : function()
1579     {
1580         //$(backdrop).remove()
1581         Roo.select('.open',true).each(function(aa) {
1582             
1583             aa.removeClass('open');
1584           //var parent = getParent($(this))
1585           //var relatedTarget = { relatedTarget: this }
1586           
1587            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1588           //if (e.isDefaultPrevented()) return
1589            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1590         })
1591     },
1592     addxtypeChild : function (tree, cntr) {
1593         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1594           
1595         this.menuitems.add(comp);
1596         return comp;
1597
1598     },
1599     getEl : function()
1600     {
1601         Roo.log(this.el);
1602         return this.el;
1603     }
1604 });
1605
1606  
1607  /*
1608  * - LGPL
1609  *
1610  * menu item
1611  * 
1612  */
1613
1614
1615 /**
1616  * @class Roo.bootstrap.MenuItem
1617  * @extends Roo.bootstrap.Component
1618  * Bootstrap MenuItem class
1619  * @cfg {String} html the menu label
1620  * @cfg {String} href the link
1621  * @cfg {Boolean} preventDefault (true | false) default true
1622  * 
1623  * 
1624  * @constructor
1625  * Create a new MenuItem
1626  * @param {Object} config The config object
1627  */
1628
1629
1630 Roo.bootstrap.MenuItem = function(config){
1631     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1632     this.addEvents({
1633         // raw events
1634         /**
1635          * @event click
1636          * The raw click event for the entire grid.
1637          * @param {Roo.EventObject} e
1638          */
1639         "click" : true
1640     });
1641 };
1642
1643 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1644     
1645     href : false,
1646     html : false,
1647     preventDefault: true,
1648     
1649     getAutoCreate : function(){
1650         var cfg= {
1651             tag: 'li',
1652         cls: 'dropdown-menu-item',
1653             cn: [
1654             {
1655                 tag : 'a',
1656                 href : '#',
1657                 html : 'Link'
1658             }
1659             ]
1660     };
1661         
1662         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1663         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1664         return cfg;
1665     },
1666     
1667     initEvents: function() {
1668         
1669         //this.el.select('a').on('click', this.onClick, this);
1670         
1671     },
1672     onClick : function(e)
1673     {
1674         Roo.log('item on click ');
1675         //if(this.preventDefault){
1676         //    e.preventDefault();
1677         //}
1678         //this.parent().hideMenuItems();
1679         
1680         this.fireEvent('click', this, e);
1681     },
1682     getEl : function()
1683     {
1684         return this.el;
1685     }
1686 });
1687
1688  
1689
1690  /*
1691  * - LGPL
1692  *
1693  * menu separator
1694  * 
1695  */
1696
1697
1698 /**
1699  * @class Roo.bootstrap.MenuSeparator
1700  * @extends Roo.bootstrap.Component
1701  * Bootstrap MenuSeparator class
1702  * 
1703  * @constructor
1704  * Create a new MenuItem
1705  * @param {Object} config The config object
1706  */
1707
1708
1709 Roo.bootstrap.MenuSeparator = function(config){
1710     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1711 };
1712
1713 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1714     
1715     getAutoCreate : function(){
1716         var cfg = {
1717             cls: 'divider',
1718             tag : 'li'
1719         };
1720         
1721         return cfg;
1722     }
1723    
1724 });
1725
1726  
1727
1728  
1729 /*
1730 <div class="modal fade">
1731   <div class="modal-dialog">
1732     <div class="modal-content">
1733       <div class="modal-header">
1734         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1735         <h4 class="modal-title">Modal title</h4>
1736       </div>
1737       <div class="modal-body">
1738         <p>One fine body&hellip;</p>
1739       </div>
1740       <div class="modal-footer">
1741         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1742         <button type="button" class="btn btn-primary">Save changes</button>
1743       </div>
1744     </div><!-- /.modal-content -->
1745   </div><!-- /.modal-dialog -->
1746 </div><!-- /.modal -->
1747 */
1748 /*
1749  * - LGPL
1750  *
1751  * page contgainer.
1752  * 
1753  */
1754
1755 /**
1756  * @class Roo.bootstrap.Modal
1757  * @extends Roo.bootstrap.Component
1758  * Bootstrap Modal class
1759  * @cfg {String} title Title of dialog
1760  * @cfg {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     
10473  * @constructor
10474  * Create a new Container
10475  * @param {Object} config The config object
10476  */
10477
10478
10479
10480 Roo.bootstrap.Calendar = function(config){
10481     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10482      this.addEvents({
10483         /**
10484              * @event select
10485              * Fires when a date is selected
10486              * @param {DatePicker} this
10487              * @param {Date} date The selected date
10488              */
10489         'select': true,
10490         /**
10491              * @event monthchange
10492              * Fires when the displayed month changes 
10493              * @param {DatePicker} this
10494              * @param {Date} date The selected month
10495              */
10496         'monthchange': true,
10497         /**
10498              * @event evententer
10499              * Fires when mouse over an event
10500              * @param {Calendar} this
10501              * @param {event} Event
10502              */
10503         'evententer': true,
10504         /**
10505              * @event eventleave
10506              * Fires when the mouse leaves an
10507              * @param {Calendar} this
10508              * @param {event}
10509              */
10510         'eventleave': true,
10511         /**
10512              * @event eventclick
10513              * Fires when the mouse click an
10514              * @param {Calendar} this
10515              * @param {event}
10516              */
10517         'eventclick': true
10518         
10519     });
10520
10521 };
10522
10523 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10524     
10525      /**
10526      * @cfg {Number} startDay
10527      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10528      */
10529     startDay : 0,
10530     
10531     loadMask : false,
10532       
10533     getAutoCreate : function(){
10534         
10535         
10536         var fc_button = function(name, corner, style, content ) {
10537             return Roo.apply({},{
10538                 tag : 'span',
10539                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10540                          (corner.length ?
10541                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10542                             ''
10543                         ),
10544                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10545                 unselectable: 'on'
10546             });
10547         };
10548         
10549         var header = {
10550             tag : 'table',
10551             cls : 'fc-header',
10552             style : 'width:100%',
10553             cn : [
10554                 {
10555                     tag: 'tr',
10556                     cn : [
10557                         {
10558                             tag : 'td',
10559                             cls : 'fc-header-left',
10560                             cn : [
10561                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
10562                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
10563                                 { tag: 'span', cls: 'fc-header-space' },
10564                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
10565                                 
10566                                 
10567                             ]
10568                         },
10569                         
10570                         {
10571                             tag : 'td',
10572                             cls : 'fc-header-center',
10573                             cn : [
10574                                 {
10575                                     tag: 'span',
10576                                     cls: 'fc-header-title',
10577                                     cn : {
10578                                         tag: 'H2',
10579                                         html : 'month / year'
10580                                     }
10581                                 }
10582                                 
10583                             ]
10584                         },
10585                         {
10586                             tag : 'td',
10587                             cls : 'fc-header-right',
10588                             cn : [
10589                           /*      fc_button('month', 'left', '', 'month' ),
10590                                 fc_button('week', '', '', 'week' ),
10591                                 fc_button('day', 'right', '', 'day' )
10592                             */    
10593                                 
10594                             ]
10595                         }
10596                         
10597                     ]
10598                 }
10599             ]
10600         };
10601         
10602        
10603         var cal_heads = function() {
10604             var ret = [];
10605             // fixme - handle this.
10606             
10607             for (var i =0; i < Date.dayNames.length; i++) {
10608                 var d = Date.dayNames[i];
10609                 ret.push({
10610                     tag: 'th',
10611                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10612                     html : d.substring(0,3)
10613                 });
10614                 
10615             }
10616             ret[0].cls += ' fc-first';
10617             ret[6].cls += ' fc-last';
10618             return ret;
10619         };
10620         var cal_cell = function(n) {
10621             return  {
10622                 tag: 'td',
10623                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10624                 cn : [
10625                     {
10626                         cn : [
10627                             {
10628                                 cls: 'fc-day-number',
10629                                 html: 'D'
10630                             },
10631                             {
10632                                 cls: 'fc-day-content',
10633                              
10634                                 cn : [
10635                                      {
10636                                         style: 'position: relative;' // height: 17px;
10637                                     }
10638                                 ]
10639                             }
10640                             
10641                             
10642                         ]
10643                     }
10644                 ]
10645                 
10646             }
10647         };
10648         var cal_rows = function() {
10649             
10650             var ret = []
10651             for (var r = 0; r < 6; r++) {
10652                 var row= {
10653                     tag : 'tr',
10654                     cls : 'fc-week',
10655                     cn : []
10656                 };
10657                 
10658                 for (var i =0; i < Date.dayNames.length; i++) {
10659                     var d = Date.dayNames[i];
10660                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10661
10662                 }
10663                 row.cn[0].cls+=' fc-first';
10664                 row.cn[0].cn[0].style = 'min-height:90px';
10665                 row.cn[6].cls+=' fc-last';
10666                 ret.push(row);
10667                 
10668             }
10669             ret[0].cls += ' fc-first';
10670             ret[4].cls += ' fc-prev-last';
10671             ret[5].cls += ' fc-last';
10672             return ret;
10673             
10674         };
10675         
10676         var cal_table = {
10677             tag: 'table',
10678             cls: 'fc-border-separate',
10679             style : 'width:100%',
10680             cellspacing  : 0,
10681             cn : [
10682                 { 
10683                     tag: 'thead',
10684                     cn : [
10685                         { 
10686                             tag: 'tr',
10687                             cls : 'fc-first fc-last',
10688                             cn : cal_heads()
10689                         }
10690                     ]
10691                 },
10692                 { 
10693                     tag: 'tbody',
10694                     cn : cal_rows()
10695                 }
10696                   
10697             ]
10698         };
10699          
10700          var cfg = {
10701             cls : 'fc fc-ltr',
10702             cn : [
10703                 header,
10704                 {
10705                     cls : 'fc-content',
10706                     style : "position: relative;",
10707                     cn : [
10708                         {
10709                             cls : 'fc-view fc-view-month fc-grid',
10710                             style : 'position: relative',
10711                             unselectable : 'on',
10712                             cn : [
10713                                 {
10714                                     cls : 'fc-event-container',
10715                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10716                                 },
10717                                 cal_table
10718                             ]
10719                         }
10720                     ]
10721     
10722                 }
10723            ] 
10724             
10725         };
10726         
10727          
10728         
10729         return cfg;
10730     },
10731     
10732     
10733     initEvents : function()
10734     {
10735         if(!this.store){
10736             throw "can not find store for calendar";
10737         }
10738         
10739         var mark = {
10740             tag: "div",
10741             cls:"x-dlg-mask",
10742             style: "text-align:center",
10743             cn: [
10744                 {
10745                     tag: "div",
10746                     style: "background-color:white;width:50%;margin:250 auto",
10747                     cn: [
10748                         {
10749                             tag: "img",
10750                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10751                         },
10752                         {
10753                             tag: "span",
10754                             html: "Loading"
10755                         }
10756                         
10757                     ]
10758                 }
10759             ]
10760         }
10761         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10762         
10763         var size = this.el.select('.fc-content', true).first().getSize();
10764         this.maskEl.setSize(size.width, size.height);
10765         this.maskEl.enableDisplayMode("block");
10766         if(!this.loadMask){
10767             this.maskEl.hide();
10768         }
10769         
10770         this.store = Roo.factory(this.store, Roo.data);
10771         this.store.on('load', this.onLoad, this);
10772         this.store.on('beforeload', this.onBeforeLoad, this);
10773         
10774         this.resize();
10775         
10776         this.cells = this.el.select('.fc-day',true);
10777         //Roo.log(this.cells);
10778         this.textNodes = this.el.query('.fc-day-number');
10779         this.cells.addClassOnOver('fc-state-hover');
10780         
10781         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10782         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10783         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10784         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10785         
10786         this.on('monthchange', this.onMonthChange, this);
10787         
10788         this.update(new Date().clearTime());
10789     },
10790     
10791     resize : function() {
10792         var sz  = this.el.getSize();
10793         
10794         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10795         this.el.select('.fc-day-content div',true).setHeight(34);
10796     },
10797     
10798     
10799     // private
10800     showPrevMonth : function(e){
10801         this.update(this.activeDate.add("mo", -1));
10802     },
10803     showToday : function(e){
10804         this.update(new Date().clearTime());
10805     },
10806     // private
10807     showNextMonth : function(e){
10808         this.update(this.activeDate.add("mo", 1));
10809     },
10810
10811     // private
10812     showPrevYear : function(){
10813         this.update(this.activeDate.add("y", -1));
10814     },
10815
10816     // private
10817     showNextYear : function(){
10818         this.update(this.activeDate.add("y", 1));
10819     },
10820
10821     
10822    // private
10823     update : function(date)
10824     {
10825         var vd = this.activeDate;
10826         this.activeDate = date;
10827 //        if(vd && this.el){
10828 //            var t = date.getTime();
10829 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10830 //                Roo.log('using add remove');
10831 //                
10832 //                this.fireEvent('monthchange', this, date);
10833 //                
10834 //                this.cells.removeClass("fc-state-highlight");
10835 //                this.cells.each(function(c){
10836 //                   if(c.dateValue == t){
10837 //                       c.addClass("fc-state-highlight");
10838 //                       setTimeout(function(){
10839 //                            try{c.dom.firstChild.focus();}catch(e){}
10840 //                       }, 50);
10841 //                       return false;
10842 //                   }
10843 //                   return true;
10844 //                });
10845 //                return;
10846 //            }
10847 //        }
10848         
10849         var days = date.getDaysInMonth();
10850         
10851         var firstOfMonth = date.getFirstDateOfMonth();
10852         var startingPos = firstOfMonth.getDay()-this.startDay;
10853         
10854         if(startingPos < this.startDay){
10855             startingPos += 7;
10856         }
10857         
10858         var pm = date.add(Date.MONTH, -1);
10859         var prevStart = pm.getDaysInMonth()-startingPos;
10860 //        
10861         this.cells = this.el.select('.fc-day',true);
10862         this.textNodes = this.el.query('.fc-day-number');
10863         this.cells.addClassOnOver('fc-state-hover');
10864         
10865         var cells = this.cells.elements;
10866         var textEls = this.textNodes;
10867         
10868         Roo.each(cells, function(cell){
10869             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10870         });
10871         
10872         days += startingPos;
10873
10874         // convert everything to numbers so it's fast
10875         var day = 86400000;
10876         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10877         //Roo.log(d);
10878         //Roo.log(pm);
10879         //Roo.log(prevStart);
10880         
10881         var today = new Date().clearTime().getTime();
10882         var sel = date.clearTime().getTime();
10883         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10884         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10885         var ddMatch = this.disabledDatesRE;
10886         var ddText = this.disabledDatesText;
10887         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10888         var ddaysText = this.disabledDaysText;
10889         var format = this.format;
10890         
10891         var setCellClass = function(cal, cell){
10892             
10893             //Roo.log('set Cell Class');
10894             cell.title = "";
10895             var t = d.getTime();
10896             
10897             //Roo.log(d);
10898             
10899             cell.dateValue = t;
10900             if(t == today){
10901                 cell.className += " fc-today";
10902                 cell.className += " fc-state-highlight";
10903                 cell.title = cal.todayText;
10904             }
10905             if(t == sel){
10906                 // disable highlight in other month..
10907                 //cell.className += " fc-state-highlight";
10908                 
10909             }
10910             // disabling
10911             if(t < min) {
10912                 cell.className = " fc-state-disabled";
10913                 cell.title = cal.minText;
10914                 return;
10915             }
10916             if(t > max) {
10917                 cell.className = " fc-state-disabled";
10918                 cell.title = cal.maxText;
10919                 return;
10920             }
10921             if(ddays){
10922                 if(ddays.indexOf(d.getDay()) != -1){
10923                     cell.title = ddaysText;
10924                     cell.className = " fc-state-disabled";
10925                 }
10926             }
10927             if(ddMatch && format){
10928                 var fvalue = d.dateFormat(format);
10929                 if(ddMatch.test(fvalue)){
10930                     cell.title = ddText.replace("%0", fvalue);
10931                     cell.className = " fc-state-disabled";
10932                 }
10933             }
10934             
10935             if (!cell.initialClassName) {
10936                 cell.initialClassName = cell.dom.className;
10937             }
10938             
10939             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10940         };
10941
10942         var i = 0;
10943         
10944         for(; i < startingPos; i++) {
10945             textEls[i].innerHTML = (++prevStart);
10946             d.setDate(d.getDate()+1);
10947             
10948             cells[i].className = "fc-past fc-other-month";
10949             setCellClass(this, cells[i]);
10950         }
10951         
10952         var intDay = 0;
10953         
10954         for(; i < days; i++){
10955             intDay = i - startingPos + 1;
10956             textEls[i].innerHTML = (intDay);
10957             d.setDate(d.getDate()+1);
10958             
10959             cells[i].className = ''; // "x-date-active";
10960             setCellClass(this, cells[i]);
10961         }
10962         var extraDays = 0;
10963         
10964         for(; i < 42; i++) {
10965             textEls[i].innerHTML = (++extraDays);
10966             d.setDate(d.getDate()+1);
10967             
10968             cells[i].className = "fc-future fc-other-month";
10969             setCellClass(this, cells[i]);
10970         }
10971         
10972         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10973         
10974         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10975         
10976         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10977         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10978         
10979         if(totalRows != 6){
10980             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10981             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10982         }
10983         
10984         this.fireEvent('monthchange', this, date);
10985         
10986         
10987         /*
10988         if(!this.internalRender){
10989             var main = this.el.dom.firstChild;
10990             var w = main.offsetWidth;
10991             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10992             Roo.fly(main).setWidth(w);
10993             this.internalRender = true;
10994             // opera does not respect the auto grow header center column
10995             // then, after it gets a width opera refuses to recalculate
10996             // without a second pass
10997             if(Roo.isOpera && !this.secondPass){
10998                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10999                 this.secondPass = true;
11000                 this.update.defer(10, this, [date]);
11001             }
11002         }
11003         */
11004         
11005     },
11006     
11007     findCell : function(dt) {
11008         dt = dt.clearTime().getTime();
11009         var ret = false;
11010         this.cells.each(function(c){
11011             //Roo.log("check " +c.dateValue + '?=' + dt);
11012             if(c.dateValue == dt){
11013                 ret = c;
11014                 return false;
11015             }
11016             return true;
11017         });
11018         
11019         return ret;
11020     },
11021     
11022     findCells : function(ev) {
11023         var s = ev.start.clone().clearTime().getTime();
11024        // Roo.log(s);
11025         var e= ev.end.clone().clearTime().getTime();
11026        // Roo.log(e);
11027         var ret = [];
11028         this.cells.each(function(c){
11029              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11030             
11031             if(c.dateValue > e){
11032                 return ;
11033             }
11034             if(c.dateValue < s){
11035                 return ;
11036             }
11037             ret.push(c);
11038         });
11039         
11040         return ret;    
11041     },
11042     
11043     findBestRow: function(cells)
11044     {
11045         var ret = 0;
11046         
11047         for (var i =0 ; i < cells.length;i++) {
11048             ret  = Math.max(cells[i].rows || 0,ret);
11049         }
11050         return ret;
11051         
11052     },
11053     
11054     
11055     addItem : function(ev)
11056     {
11057         // look for vertical location slot in
11058         var cells = this.findCells(ev);
11059         
11060         ev.row = this.findBestRow(cells);
11061         
11062         // work out the location.
11063         
11064         var crow = false;
11065         var rows = [];
11066         for(var i =0; i < cells.length; i++) {
11067             if (!crow) {
11068                 crow = {
11069                     start : cells[i],
11070                     end :  cells[i]
11071                 };
11072                 continue;
11073             }
11074             if (crow.start.getY() == cells[i].getY()) {
11075                 // on same row.
11076                 crow.end = cells[i];
11077                 continue;
11078             }
11079             // different row.
11080             rows.push(crow);
11081             crow = {
11082                 start: cells[i],
11083                 end : cells[i]
11084             };
11085             
11086         }
11087         
11088         rows.push(crow);
11089         ev.els = [];
11090         ev.rows = rows;
11091         ev.cells = cells;
11092         for (var i = 0; i < cells.length;i++) {
11093             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11094             
11095         }
11096         
11097         this.calevents.push(ev);
11098     },
11099     
11100     clearEvents: function() {
11101         
11102         if(!this.calevents){
11103             return;
11104         }
11105         
11106         Roo.each(this.cells.elements, function(c){
11107             c.rows = 0;
11108         });
11109         
11110         Roo.each(this.calevents, function(e) {
11111             Roo.each(e.els, function(el) {
11112                 el.un('mouseenter' ,this.onEventEnter, this);
11113                 el.un('mouseleave' ,this.onEventLeave, this);
11114                 el.remove();
11115             },this);
11116         },this);
11117         
11118     },
11119     
11120     renderEvents: function()
11121     {   
11122         // first make sure there is enough space..
11123         
11124         this.cells.each(function(c) {
11125         
11126             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11127         });
11128         
11129         for (var e = 0; e < this.calevents.length; e++) {
11130             var ev = this.calevents[e];
11131             var cells = ev.cells;
11132             var rows = ev.rows;
11133             
11134             for(var i =0; i < rows.length; i++) {
11135                 
11136                  
11137                 // how many rows should it span..
11138                 
11139                 var  cfg = {
11140                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11141                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11142                     
11143                     unselectable : "on",
11144                     cn : [
11145                         {
11146                             cls: 'fc-event-inner',
11147                             cn : [
11148 //                                {
11149 //                                  tag:'span',
11150 //                                  cls: 'fc-event-time',
11151 //                                  html : cells.length > 1 ? '' : ev.time
11152 //                                },
11153                                 {
11154                                   tag:'span',
11155                                   cls: 'fc-event-title',
11156                                   html : String.format('{0}', ev.title)
11157                                 }
11158                                 
11159                                 
11160                             ]
11161                         },
11162                         {
11163                             cls: 'ui-resizable-handle ui-resizable-e',
11164                             html : '&nbsp;&nbsp;&nbsp'
11165                         }
11166                         
11167                     ]
11168                 };
11169                 if (i == 0) {
11170                     cfg.cls += ' fc-event-start';
11171                 }
11172                 if ((i+1) == rows.length) {
11173                     cfg.cls += ' fc-event-end';
11174                 }
11175                 
11176                 var ctr = this.el.select('.fc-event-container',true).first();
11177                 var cg = ctr.createChild(cfg);
11178                 
11179                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11180                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11181                 cg.on('click', this.onEventClick, this, ev);
11182                 
11183                 ev.els.push(cg);
11184                 
11185                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11186                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11187                 //Roo.log(cg);
11188                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11189                 cg.setWidth(ebox.right - sbox.x -2);
11190             }
11191             
11192             
11193         }
11194         
11195     },
11196     
11197     onEventEnter: function (e, el,event,d) {
11198         this.fireEvent('evententer', this, el, event);
11199     },
11200     
11201     onEventLeave: function (e, el,event,d) {
11202         this.fireEvent('eventleave', this, el, event);
11203     },
11204     
11205     onEventClick: function (e, el,event,d) {
11206         this.fireEvent('eventclick', this, el, event);
11207     },
11208     
11209     onMonthChange: function () {
11210         this.store.load();
11211     },
11212     
11213     onLoad: function () 
11214     {   
11215         this.calevents = [];
11216         var cal = this;
11217         
11218         if(this.store.getCount() > 0){
11219             this.store.data.each(function(d){
11220                cal.addItem({
11221                     id : d.data.id,
11222                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11223                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11224                     time : d.data.start_time,
11225                     title : d.data.title,
11226                     description : d.data.description,
11227                     venue : d.data.venue
11228                 });
11229             });
11230         }
11231         
11232         this.renderEvents();
11233         
11234         if(this.loadMask){
11235             this.maskEl.hide();
11236         }
11237     },
11238     
11239     onBeforeLoad: function()
11240     {
11241         this.clearEvents();
11242         
11243         if(this.loadMask){
11244             this.maskEl.show();
11245         }
11246     }
11247 });
11248
11249  
11250  /*
11251  * - LGPL
11252  *
11253  * element
11254  * 
11255  */
11256
11257 /**
11258  * @class Roo.bootstrap.Popover
11259  * @extends Roo.bootstrap.Component
11260  * Bootstrap Popover class
11261  * @cfg {String} html contents of the popover   (or false to use children..)
11262  * @cfg {String} title of popover (or false to hide)
11263  * @cfg {String} placement how it is placed
11264  * @cfg {String} trigger click || hover (or false to trigger manually)
11265  * @cfg {String} over what (parent or false to trigger manually.)
11266  * 
11267  * @constructor
11268  * Create a new Popover
11269  * @param {Object} config The config object
11270  */
11271
11272 Roo.bootstrap.Popover = function(config){
11273     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11274 };
11275
11276 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11277     
11278     title: 'Fill in a title',
11279     html: false,
11280     
11281     placement : 'right',
11282     trigger : 'hover', // hover
11283     
11284     over: 'parent',
11285     
11286     can_build_overlaid : false,
11287     
11288     getChildContainer : function()
11289     {
11290         return this.el.select('.popover-content',true).first();
11291     },
11292     
11293     getAutoCreate : function(){
11294          Roo.log('make popover?');
11295         var cfg = {
11296            cls : 'popover roo-dynamic',
11297            style: 'display:block',
11298            cn : [
11299                 {
11300                     cls : 'arrow'
11301                 },
11302                 {
11303                     cls : 'popover-inner',
11304                     cn : [
11305                         {
11306                             tag: 'h3',
11307                             cls: 'popover-title',
11308                             html : this.title
11309                         },
11310                         {
11311                             cls : 'popover-content',
11312                             html : this.html
11313                         }
11314                     ]
11315                     
11316                 }
11317            ]
11318         };
11319         
11320         return cfg;
11321     },
11322     setTitle: function(str)
11323     {
11324         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11325     },
11326     setContent: function(str)
11327     {
11328         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11329     },
11330     // as it get's added to the bottom of the page.
11331     onRender : function(ct, position)
11332     {
11333         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11334         if(!this.el){
11335             var cfg = Roo.apply({},  this.getAutoCreate());
11336             cfg.id = Roo.id();
11337             
11338             if (this.cls) {
11339                 cfg.cls += ' ' + this.cls;
11340             }
11341             if (this.style) {
11342                 cfg.style = this.style;
11343             }
11344             Roo.log("adding to ")
11345             this.el = Roo.get(document.body).createChild(cfg, position);
11346             Roo.log(this.el);
11347         }
11348         this.initEvents();
11349     },
11350     
11351     initEvents : function()
11352     {
11353         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11354         this.el.enableDisplayMode('block');
11355         this.el.hide();
11356         if (this.over === false) {
11357             return; 
11358         }
11359         if (this.triggers === false) {
11360             return;
11361         }
11362         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11363         var triggers = this.trigger ? this.trigger.split(' ') : [];
11364         Roo.each(triggers, function(trigger) {
11365         
11366             if (trigger == 'click') {
11367                 on_el.on('click', this.toggle, this);
11368             } else if (trigger != 'manual') {
11369                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11370                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11371       
11372                 on_el.on(eventIn  ,this.enter, this);
11373                 on_el.on(eventOut, this.leave, this);
11374             }
11375         }, this);
11376         
11377     },
11378     
11379     
11380     // private
11381     timeout : null,
11382     hoverState : null,
11383     
11384     toggle : function () {
11385         this.hoverState == 'in' ? this.leave() : this.enter();
11386     },
11387     
11388     enter : function () {
11389        
11390     
11391         clearTimeout(this.timeout);
11392     
11393         this.hoverState = 'in'
11394     
11395         if (!this.delay || !this.delay.show) {
11396             this.show();
11397             return 
11398         }
11399         var _t = this;
11400         this.timeout = setTimeout(function () {
11401             if (_t.hoverState == 'in') {
11402                 _t.show();
11403             }
11404         }, this.delay.show)
11405     },
11406     leave : function() {
11407         clearTimeout(this.timeout);
11408     
11409         this.hoverState = 'out'
11410     
11411         if (!this.delay || !this.delay.hide) {
11412             this.hide();
11413             return 
11414         }
11415         var _t = this;
11416         this.timeout = setTimeout(function () {
11417             if (_t.hoverState == 'out') {
11418                 _t.hide();
11419             }
11420         }, this.delay.hide)
11421     },
11422     
11423     show : function (on_el)
11424     {
11425         if (!on_el) {
11426             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11427         }
11428         // set content.
11429         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11430         if (this.html !== false) {
11431             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11432         }
11433         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11434         if (!this.title.length) {
11435             this.el.select('.popover-title',true).hide();
11436         }
11437         
11438         var placement = typeof this.placement == 'function' ?
11439             this.placement.call(this, this.el, on_el) :
11440             this.placement;
11441             
11442         var autoToken = /\s?auto?\s?/i;
11443         var autoPlace = autoToken.test(placement);
11444         if (autoPlace) {
11445             placement = placement.replace(autoToken, '') || 'top';
11446         }
11447         
11448         //this.el.detach()
11449         //this.el.setXY([0,0]);
11450         this.el.show();
11451         this.el.dom.style.display='block';
11452         this.el.addClass(placement);
11453         
11454         //this.el.appendTo(on_el);
11455         
11456         var p = this.getPosition();
11457         var box = this.el.getBox();
11458         
11459         if (autoPlace) {
11460             // fixme..
11461         }
11462         var align = Roo.bootstrap.Popover.alignment[placement]
11463         this.el.alignTo(on_el, align[0],align[1]);
11464         //var arrow = this.el.select('.arrow',true).first();
11465         //arrow.set(align[2], 
11466         
11467         this.el.addClass('in');
11468         this.hoverState = null;
11469         
11470         if (this.el.hasClass('fade')) {
11471             // fade it?
11472         }
11473         
11474     },
11475     hide : function()
11476     {
11477         this.el.setXY([0,0]);
11478         this.el.removeClass('in');
11479         this.el.hide();
11480         
11481     }
11482     
11483 });
11484
11485 Roo.bootstrap.Popover.alignment = {
11486     'left' : ['r-l', [-10,0], 'right'],
11487     'right' : ['l-r', [10,0], 'left'],
11488     'bottom' : ['t-b', [0,10], 'top'],
11489     'top' : [ 'b-t', [0,-10], 'bottom']
11490 };
11491
11492  /*
11493  * - LGPL
11494  *
11495  * Progress
11496  * 
11497  */
11498
11499 /**
11500  * @class Roo.bootstrap.Progress
11501  * @extends Roo.bootstrap.Component
11502  * Bootstrap Progress class
11503  * @cfg {Boolean} striped striped of the progress bar
11504  * @cfg {Boolean} active animated of the progress bar
11505  * 
11506  * 
11507  * @constructor
11508  * Create a new Progress
11509  * @param {Object} config The config object
11510  */
11511
11512 Roo.bootstrap.Progress = function(config){
11513     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11514 };
11515
11516 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
11517     
11518     striped : false,
11519     active: false,
11520     
11521     getAutoCreate : function(){
11522         var cfg = {
11523             tag: 'div',
11524             cls: 'progress'
11525         };
11526         
11527         
11528         if(this.striped){
11529             cfg.cls += ' progress-striped';
11530         }
11531       
11532         if(this.active){
11533             cfg.cls += ' active';
11534         }
11535         
11536         
11537         return cfg;
11538     }
11539    
11540 });
11541
11542  
11543
11544  /*
11545  * - LGPL
11546  *
11547  * ProgressBar
11548  * 
11549  */
11550
11551 /**
11552  * @class Roo.bootstrap.ProgressBar
11553  * @extends Roo.bootstrap.Component
11554  * Bootstrap ProgressBar class
11555  * @cfg {Number} aria_valuenow aria-value now
11556  * @cfg {Number} aria_valuemin aria-value min
11557  * @cfg {Number} aria_valuemax aria-value max
11558  * @cfg {String} label label for the progress bar
11559  * @cfg {String} panel (success | info | warning | danger )
11560  * @cfg {String} role role of the progress bar
11561  * @cfg {String} sr_only text
11562  * 
11563  * 
11564  * @constructor
11565  * Create a new ProgressBar
11566  * @param {Object} config The config object
11567  */
11568
11569 Roo.bootstrap.ProgressBar = function(config){
11570     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11571 };
11572
11573 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
11574     
11575     aria_valuenow : 0,
11576     aria_valuemin : 0,
11577     aria_valuemax : 100,
11578     label : false,
11579     panel : false,
11580     role : false,
11581     sr_only: false,
11582     
11583     getAutoCreate : function()
11584     {
11585         
11586         var cfg = {
11587             tag: 'div',
11588             cls: 'progress-bar',
11589             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11590         };
11591         
11592         if(this.sr_only){
11593             cfg.cn = {
11594                 tag: 'span',
11595                 cls: 'sr-only',
11596                 html: this.sr_only
11597             }
11598         }
11599         
11600         if(this.role){
11601             cfg.role = this.role;
11602         }
11603         
11604         if(this.aria_valuenow){
11605             cfg['aria-valuenow'] = this.aria_valuenow;
11606         }
11607         
11608         if(this.aria_valuemin){
11609             cfg['aria-valuemin'] = this.aria_valuemin;
11610         }
11611         
11612         if(this.aria_valuemax){
11613             cfg['aria-valuemax'] = this.aria_valuemax;
11614         }
11615         
11616         if(this.label && !this.sr_only){
11617             cfg.html = this.label;
11618         }
11619         
11620         if(this.panel){
11621             cfg.cls += ' progress-bar-' + this.panel;
11622         }
11623         
11624         return cfg;
11625     },
11626     
11627     update : function(aria_valuenow)
11628     {
11629         this.aria_valuenow = aria_valuenow;
11630         
11631         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11632     }
11633    
11634 });
11635
11636  
11637
11638  /*
11639  * - LGPL
11640  *
11641  * TabPanel
11642  * 
11643  */
11644
11645 /**
11646  * @class Roo.bootstrap.TabPanel
11647  * @extends Roo.bootstrap.Component
11648  * Bootstrap TabPanel class
11649  * @cfg {Boolean} active panel active
11650  * @cfg {String} html panel content
11651  * @cfg {String} tabId tab relate id
11652  * @cfg {String} navId The navbar which triggers show hide
11653  * 
11654  * 
11655  * @constructor
11656  * Create a new TabPanel
11657  * @param {Object} config The config object
11658  */
11659
11660 Roo.bootstrap.TabPanel = function(config){
11661     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11662      this.addEvents({
11663         /**
11664              * @event changed
11665              * Fires when the active status changes
11666              * @param {Roo.bootstrap.TabPanel} this
11667              * @param {Boolean} state the new state
11668             
11669          */
11670         'changed': true
11671      });
11672 };
11673
11674 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11675     
11676     active: false,
11677     html: false,
11678     tabId: false,
11679     navId : false,
11680     
11681     getAutoCreate : function(){
11682         var cfg = {
11683             tag: 'div',
11684             cls: 'tab-pane',
11685             html: this.html || ''
11686         };
11687         
11688         if(this.active){
11689             cfg.cls += ' active';
11690         }
11691         
11692         if(this.tabId){
11693             cfg.tabId = this.tabId;
11694         }
11695         
11696         return cfg;
11697     },
11698     onRender : function(ct, position)
11699     {
11700        // Roo.log("Call onRender: " + this.xtype);
11701         
11702         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11703         
11704         if (this.navId && this.tabId) {
11705             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11706             if (!item) {
11707                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11708             } else {
11709                 item.on('changed', function(item, state) {
11710                     this.setActive(state);
11711                 }, this);
11712             }
11713         }
11714         
11715     },
11716     setActive: function(state)
11717     {
11718         Roo.log("panel - set active " + this.tabId + "=" + state);
11719         
11720         this.active = state;
11721         if (!state) {
11722             this.el.removeClass('active');
11723             
11724         } else  if (!this.el.hasClass('active')) {
11725             this.el.addClass('active');
11726         }
11727         this.fireEvent('changed', this, state);
11728     }
11729     
11730     
11731 });
11732  
11733
11734  
11735
11736  /*
11737  * - LGPL
11738  *
11739  * DateField
11740  * 
11741  */
11742
11743 /**
11744  * @class Roo.bootstrap.DateField
11745  * @extends Roo.bootstrap.Input
11746  * Bootstrap DateField class
11747  * @cfg {Number} weekStart default 0
11748  * @cfg {Number} weekStart default 0
11749  * @cfg {Number} viewMode default empty, (months|years)
11750  * @cfg {Number} minViewMode default empty, (months|years)
11751  * @cfg {Number} startDate default -Infinity
11752  * @cfg {Number} endDate default Infinity
11753  * @cfg {Boolean} todayHighlight default false
11754  * @cfg {Boolean} todayBtn default false
11755  * @cfg {Boolean} calendarWeeks default false
11756  * @cfg {Object} daysOfWeekDisabled default empty
11757  * 
11758  * @cfg {Boolean} keyboardNavigation default true
11759  * @cfg {String} language default en
11760  * 
11761  * @constructor
11762  * Create a new DateField
11763  * @param {Object} config The config object
11764  */
11765
11766 Roo.bootstrap.DateField = function(config){
11767     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11768      this.addEvents({
11769             /**
11770              * @event show
11771              * Fires when this field show.
11772              * @param {Roo.bootstrap.DateField} this
11773              * @param {Mixed} date The date value
11774              */
11775             show : true,
11776             /**
11777              * @event show
11778              * Fires when this field hide.
11779              * @param {Roo.bootstrap.DateField} this
11780              * @param {Mixed} date The date value
11781              */
11782             hide : true,
11783             /**
11784              * @event select
11785              * Fires when select a date.
11786              * @param {Roo.bootstrap.DateField} this
11787              * @param {Mixed} date The date value
11788              */
11789             select : true
11790         });
11791 };
11792
11793 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11794     
11795     /**
11796      * @cfg {String} format
11797      * The default date format string which can be overriden for localization support.  The format must be
11798      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11799      */
11800     format : "m/d/y",
11801     /**
11802      * @cfg {String} altFormats
11803      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11804      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11805      */
11806     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11807     
11808     weekStart : 0,
11809     
11810     viewMode : '',
11811     
11812     minViewMode : '',
11813     
11814     todayHighlight : false,
11815     
11816     todayBtn: false,
11817     
11818     language: 'en',
11819     
11820     keyboardNavigation: true,
11821     
11822     calendarWeeks: false,
11823     
11824     startDate: -Infinity,
11825     
11826     endDate: Infinity,
11827     
11828     daysOfWeekDisabled: [],
11829     
11830     _events: [],
11831     
11832     UTCDate: function()
11833     {
11834         return new Date(Date.UTC.apply(Date, arguments));
11835     },
11836     
11837     UTCToday: function()
11838     {
11839         var today = new Date();
11840         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11841     },
11842     
11843     getDate: function() {
11844             var d = this.getUTCDate();
11845             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11846     },
11847     
11848     getUTCDate: function() {
11849             return this.date;
11850     },
11851     
11852     setDate: function(d) {
11853             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11854     },
11855     
11856     setUTCDate: function(d) {
11857             this.date = d;
11858             this.setValue(this.formatDate(this.date));
11859     },
11860         
11861     onRender: function(ct, position)
11862     {
11863         
11864         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11865         
11866         this.language = this.language || 'en';
11867         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11868         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11869         
11870         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11871         this.format = this.format || 'm/d/y';
11872         this.isInline = false;
11873         this.isInput = true;
11874         this.component = this.el.select('.add-on', true).first() || false;
11875         this.component = (this.component && this.component.length === 0) ? false : this.component;
11876         this.hasInput = this.component && this.inputEL().length;
11877         
11878         if (typeof(this.minViewMode === 'string')) {
11879             switch (this.minViewMode) {
11880                 case 'months':
11881                     this.minViewMode = 1;
11882                     break;
11883                 case 'years':
11884                     this.minViewMode = 2;
11885                     break;
11886                 default:
11887                     this.minViewMode = 0;
11888                     break;
11889             }
11890         }
11891         
11892         if (typeof(this.viewMode === 'string')) {
11893             switch (this.viewMode) {
11894                 case 'months':
11895                     this.viewMode = 1;
11896                     break;
11897                 case 'years':
11898                     this.viewMode = 2;
11899                     break;
11900                 default:
11901                     this.viewMode = 0;
11902                     break;
11903             }
11904         }
11905                 
11906         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11907         
11908         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11909         
11910         this.picker().on('mousedown', this.onMousedown, this);
11911         this.picker().on('click', this.onClick, this);
11912         
11913         this.picker().addClass('datepicker-dropdown');
11914         
11915         this.startViewMode = this.viewMode;
11916         
11917         
11918         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11919             if(!this.calendarWeeks){
11920                 v.remove();
11921                 return;
11922             };
11923             
11924             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11925             v.attr('colspan', function(i, val){
11926                 return parseInt(val) + 1;
11927             });
11928         })
11929                         
11930         
11931         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11932         
11933         this.setStartDate(this.startDate);
11934         this.setEndDate(this.endDate);
11935         
11936         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11937         
11938         this.fillDow();
11939         this.fillMonths();
11940         this.update();
11941         this.showMode();
11942         
11943         if(this.isInline) {
11944             this.show();
11945         }
11946     },
11947     
11948     picker : function()
11949     {
11950         return this.el.select('.datepicker', true).first();
11951     },
11952     
11953     fillDow: function()
11954     {
11955         var dowCnt = this.weekStart;
11956         
11957         var dow = {
11958             tag: 'tr',
11959             cn: [
11960                 
11961             ]
11962         };
11963         
11964         if(this.calendarWeeks){
11965             dow.cn.push({
11966                 tag: 'th',
11967                 cls: 'cw',
11968                 html: '&nbsp;'
11969             })
11970         }
11971         
11972         while (dowCnt < this.weekStart + 7) {
11973             dow.cn.push({
11974                 tag: 'th',
11975                 cls: 'dow',
11976                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11977             });
11978         }
11979         
11980         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11981     },
11982     
11983     fillMonths: function()
11984     {    
11985         var i = 0
11986         var months = this.picker().select('>.datepicker-months td', true).first();
11987         
11988         months.dom.innerHTML = '';
11989         
11990         while (i < 12) {
11991             var month = {
11992                 tag: 'span',
11993                 cls: 'month',
11994                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11995             }
11996             
11997             months.createChild(month);
11998         }
11999         
12000     },
12001     
12002     update: function(){
12003         
12004         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12005         
12006         if (this.date < this.startDate) {
12007             this.viewDate = new Date(this.startDate);
12008         } else if (this.date > this.endDate) {
12009             this.viewDate = new Date(this.endDate);
12010         } else {
12011             this.viewDate = new Date(this.date);
12012         }
12013         
12014         this.fill();
12015     },
12016     
12017     fill: function() {
12018         var d = new Date(this.viewDate),
12019                 year = d.getUTCFullYear(),
12020                 month = d.getUTCMonth(),
12021                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12022                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12023                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12024                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12025                 currentDate = this.date && this.date.valueOf(),
12026                 today = this.UTCToday();
12027         
12028         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12029         
12030 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12031         
12032 //        this.picker.select('>tfoot th.today').
12033 //                                              .text(dates[this.language].today)
12034 //                                              .toggle(this.todayBtn !== false);
12035     
12036         this.updateNavArrows();
12037         this.fillMonths();
12038                                                 
12039         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12040         
12041         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12042          
12043         prevMonth.setUTCDate(day);
12044         
12045         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12046         
12047         var nextMonth = new Date(prevMonth);
12048         
12049         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12050         
12051         nextMonth = nextMonth.valueOf();
12052         
12053         var fillMonths = false;
12054         
12055         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12056         
12057         while(prevMonth.valueOf() < nextMonth) {
12058             var clsName = '';
12059             
12060             if (prevMonth.getUTCDay() === this.weekStart) {
12061                 if(fillMonths){
12062                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12063                 }
12064                     
12065                 fillMonths = {
12066                     tag: 'tr',
12067                     cn: []
12068                 };
12069                 
12070                 if(this.calendarWeeks){
12071                     // ISO 8601: First week contains first thursday.
12072                     // ISO also states week starts on Monday, but we can be more abstract here.
12073                     var
12074                     // Start of current week: based on weekstart/current date
12075                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12076                     // Thursday of this week
12077                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12078                     // First Thursday of year, year from thursday
12079                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12080                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12081                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12082                     
12083                     fillMonths.cn.push({
12084                         tag: 'td',
12085                         cls: 'cw',
12086                         html: calWeek
12087                     });
12088                 }
12089             }
12090             
12091             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12092                 clsName += ' old';
12093             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12094                 clsName += ' new';
12095             }
12096             if (this.todayHighlight &&
12097                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12098                 prevMonth.getUTCMonth() == today.getMonth() &&
12099                 prevMonth.getUTCDate() == today.getDate()) {
12100                 clsName += ' today';
12101             }
12102             
12103             if (currentDate && prevMonth.valueOf() === currentDate) {
12104                 clsName += ' active';
12105             }
12106             
12107             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12108                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12109                     clsName += ' disabled';
12110             }
12111             
12112             fillMonths.cn.push({
12113                 tag: 'td',
12114                 cls: 'day ' + clsName,
12115                 html: prevMonth.getDate()
12116             })
12117             
12118             prevMonth.setDate(prevMonth.getDate()+1);
12119         }
12120           
12121         var currentYear = this.date && this.date.getUTCFullYear();
12122         var currentMonth = this.date && this.date.getUTCMonth();
12123         
12124         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12125         
12126         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12127             v.removeClass('active');
12128             
12129             if(currentYear === year && k === currentMonth){
12130                 v.addClass('active');
12131             }
12132             
12133             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12134                 v.addClass('disabled');
12135             }
12136             
12137         });
12138         
12139         
12140         year = parseInt(year/10, 10) * 10;
12141         
12142         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12143         
12144         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12145         
12146         year -= 1;
12147         for (var i = -1; i < 11; i++) {
12148             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12149                 tag: 'span',
12150                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12151                 html: year
12152             })
12153             
12154             year += 1;
12155         }
12156     },
12157     
12158     showMode: function(dir) {
12159         if (dir) {
12160             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12161         }
12162         Roo.each(this.picker().select('>div',true).elements, function(v){
12163             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12164             v.hide();
12165         });
12166         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12167     },
12168     
12169     place: function()
12170     {
12171         if(this.isInline) return;
12172         
12173         this.picker().removeClass(['bottom', 'top']);
12174         
12175         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12176             /*
12177              * place to the top of element!
12178              *
12179              */
12180             
12181             this.picker().addClass('top');
12182             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12183             
12184             return;
12185         }
12186         
12187         this.picker().addClass('bottom');
12188         
12189         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12190     },
12191     
12192     parseDate : function(value){
12193         if(!value || value instanceof Date){
12194             return value;
12195         }
12196         var v = Date.parseDate(value, this.format);
12197         if (!v && this.useIso) {
12198             v = Date.parseDate(value, 'Y-m-d');
12199         }
12200         if(!v && this.altFormats){
12201             if(!this.altFormatsArray){
12202                 this.altFormatsArray = this.altFormats.split("|");
12203             }
12204             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12205                 v = Date.parseDate(value, this.altFormatsArray[i]);
12206             }
12207         }
12208         return v;
12209     },
12210     
12211     formatDate : function(date, fmt){
12212         return (!date || !(date instanceof Date)) ?
12213         date : date.dateFormat(fmt || this.format);
12214     },
12215     
12216     onFocus : function()
12217     {
12218         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12219         this.show();
12220     },
12221     
12222     onBlur : function()
12223     {
12224         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12225         this.hide();
12226     },
12227     
12228     show : function()
12229     {
12230         this.picker().show();
12231         this.update();
12232         this.place();
12233         
12234         this.fireEvent('show', this, this.date);
12235     },
12236     
12237     hide : function()
12238     {
12239         if(this.isInline) return;
12240         this.picker().hide();
12241         this.viewMode = this.startViewMode;
12242         this.showMode();
12243         
12244         this.fireEvent('hide', this, this.date);
12245         
12246     },
12247     
12248     onMousedown: function(e){
12249         e.stopPropagation();
12250         e.preventDefault();
12251     },
12252     
12253     keyup: function(e){
12254         Roo.bootstrap.DateField.superclass.keyup.call(this);
12255         this.update();
12256         
12257     },
12258
12259     setValue: function(v){
12260         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12261         
12262         this.fireEvent('select', this, this.date);
12263         
12264     },
12265     
12266     fireKey: function(e){
12267         if (!this.picker().isVisible()){
12268             if (e.keyCode == 27) // allow escape to hide and re-show picker
12269                 this.show();
12270             return;
12271         }
12272         var dateChanged = false,
12273         dir, day, month,
12274         newDate, newViewDate;
12275         switch(e.keyCode){
12276             case 27: // escape
12277                 this.hide();
12278                 e.preventDefault();
12279                 break;
12280             case 37: // left
12281             case 39: // right
12282                 if (!this.keyboardNavigation) break;
12283                 dir = e.keyCode == 37 ? -1 : 1;
12284                 
12285                 if (e.ctrlKey){
12286                     newDate = this.moveYear(this.date, dir);
12287                     newViewDate = this.moveYear(this.viewDate, dir);
12288                 } else if (e.shiftKey){
12289                     newDate = this.moveMonth(this.date, dir);
12290                     newViewDate = this.moveMonth(this.viewDate, dir);
12291                 } else {
12292                     newDate = new Date(this.date);
12293                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12294                     newViewDate = new Date(this.viewDate);
12295                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12296                 }
12297                 if (this.dateWithinRange(newDate)){
12298                     this.date = newDate;
12299                     this.viewDate = newViewDate;
12300                     this.setValue(this.formatDate(this.date));
12301                     this.update();
12302                     e.preventDefault();
12303                     dateChanged = true;
12304                 }
12305                 break;
12306             case 38: // up
12307             case 40: // down
12308                 if (!this.keyboardNavigation) break;
12309                 dir = e.keyCode == 38 ? -1 : 1;
12310                 if (e.ctrlKey){
12311                     newDate = this.moveYear(this.date, dir);
12312                     newViewDate = this.moveYear(this.viewDate, dir);
12313                 } else if (e.shiftKey){
12314                     newDate = this.moveMonth(this.date, dir);
12315                     newViewDate = this.moveMonth(this.viewDate, dir);
12316                 } else {
12317                     newDate = new Date(this.date);
12318                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12319                     newViewDate = new Date(this.viewDate);
12320                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12321                 }
12322                 if (this.dateWithinRange(newDate)){
12323                     this.date = newDate;
12324                     this.viewDate = newViewDate;
12325                     this.setValue(this.formatDate(this.date));
12326                     this.update();
12327                     e.preventDefault();
12328                     dateChanged = true;
12329                 }
12330                 break;
12331             case 13: // enter
12332                 this.setValue(this.formatDate(this.date));
12333                 this.hide();
12334                 e.preventDefault();
12335                 break;
12336             case 9: // tab
12337                 this.setValue(this.formatDate(this.date));
12338                 this.hide();
12339                 break;
12340         }
12341     },
12342     
12343     
12344     onClick: function(e) {
12345         e.stopPropagation();
12346         e.preventDefault();
12347         
12348         var target = e.getTarget();
12349         
12350         if(target.nodeName.toLowerCase() === 'i'){
12351             target = Roo.get(target).dom.parentNode;
12352         }
12353         
12354         var nodeName = target.nodeName;
12355         var className = target.className;
12356         var html = target.innerHTML;
12357         
12358         switch(nodeName.toLowerCase()) {
12359             case 'th':
12360                 switch(className) {
12361                     case 'switch':
12362                         this.showMode(1);
12363                         break;
12364                     case 'prev':
12365                     case 'next':
12366                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12367                         switch(this.viewMode){
12368                                 case 0:
12369                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12370                                         break;
12371                                 case 1:
12372                                 case 2:
12373                                         this.viewDate = this.moveYear(this.viewDate, dir);
12374                                         break;
12375                         }
12376                         this.fill();
12377                         break;
12378                     case 'today':
12379                         var date = new Date();
12380                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12381                         this.fill()
12382                         this.setValue(this.formatDate(this.date));
12383                         this.hide();
12384                         break;
12385                 }
12386                 break;
12387             case 'span':
12388                 if (className.indexOf('disabled') === -1) {
12389                     this.viewDate.setUTCDate(1);
12390                     if (className.indexOf('month') !== -1) {
12391                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12392                     } else {
12393                         var year = parseInt(html, 10) || 0;
12394                         this.viewDate.setUTCFullYear(year);
12395                         
12396                     }
12397                     this.showMode(-1);
12398                     this.fill();
12399                 }
12400                 break;
12401                 
12402             case 'td':
12403                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12404                     var day = parseInt(html, 10) || 1;
12405                     var year = this.viewDate.getUTCFullYear(),
12406                         month = this.viewDate.getUTCMonth();
12407
12408                     if (className.indexOf('old') !== -1) {
12409                         if(month === 0 ){
12410                             month = 11;
12411                             year -= 1;
12412                         }else{
12413                             month -= 1;
12414                         }
12415                     } else if (className.indexOf('new') !== -1) {
12416                         if (month == 11) {
12417                             month = 0;
12418                             year += 1;
12419                         } else {
12420                             month += 1;
12421                         }
12422                     }
12423                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12424                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12425                     this.fill();
12426                     this.setValue(this.formatDate(this.date));
12427                     this.hide();
12428                 }
12429                 break;
12430         }
12431     },
12432     
12433     setStartDate: function(startDate){
12434         this.startDate = startDate || -Infinity;
12435         if (this.startDate !== -Infinity) {
12436             this.startDate = this.parseDate(this.startDate);
12437         }
12438         this.update();
12439         this.updateNavArrows();
12440     },
12441
12442     setEndDate: function(endDate){
12443         this.endDate = endDate || Infinity;
12444         if (this.endDate !== Infinity) {
12445             this.endDate = this.parseDate(this.endDate);
12446         }
12447         this.update();
12448         this.updateNavArrows();
12449     },
12450     
12451     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12452         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12453         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12454             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12455         }
12456         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12457             return parseInt(d, 10);
12458         });
12459         this.update();
12460         this.updateNavArrows();
12461     },
12462     
12463     updateNavArrows: function() {
12464         var d = new Date(this.viewDate),
12465         year = d.getUTCFullYear(),
12466         month = d.getUTCMonth();
12467         
12468         Roo.each(this.picker().select('.prev', true).elements, function(v){
12469             v.show();
12470             switch (this.viewMode) {
12471                 case 0:
12472
12473                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12474                         v.hide();
12475                     }
12476                     break;
12477                 case 1:
12478                 case 2:
12479                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12480                         v.hide();
12481                     }
12482                     break;
12483             }
12484         });
12485         
12486         Roo.each(this.picker().select('.next', true).elements, function(v){
12487             v.show();
12488             switch (this.viewMode) {
12489                 case 0:
12490
12491                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12492                         v.hide();
12493                     }
12494                     break;
12495                 case 1:
12496                 case 2:
12497                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12498                         v.hide();
12499                     }
12500                     break;
12501             }
12502         })
12503     },
12504     
12505     moveMonth: function(date, dir){
12506         if (!dir) return date;
12507         var new_date = new Date(date.valueOf()),
12508         day = new_date.getUTCDate(),
12509         month = new_date.getUTCMonth(),
12510         mag = Math.abs(dir),
12511         new_month, test;
12512         dir = dir > 0 ? 1 : -1;
12513         if (mag == 1){
12514             test = dir == -1
12515             // If going back one month, make sure month is not current month
12516             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12517             ? function(){
12518                 return new_date.getUTCMonth() == month;
12519             }
12520             // If going forward one month, make sure month is as expected
12521             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12522             : function(){
12523                 return new_date.getUTCMonth() != new_month;
12524             };
12525             new_month = month + dir;
12526             new_date.setUTCMonth(new_month);
12527             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12528             if (new_month < 0 || new_month > 11)
12529                 new_month = (new_month + 12) % 12;
12530         } else {
12531             // For magnitudes >1, move one month at a time...
12532             for (var i=0; i<mag; i++)
12533                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12534                 new_date = this.moveMonth(new_date, dir);
12535             // ...then reset the day, keeping it in the new month
12536             new_month = new_date.getUTCMonth();
12537             new_date.setUTCDate(day);
12538             test = function(){
12539                 return new_month != new_date.getUTCMonth();
12540             };
12541         }
12542         // Common date-resetting loop -- if date is beyond end of month, make it
12543         // end of month
12544         while (test()){
12545             new_date.setUTCDate(--day);
12546             new_date.setUTCMonth(new_month);
12547         }
12548         return new_date;
12549     },
12550
12551     moveYear: function(date, dir){
12552         return this.moveMonth(date, dir*12);
12553     },
12554
12555     dateWithinRange: function(date){
12556         return date >= this.startDate && date <= this.endDate;
12557     },
12558
12559     
12560     remove: function() {
12561         this.picker().remove();
12562     }
12563    
12564 });
12565
12566 Roo.apply(Roo.bootstrap.DateField,  {
12567     
12568     head : {
12569         tag: 'thead',
12570         cn: [
12571         {
12572             tag: 'tr',
12573             cn: [
12574             {
12575                 tag: 'th',
12576                 cls: 'prev',
12577                 html: '<i class="icon-arrow-left"/>'
12578             },
12579             {
12580                 tag: 'th',
12581                 cls: 'switch',
12582                 colspan: '5'
12583             },
12584             {
12585                 tag: 'th',
12586                 cls: 'next',
12587                 html: '<i class="icon-arrow-right"/>'
12588             }
12589
12590             ]
12591         }
12592         ]
12593     },
12594     
12595     content : {
12596         tag: 'tbody',
12597         cn: [
12598         {
12599             tag: 'tr',
12600             cn: [
12601             {
12602                 tag: 'td',
12603                 colspan: '7'
12604             }
12605             ]
12606         }
12607         ]
12608     },
12609     
12610     footer : {
12611         tag: 'tfoot',
12612         cn: [
12613         {
12614             tag: 'tr',
12615             cn: [
12616             {
12617                 tag: 'th',
12618                 colspan: '7',
12619                 cls: 'today'
12620             }
12621                     
12622             ]
12623         }
12624         ]
12625     },
12626     
12627     dates:{
12628         en: {
12629             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12630             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12631             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12632             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12633             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12634             today: "Today"
12635         }
12636     },
12637     
12638     modes: [
12639     {
12640         clsName: 'days',
12641         navFnc: 'Month',
12642         navStep: 1
12643     },
12644     {
12645         clsName: 'months',
12646         navFnc: 'FullYear',
12647         navStep: 1
12648     },
12649     {
12650         clsName: 'years',
12651         navFnc: 'FullYear',
12652         navStep: 10
12653     }]
12654 });
12655
12656 Roo.apply(Roo.bootstrap.DateField,  {
12657   
12658     template : {
12659         tag: 'div',
12660         cls: 'datepicker dropdown-menu',
12661         cn: [
12662         {
12663             tag: 'div',
12664             cls: 'datepicker-days',
12665             cn: [
12666             {
12667                 tag: 'table',
12668                 cls: 'table-condensed',
12669                 cn:[
12670                 Roo.bootstrap.DateField.head,
12671                 {
12672                     tag: 'tbody'
12673                 },
12674                 Roo.bootstrap.DateField.footer
12675                 ]
12676             }
12677             ]
12678         },
12679         {
12680             tag: 'div',
12681             cls: 'datepicker-months',
12682             cn: [
12683             {
12684                 tag: 'table',
12685                 cls: 'table-condensed',
12686                 cn:[
12687                 Roo.bootstrap.DateField.head,
12688                 Roo.bootstrap.DateField.content,
12689                 Roo.bootstrap.DateField.footer
12690                 ]
12691             }
12692             ]
12693         },
12694         {
12695             tag: 'div',
12696             cls: 'datepicker-years',
12697             cn: [
12698             {
12699                 tag: 'table',
12700                 cls: 'table-condensed',
12701                 cn:[
12702                 Roo.bootstrap.DateField.head,
12703                 Roo.bootstrap.DateField.content,
12704                 Roo.bootstrap.DateField.footer
12705                 ]
12706             }
12707             ]
12708         }
12709         ]
12710     }
12711 });
12712
12713  
12714
12715  /*
12716  * - LGPL
12717  *
12718  * TimeField
12719  * 
12720  */
12721
12722 /**
12723  * @class Roo.bootstrap.TimeField
12724  * @extends Roo.bootstrap.Input
12725  * Bootstrap DateField class
12726  * 
12727  * 
12728  * @constructor
12729  * Create a new TimeField
12730  * @param {Object} config The config object
12731  */
12732
12733 Roo.bootstrap.TimeField = function(config){
12734     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12735     this.addEvents({
12736             /**
12737              * @event show
12738              * Fires when this field show.
12739              * @param {Roo.bootstrap.DateField} this
12740              * @param {Mixed} date The date value
12741              */
12742             show : true,
12743             /**
12744              * @event show
12745              * Fires when this field hide.
12746              * @param {Roo.bootstrap.DateField} this
12747              * @param {Mixed} date The date value
12748              */
12749             hide : true,
12750             /**
12751              * @event select
12752              * Fires when select a date.
12753              * @param {Roo.bootstrap.DateField} this
12754              * @param {Mixed} date The date value
12755              */
12756             select : true
12757         });
12758 };
12759
12760 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12761     
12762     /**
12763      * @cfg {String} format
12764      * The default time format string which can be overriden for localization support.  The format must be
12765      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12766      */
12767     format : "H:i",
12768        
12769     onRender: function(ct, position)
12770     {
12771         
12772         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12773                 
12774         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12775         
12776         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12777         
12778         this.pop = this.picker().select('>.datepicker-time',true).first();
12779         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12780         
12781         this.picker().on('mousedown', this.onMousedown, this);
12782         this.picker().on('click', this.onClick, this);
12783         
12784         this.picker().addClass('datepicker-dropdown');
12785     
12786         this.fillTime();
12787         this.update();
12788             
12789         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12790         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12791         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12792         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12793         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12794         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12795
12796     },
12797     
12798     fireKey: function(e){
12799         if (!this.picker().isVisible()){
12800             if (e.keyCode == 27) // allow escape to hide and re-show picker
12801                 this.show();
12802             return;
12803         }
12804
12805         e.preventDefault();
12806         
12807         switch(e.keyCode){
12808             case 27: // escape
12809                 this.hide();
12810                 break;
12811             case 37: // left
12812             case 39: // right
12813                 this.onTogglePeriod();
12814                 break;
12815             case 38: // up
12816                 this.onIncrementMinutes();
12817                 break;
12818             case 40: // down
12819                 this.onDecrementMinutes();
12820                 break;
12821             case 13: // enter
12822             case 9: // tab
12823                 this.setTime();
12824                 break;
12825         }
12826     },
12827     
12828     onClick: function(e) {
12829         e.stopPropagation();
12830         e.preventDefault();
12831     },
12832     
12833     picker : function()
12834     {
12835         return this.el.select('.datepicker', true).first();
12836     },
12837     
12838     fillTime: function()
12839     {    
12840         var time = this.pop.select('tbody', true).first();
12841         
12842         time.dom.innerHTML = '';
12843         
12844         time.createChild({
12845             tag: 'tr',
12846             cn: [
12847                 {
12848                     tag: 'td',
12849                     cn: [
12850                         {
12851                             tag: 'a',
12852                             href: '#',
12853                             cls: 'btn',
12854                             cn: [
12855                                 {
12856                                     tag: 'span',
12857                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12858                                 }
12859                             ]
12860                         } 
12861                     ]
12862                 },
12863                 {
12864                     tag: 'td',
12865                     cls: 'separator'
12866                 },
12867                 {
12868                     tag: 'td',
12869                     cn: [
12870                         {
12871                             tag: 'a',
12872                             href: '#',
12873                             cls: 'btn',
12874                             cn: [
12875                                 {
12876                                     tag: 'span',
12877                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12878                                 }
12879                             ]
12880                         }
12881                     ]
12882                 },
12883                 {
12884                     tag: 'td',
12885                     cls: 'separator'
12886                 }
12887             ]
12888         });
12889         
12890         time.createChild({
12891             tag: 'tr',
12892             cn: [
12893                 {
12894                     tag: 'td',
12895                     cn: [
12896                         {
12897                             tag: 'span',
12898                             cls: 'timepicker-hour',
12899                             html: '00'
12900                         }  
12901                     ]
12902                 },
12903                 {
12904                     tag: 'td',
12905                     cls: 'separator',
12906                     html: ':'
12907                 },
12908                 {
12909                     tag: 'td',
12910                     cn: [
12911                         {
12912                             tag: 'span',
12913                             cls: 'timepicker-minute',
12914                             html: '00'
12915                         }  
12916                     ]
12917                 },
12918                 {
12919                     tag: 'td',
12920                     cls: 'separator'
12921                 },
12922                 {
12923                     tag: 'td',
12924                     cn: [
12925                         {
12926                             tag: 'button',
12927                             type: 'button',
12928                             cls: 'btn btn-primary period',
12929                             html: 'AM'
12930                             
12931                         }
12932                     ]
12933                 }
12934             ]
12935         });
12936         
12937         time.createChild({
12938             tag: 'tr',
12939             cn: [
12940                 {
12941                     tag: 'td',
12942                     cn: [
12943                         {
12944                             tag: 'a',
12945                             href: '#',
12946                             cls: 'btn',
12947                             cn: [
12948                                 {
12949                                     tag: 'span',
12950                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12951                                 }
12952                             ]
12953                         }
12954                     ]
12955                 },
12956                 {
12957                     tag: 'td',
12958                     cls: 'separator'
12959                 },
12960                 {
12961                     tag: 'td',
12962                     cn: [
12963                         {
12964                             tag: 'a',
12965                             href: '#',
12966                             cls: 'btn',
12967                             cn: [
12968                                 {
12969                                     tag: 'span',
12970                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12971                                 }
12972                             ]
12973                         }
12974                     ]
12975                 },
12976                 {
12977                     tag: 'td',
12978                     cls: 'separator'
12979                 }
12980             ]
12981         });
12982         
12983     },
12984     
12985     update: function()
12986     {
12987         
12988         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12989         
12990         this.fill();
12991     },
12992     
12993     fill: function() 
12994     {
12995         var hours = this.time.getHours();
12996         var minutes = this.time.getMinutes();
12997         var period = 'AM';
12998         
12999         if(hours > 11){
13000             period = 'PM';
13001         }
13002         
13003         if(hours == 0){
13004             hours = 12;
13005         }
13006         
13007         
13008         if(hours > 12){
13009             hours = hours - 12;
13010         }
13011         
13012         if(hours < 10){
13013             hours = '0' + hours;
13014         }
13015         
13016         if(minutes < 10){
13017             minutes = '0' + minutes;
13018         }
13019         
13020         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13021         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13022         this.pop.select('button', true).first().dom.innerHTML = period;
13023         
13024     },
13025     
13026     place: function()
13027     {   
13028         this.picker().removeClass(['bottom', 'top']);
13029         
13030         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13031             /*
13032              * place to the top of element!
13033              *
13034              */
13035             
13036             this.picker().addClass('top');
13037             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13038             
13039             return;
13040         }
13041         
13042         this.picker().addClass('bottom');
13043         
13044         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13045     },
13046   
13047     onFocus : function()
13048     {
13049         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13050         this.show();
13051     },
13052     
13053     onBlur : function()
13054     {
13055         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13056         this.hide();
13057     },
13058     
13059     show : function()
13060     {
13061         this.picker().show();
13062         this.pop.show();
13063         this.update();
13064         this.place();
13065         
13066         this.fireEvent('show', this, this.date);
13067     },
13068     
13069     hide : function()
13070     {
13071         this.picker().hide();
13072         this.pop.hide();
13073         
13074         this.fireEvent('hide', this, this.date);
13075     },
13076     
13077     setTime : function()
13078     {
13079         this.hide();
13080         this.setValue(this.time.format(this.format));
13081         
13082         this.fireEvent('select', this, this.date);
13083         
13084         
13085     },
13086     
13087     onMousedown: function(e){
13088         e.stopPropagation();
13089         e.preventDefault();
13090     },
13091     
13092     onIncrementHours: function()
13093     {
13094         Roo.log('onIncrementHours');
13095         this.time = this.time.add(Date.HOUR, 1);
13096         this.update();
13097         
13098     },
13099     
13100     onDecrementHours: function()
13101     {
13102         Roo.log('onDecrementHours');
13103         this.time = this.time.add(Date.HOUR, -1);
13104         this.update();
13105     },
13106     
13107     onIncrementMinutes: function()
13108     {
13109         Roo.log('onIncrementMinutes');
13110         this.time = this.time.add(Date.MINUTE, 1);
13111         this.update();
13112     },
13113     
13114     onDecrementMinutes: function()
13115     {
13116         Roo.log('onDecrementMinutes');
13117         this.time = this.time.add(Date.MINUTE, -1);
13118         this.update();
13119     },
13120     
13121     onTogglePeriod: function()
13122     {
13123         Roo.log('onTogglePeriod');
13124         this.time = this.time.add(Date.HOUR, 12);
13125         this.update();
13126     }
13127     
13128    
13129 });
13130
13131 Roo.apply(Roo.bootstrap.TimeField,  {
13132     
13133     content : {
13134         tag: 'tbody',
13135         cn: [
13136             {
13137                 tag: 'tr',
13138                 cn: [
13139                 {
13140                     tag: 'td',
13141                     colspan: '7'
13142                 }
13143                 ]
13144             }
13145         ]
13146     },
13147     
13148     footer : {
13149         tag: 'tfoot',
13150         cn: [
13151             {
13152                 tag: 'tr',
13153                 cn: [
13154                 {
13155                     tag: 'th',
13156                     colspan: '7',
13157                     cls: '',
13158                     cn: [
13159                         {
13160                             tag: 'button',
13161                             cls: 'btn btn-info ok',
13162                             html: 'OK'
13163                         }
13164                     ]
13165                 }
13166
13167                 ]
13168             }
13169         ]
13170     }
13171 });
13172
13173 Roo.apply(Roo.bootstrap.TimeField,  {
13174   
13175     template : {
13176         tag: 'div',
13177         cls: 'datepicker dropdown-menu',
13178         cn: [
13179             {
13180                 tag: 'div',
13181                 cls: 'datepicker-time',
13182                 cn: [
13183                 {
13184                     tag: 'table',
13185                     cls: 'table-condensed',
13186                     cn:[
13187                     Roo.bootstrap.TimeField.content,
13188                     Roo.bootstrap.TimeField.footer
13189                     ]
13190                 }
13191                 ]
13192             }
13193         ]
13194     }
13195 });
13196
13197  
13198
13199  /*
13200  * - LGPL
13201  *
13202  * CheckBox
13203  * 
13204  */
13205
13206 /**
13207  * @class Roo.bootstrap.CheckBox
13208  * @extends Roo.bootstrap.Input
13209  * Bootstrap CheckBox class
13210  * 
13211  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13212  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13213  * @cfg {String} boxLabel The text that appears beside the checkbox
13214  * @cfg {Boolean} checked initnal the element
13215  * 
13216  * @constructor
13217  * Create a new CheckBox
13218  * @param {Object} config The config object
13219  */
13220
13221 Roo.bootstrap.CheckBox = function(config){
13222     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13223    
13224         this.addEvents({
13225             /**
13226             * @event check
13227             * Fires when the element is checked or unchecked.
13228             * @param {Roo.bootstrap.CheckBox} this This input
13229             * @param {Boolean} checked The new checked value
13230             */
13231            check : true
13232         });
13233 };
13234
13235 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13236     
13237     inputType: 'checkbox',
13238     inputValue: 1,
13239     valueOff: 0,
13240     boxLabel: false,
13241     checked: false,
13242     
13243     getAutoCreate : function()
13244     {
13245         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13246         
13247         var id = Roo.id();
13248         
13249         var cfg = {};
13250         
13251         cfg.cls = 'form-group' //input-group
13252         
13253         var input =  {
13254             tag: 'input',
13255             id : id,
13256             type : this.inputType,
13257             value : (!this.checked) ? this.valueOff : this.inputValue,
13258             cls : 'form-box',
13259             placeholder : this.placeholder || ''
13260             
13261         };
13262         
13263         if (this.disabled) {
13264             input.disabled=true;
13265         }
13266         
13267         if(this.checked){
13268             input.checked = this.checked;
13269         }
13270         
13271         if (this.name) {
13272             input.name = this.name;
13273         }
13274         
13275         if (this.size) {
13276             input.cls += ' input-' + this.size;
13277         }
13278         
13279         var settings=this;
13280         ['xs','sm','md','lg'].map(function(size){
13281             if (settings[size]) {
13282                 cfg.cls += ' col-' + size + '-' + settings[size];
13283             }
13284         });
13285         
13286         var inputblock = input;
13287         
13288         if (this.before || this.after) {
13289             
13290             inputblock = {
13291                 cls : 'input-group',
13292                 cn :  [] 
13293             };
13294             if (this.before) {
13295                 inputblock.cn.push({
13296                     tag :'span',
13297                     cls : 'input-group-addon',
13298                     html : this.before
13299                 });
13300             }
13301             inputblock.cn.push(input);
13302             if (this.after) {
13303                 inputblock.cn.push({
13304                     tag :'span',
13305                     cls : 'input-group-addon',
13306                     html : this.after
13307                 });
13308             }
13309             
13310         };
13311         
13312         if (align ==='left' && this.fieldLabel.length) {
13313                 Roo.log("left and has label");
13314                 cfg.cn = [
13315                     
13316                     {
13317                         tag: 'label',
13318                         'for' :  id,
13319                         cls : 'control-label col-md-' + this.labelWidth,
13320                         html : this.fieldLabel
13321                         
13322                     },
13323                     {
13324                         cls : "col-md-" + (12 - this.labelWidth), 
13325                         cn: [
13326                             inputblock
13327                         ]
13328                     }
13329                     
13330                 ];
13331         } else if ( this.fieldLabel.length) {
13332                 Roo.log(" label");
13333                 cfg.cn = [
13334                    
13335                     {
13336                         tag: this.boxLabel ? 'span' : 'label',
13337                         'for': id,
13338                         cls: 'control-label box-input-label',
13339                         //cls : 'input-group-addon',
13340                         html : this.fieldLabel
13341                         
13342                     },
13343                     
13344                     inputblock
13345                     
13346                 ];
13347
13348         } else {
13349             
13350                    Roo.log(" no label && no align");
13351                 cfg.cn = [
13352                     
13353                         inputblock
13354                     
13355                 ];
13356                 
13357                 
13358         };
13359         
13360         if(this.boxLabel){
13361             cfg.cn.push({
13362                 tag: 'label',
13363                 'for': id,
13364                 cls: 'box-label',
13365                 html: this.boxLabel
13366             })
13367         }
13368         
13369         return cfg;
13370         
13371     },
13372     
13373     /**
13374      * return the real input element.
13375      */
13376     inputEl: function ()
13377     {
13378         return this.el.select('input.form-box',true).first();
13379     },
13380     
13381     initEvents : function()
13382     {
13383 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13384         
13385         this.inputEl().on('click', this.onClick,  this);
13386         
13387     },
13388     
13389     onClick : function()
13390     {   
13391         this.setChecked(!this.checked);
13392     },
13393     
13394     setChecked : function(state,suppressEvent)
13395     {
13396         this.checked = state;
13397         
13398         this.inputEl().dom.checked = state;
13399         
13400         if(suppressEvent !== true){
13401             this.fireEvent('check', this, state);
13402         }
13403         
13404         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13405         
13406     },
13407     
13408     setValue : function(v,suppressEvent)
13409     {
13410         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13411     }
13412     
13413 });
13414
13415  
13416 /*
13417  * - LGPL
13418  *
13419  * Radio
13420  * 
13421  */
13422
13423 /**
13424  * @class Roo.bootstrap.Radio
13425  * @extends Roo.bootstrap.CheckBox
13426  * Bootstrap Radio class
13427
13428  * @constructor
13429  * Create a new Radio
13430  * @param {Object} config The config object
13431  */
13432
13433 Roo.bootstrap.Radio = function(config){
13434     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13435    
13436 };
13437
13438 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13439     
13440     inputType: 'radio',
13441     inputValue: '',
13442     valueOff: '',
13443     
13444     getAutoCreate : function()
13445     {
13446         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13447         
13448         var id = Roo.id();
13449         
13450         var cfg = {};
13451         
13452         cfg.cls = 'form-group' //input-group
13453         
13454         var input =  {
13455             tag: 'input',
13456             id : id,
13457             type : this.inputType,
13458             value : (!this.checked) ? this.valueOff : this.inputValue,
13459             cls : 'form-box',
13460             placeholder : this.placeholder || ''
13461             
13462         };
13463         
13464         if (this.disabled) {
13465             input.disabled=true;
13466         }
13467         
13468         if(this.checked){
13469             input.checked = this.checked;
13470         }
13471         
13472         if (this.name) {
13473             input.name = this.name;
13474         }
13475         
13476         if (this.size) {
13477             input.cls += ' input-' + this.size;
13478         }
13479         
13480         var settings=this;
13481         ['xs','sm','md','lg'].map(function(size){
13482             if (settings[size]) {
13483                 cfg.cls += ' col-' + size + '-' + settings[size];
13484             }
13485         });
13486         
13487         var inputblock = input;
13488         
13489         if (this.before || this.after) {
13490             
13491             inputblock = {
13492                 cls : 'input-group',
13493                 cn :  [] 
13494             };
13495             if (this.before) {
13496                 inputblock.cn.push({
13497                     tag :'span',
13498                     cls : 'input-group-addon',
13499                     html : this.before
13500                 });
13501             }
13502             inputblock.cn.push(input);
13503             if (this.after) {
13504                 inputblock.cn.push({
13505                     tag :'span',
13506                     cls : 'input-group-addon',
13507                     html : this.after
13508                 });
13509             }
13510             
13511         };
13512         
13513         if (align ==='left' && this.fieldLabel.length) {
13514                 Roo.log("left and has label");
13515                 cfg.cn = [
13516                     
13517                     {
13518                         tag: 'label',
13519                         'for' :  id,
13520                         cls : 'control-label col-md-' + this.labelWidth,
13521                         html : this.fieldLabel
13522                         
13523                     },
13524                     {
13525                         cls : "col-md-" + (12 - this.labelWidth), 
13526                         cn: [
13527                             inputblock
13528                         ]
13529                     }
13530                     
13531                 ];
13532         } else if ( this.fieldLabel.length) {
13533                 Roo.log(" label");
13534                  cfg.cn = [
13535                    
13536                     {
13537                         tag: 'label',
13538                         'for': id,
13539                         cls: 'control-label box-input-label',
13540                         //cls : 'input-group-addon',
13541                         html : this.fieldLabel
13542                         
13543                     },
13544                     
13545                     inputblock
13546                     
13547                 ];
13548
13549         } else {
13550             
13551                    Roo.log(" no label && no align");
13552                 cfg.cn = [
13553                     
13554                         inputblock
13555                     
13556                 ];
13557                 
13558                 
13559         };
13560         
13561         if(this.boxLabel){
13562             cfg.cn.push({
13563                 tag: 'label',
13564                 'for': id,
13565                 cls: 'box-label',
13566                 html: this.boxLabel
13567             })
13568         }
13569         
13570         return cfg;
13571         
13572     },
13573    
13574     onClick : function()
13575     {   
13576         this.setChecked(true);
13577     },
13578     
13579     setChecked : function(state,suppressEvent)
13580     {
13581         if(state){
13582             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13583                 v.dom.checked = false;
13584             });
13585         }
13586         
13587         this.checked = state;
13588         this.inputEl().dom.checked = state;
13589         
13590         if(suppressEvent !== true){
13591             this.fireEvent('check', this, state);
13592         }
13593         
13594         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13595         
13596     },
13597     
13598     getGroupValue : function()
13599     {
13600         var value = ''
13601         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13602             if(v.dom.checked == true){
13603                 value = v.dom.value;
13604             }
13605         });
13606         
13607         return value;
13608     },
13609     
13610     /**
13611      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13612      * @return {Mixed} value The field value
13613      */
13614     getValue : function(){
13615         return this.getGroupValue();
13616     }
13617     
13618 });
13619
13620  
13621 //<script type="text/javascript">
13622
13623 /*
13624  * Based  Ext JS Library 1.1.1
13625  * Copyright(c) 2006-2007, Ext JS, LLC.
13626  * LGPL
13627  *
13628  */
13629  
13630 /**
13631  * @class Roo.HtmlEditorCore
13632  * @extends Roo.Component
13633  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13634  *
13635  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13636  */
13637
13638 Roo.HtmlEditorCore = function(config){
13639     
13640     
13641     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13642     this.addEvents({
13643         /**
13644          * @event initialize
13645          * Fires when the editor is fully initialized (including the iframe)
13646          * @param {Roo.HtmlEditorCore} this
13647          */
13648         initialize: true,
13649         /**
13650          * @event activate
13651          * Fires when the editor is first receives the focus. Any insertion must wait
13652          * until after this event.
13653          * @param {Roo.HtmlEditorCore} this
13654          */
13655         activate: true,
13656          /**
13657          * @event beforesync
13658          * Fires before the textarea is updated with content from the editor iframe. Return false
13659          * to cancel the sync.
13660          * @param {Roo.HtmlEditorCore} this
13661          * @param {String} html
13662          */
13663         beforesync: true,
13664          /**
13665          * @event beforepush
13666          * Fires before the iframe editor is updated with content from the textarea. Return false
13667          * to cancel the push.
13668          * @param {Roo.HtmlEditorCore} this
13669          * @param {String} html
13670          */
13671         beforepush: true,
13672          /**
13673          * @event sync
13674          * Fires when the textarea is updated with content from the editor iframe.
13675          * @param {Roo.HtmlEditorCore} this
13676          * @param {String} html
13677          */
13678         sync: true,
13679          /**
13680          * @event push
13681          * Fires when the iframe editor is updated with content from the textarea.
13682          * @param {Roo.HtmlEditorCore} this
13683          * @param {String} html
13684          */
13685         push: true,
13686         
13687         /**
13688          * @event editorevent
13689          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13690          * @param {Roo.HtmlEditorCore} this
13691          */
13692         editorevent: true
13693     });
13694      
13695 };
13696
13697
13698 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13699
13700
13701      /**
13702      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13703      */
13704     
13705     owner : false,
13706     
13707      /**
13708      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13709      *                        Roo.resizable.
13710      */
13711     resizable : false,
13712      /**
13713      * @cfg {Number} height (in pixels)
13714      */   
13715     height: 300,
13716    /**
13717      * @cfg {Number} width (in pixels)
13718      */   
13719     width: 500,
13720     
13721     /**
13722      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13723      * 
13724      */
13725     stylesheets: false,
13726     
13727     // id of frame..
13728     frameId: false,
13729     
13730     // private properties
13731     validationEvent : false,
13732     deferHeight: true,
13733     initialized : false,
13734     activated : false,
13735     sourceEditMode : false,
13736     onFocus : Roo.emptyFn,
13737     iframePad:3,
13738     hideMode:'offsets',
13739     
13740     clearUp: true,
13741     
13742      
13743     
13744
13745     /**
13746      * Protected method that will not generally be called directly. It
13747      * is called when the editor initializes the iframe with HTML contents. Override this method if you
13748      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13749      */
13750     getDocMarkup : function(){
13751         // body styles..
13752         var st = '';
13753         Roo.log(this.stylesheets);
13754         
13755         // inherit styels from page...?? 
13756         if (this.stylesheets === false) {
13757             
13758             Roo.get(document.head).select('style').each(function(node) {
13759                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13760             });
13761             
13762             Roo.get(document.head).select('link').each(function(node) { 
13763                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13764             });
13765             
13766         } else if (!this.stylesheets.length) {
13767                 // simple..
13768                 st = '<style type="text/css">' +
13769                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13770                    '</style>';
13771         } else {
13772             Roo.each(this.stylesheets, function(s) {
13773                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13774             });
13775             
13776         }
13777         
13778         st +=  '<style type="text/css">' +
13779             'IMG { cursor: pointer } ' +
13780         '</style>';
13781
13782         
13783         return '<html><head>' + st  +
13784             //<style type="text/css">' +
13785             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13786             //'</style>' +
13787             ' </head><body class="roo-htmleditor-body"></body></html>';
13788     },
13789
13790     // private
13791     onRender : function(ct, position)
13792     {
13793         var _t = this;
13794         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13795         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13796         
13797         
13798         this.el.dom.style.border = '0 none';
13799         this.el.dom.setAttribute('tabIndex', -1);
13800         this.el.addClass('x-hidden hide');
13801         
13802         
13803         
13804         if(Roo.isIE){ // fix IE 1px bogus margin
13805             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13806         }
13807        
13808         
13809         this.frameId = Roo.id();
13810         
13811          
13812         
13813         var iframe = this.owner.wrap.createChild({
13814             tag: 'iframe',
13815             cls: 'form-control', // bootstrap..
13816             id: this.frameId,
13817             name: this.frameId,
13818             frameBorder : 'no',
13819             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13820         }, this.el
13821         );
13822         
13823         
13824         this.iframe = iframe.dom;
13825
13826          this.assignDocWin();
13827         
13828         this.doc.designMode = 'on';
13829        
13830         this.doc.open();
13831         this.doc.write(this.getDocMarkup());
13832         this.doc.close();
13833
13834         
13835         var task = { // must defer to wait for browser to be ready
13836             run : function(){
13837                 //console.log("run task?" + this.doc.readyState);
13838                 this.assignDocWin();
13839                 if(this.doc.body || this.doc.readyState == 'complete'){
13840                     try {
13841                         this.doc.designMode="on";
13842                     } catch (e) {
13843                         return;
13844                     }
13845                     Roo.TaskMgr.stop(task);
13846                     this.initEditor.defer(10, this);
13847                 }
13848             },
13849             interval : 10,
13850             duration: 10000,
13851             scope: this
13852         };
13853         Roo.TaskMgr.start(task);
13854
13855         
13856          
13857     },
13858
13859     // private
13860     onResize : function(w, h)
13861     {
13862          Roo.log('resize: ' +w + ',' + h );
13863         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13864         if(!this.iframe){
13865             return;
13866         }
13867         if(typeof w == 'number'){
13868             
13869             this.iframe.style.width = w + 'px';
13870         }
13871         if(typeof h == 'number'){
13872             
13873             this.iframe.style.height = h + 'px';
13874             if(this.doc){
13875                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13876             }
13877         }
13878         
13879     },
13880
13881     /**
13882      * Toggles the editor between standard and source edit mode.
13883      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13884      */
13885     toggleSourceEdit : function(sourceEditMode){
13886         
13887         this.sourceEditMode = sourceEditMode === true;
13888         
13889         if(this.sourceEditMode){
13890  
13891             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13892             
13893         }else{
13894             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13895             //this.iframe.className = '';
13896             this.deferFocus();
13897         }
13898         //this.setSize(this.owner.wrap.getSize());
13899         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13900     },
13901
13902     
13903   
13904
13905     /**
13906      * Protected method that will not generally be called directly. If you need/want
13907      * custom HTML cleanup, this is the method you should override.
13908      * @param {String} html The HTML to be cleaned
13909      * return {String} The cleaned HTML
13910      */
13911     cleanHtml : function(html){
13912         html = String(html);
13913         if(html.length > 5){
13914             if(Roo.isSafari){ // strip safari nonsense
13915                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13916             }
13917         }
13918         if(html == '&nbsp;'){
13919             html = '';
13920         }
13921         return html;
13922     },
13923
13924     /**
13925      * HTML Editor -> Textarea
13926      * Protected method that will not generally be called directly. Syncs the contents
13927      * of the editor iframe with the textarea.
13928      */
13929     syncValue : function(){
13930         if(this.initialized){
13931             var bd = (this.doc.body || this.doc.documentElement);
13932             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13933             var html = bd.innerHTML;
13934             if(Roo.isSafari){
13935                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13936                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13937                 if(m && m[1]){
13938                     html = '<div style="'+m[0]+'">' + html + '</div>';
13939                 }
13940             }
13941             html = this.cleanHtml(html);
13942             // fix up the special chars.. normaly like back quotes in word...
13943             // however we do not want to do this with chinese..
13944             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13945                 var cc = b.charCodeAt();
13946                 if (
13947                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13948                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13949                     (cc >= 0xf900 && cc < 0xfb00 )
13950                 ) {
13951                         return b;
13952                 }
13953                 return "&#"+cc+";" 
13954             });
13955             if(this.owner.fireEvent('beforesync', this, html) !== false){
13956                 this.el.dom.value = html;
13957                 this.owner.fireEvent('sync', this, html);
13958             }
13959         }
13960     },
13961
13962     /**
13963      * Protected method that will not generally be called directly. Pushes the value of the textarea
13964      * into the iframe editor.
13965      */
13966     pushValue : function(){
13967         if(this.initialized){
13968             var v = this.el.dom.value.trim();
13969             
13970 //            if(v.length < 1){
13971 //                v = '&#160;';
13972 //            }
13973             
13974             if(this.owner.fireEvent('beforepush', this, v) !== false){
13975                 var d = (this.doc.body || this.doc.documentElement);
13976                 d.innerHTML = v;
13977                 this.cleanUpPaste();
13978                 this.el.dom.value = d.innerHTML;
13979                 this.owner.fireEvent('push', this, v);
13980             }
13981         }
13982     },
13983
13984     // private
13985     deferFocus : function(){
13986         this.focus.defer(10, this);
13987     },
13988
13989     // doc'ed in Field
13990     focus : function(){
13991         if(this.win && !this.sourceEditMode){
13992             this.win.focus();
13993         }else{
13994             this.el.focus();
13995         }
13996     },
13997     
13998     assignDocWin: function()
13999     {
14000         var iframe = this.iframe;
14001         
14002          if(Roo.isIE){
14003             this.doc = iframe.contentWindow.document;
14004             this.win = iframe.contentWindow;
14005         } else {
14006             if (!Roo.get(this.frameId)) {
14007                 return;
14008             }
14009             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14010             this.win = Roo.get(this.frameId).dom.contentWindow;
14011         }
14012     },
14013     
14014     // private
14015     initEditor : function(){
14016         //console.log("INIT EDITOR");
14017         this.assignDocWin();
14018         
14019         
14020         
14021         this.doc.designMode="on";
14022         this.doc.open();
14023         this.doc.write(this.getDocMarkup());
14024         this.doc.close();
14025         
14026         var dbody = (this.doc.body || this.doc.documentElement);
14027         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14028         // this copies styles from the containing element into thsi one..
14029         // not sure why we need all of this..
14030         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14031         ss['background-attachment'] = 'fixed'; // w3c
14032         dbody.bgProperties = 'fixed'; // ie
14033         Roo.DomHelper.applyStyles(dbody, ss);
14034         Roo.EventManager.on(this.doc, {
14035             //'mousedown': this.onEditorEvent,
14036             'mouseup': this.onEditorEvent,
14037             'dblclick': this.onEditorEvent,
14038             'click': this.onEditorEvent,
14039             'keyup': this.onEditorEvent,
14040             buffer:100,
14041             scope: this
14042         });
14043         if(Roo.isGecko){
14044             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14045         }
14046         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14047             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14048         }
14049         this.initialized = true;
14050
14051         this.owner.fireEvent('initialize', this);
14052         this.pushValue();
14053     },
14054
14055     // private
14056     onDestroy : function(){
14057         
14058         
14059         
14060         if(this.rendered){
14061             
14062             //for (var i =0; i < this.toolbars.length;i++) {
14063             //    // fixme - ask toolbars for heights?
14064             //    this.toolbars[i].onDestroy();
14065            // }
14066             
14067             //this.wrap.dom.innerHTML = '';
14068             //this.wrap.remove();
14069         }
14070     },
14071
14072     // private
14073     onFirstFocus : function(){
14074         
14075         this.assignDocWin();
14076         
14077         
14078         this.activated = true;
14079          
14080     
14081         if(Roo.isGecko){ // prevent silly gecko errors
14082             this.win.focus();
14083             var s = this.win.getSelection();
14084             if(!s.focusNode || s.focusNode.nodeType != 3){
14085                 var r = s.getRangeAt(0);
14086                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14087                 r.collapse(true);
14088                 this.deferFocus();
14089             }
14090             try{
14091                 this.execCmd('useCSS', true);
14092                 this.execCmd('styleWithCSS', false);
14093             }catch(e){}
14094         }
14095         this.owner.fireEvent('activate', this);
14096     },
14097
14098     // private
14099     adjustFont: function(btn){
14100         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14101         //if(Roo.isSafari){ // safari
14102         //    adjust *= 2;
14103        // }
14104         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14105         if(Roo.isSafari){ // safari
14106             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14107             v =  (v < 10) ? 10 : v;
14108             v =  (v > 48) ? 48 : v;
14109             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14110             
14111         }
14112         
14113         
14114         v = Math.max(1, v+adjust);
14115         
14116         this.execCmd('FontSize', v  );
14117     },
14118
14119     onEditorEvent : function(e){
14120         this.owner.fireEvent('editorevent', this, e);
14121       //  this.updateToolbar();
14122         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14123     },
14124
14125     insertTag : function(tg)
14126     {
14127         // could be a bit smarter... -> wrap the current selected tRoo..
14128         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14129             
14130             range = this.createRange(this.getSelection());
14131             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14132             wrappingNode.appendChild(range.extractContents());
14133             range.insertNode(wrappingNode);
14134
14135             return;
14136             
14137             
14138             
14139         }
14140         this.execCmd("formatblock",   tg);
14141         
14142     },
14143     
14144     insertText : function(txt)
14145     {
14146         
14147         
14148         var range = this.createRange();
14149         range.deleteContents();
14150                //alert(Sender.getAttribute('label'));
14151                
14152         range.insertNode(this.doc.createTextNode(txt));
14153     } ,
14154     
14155      
14156
14157     /**
14158      * Executes a Midas editor command on the editor document and performs necessary focus and
14159      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14160      * @param {String} cmd The Midas command
14161      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14162      */
14163     relayCmd : function(cmd, value){
14164         this.win.focus();
14165         this.execCmd(cmd, value);
14166         this.owner.fireEvent('editorevent', this);
14167         //this.updateToolbar();
14168         this.owner.deferFocus();
14169     },
14170
14171     /**
14172      * Executes a Midas editor command directly on the editor document.
14173      * For visual commands, you should use {@link #relayCmd} instead.
14174      * <b>This should only be called after the editor is initialized.</b>
14175      * @param {String} cmd The Midas command
14176      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14177      */
14178     execCmd : function(cmd, value){
14179         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14180         this.syncValue();
14181     },
14182  
14183  
14184    
14185     /**
14186      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14187      * to insert tRoo.
14188      * @param {String} text | dom node.. 
14189      */
14190     insertAtCursor : function(text)
14191     {
14192         
14193         
14194         
14195         if(!this.activated){
14196             return;
14197         }
14198         /*
14199         if(Roo.isIE){
14200             this.win.focus();
14201             var r = this.doc.selection.createRange();
14202             if(r){
14203                 r.collapse(true);
14204                 r.pasteHTML(text);
14205                 this.syncValue();
14206                 this.deferFocus();
14207             
14208             }
14209             return;
14210         }
14211         */
14212         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14213             this.win.focus();
14214             
14215             
14216             // from jquery ui (MIT licenced)
14217             var range, node;
14218             var win = this.win;
14219             
14220             if (win.getSelection && win.getSelection().getRangeAt) {
14221                 range = win.getSelection().getRangeAt(0);
14222                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14223                 range.insertNode(node);
14224             } else if (win.document.selection && win.document.selection.createRange) {
14225                 // no firefox support
14226                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14227                 win.document.selection.createRange().pasteHTML(txt);
14228             } else {
14229                 // no firefox support
14230                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14231                 this.execCmd('InsertHTML', txt);
14232             } 
14233             
14234             this.syncValue();
14235             
14236             this.deferFocus();
14237         }
14238     },
14239  // private
14240     mozKeyPress : function(e){
14241         if(e.ctrlKey){
14242             var c = e.getCharCode(), cmd;
14243           
14244             if(c > 0){
14245                 c = String.fromCharCode(c).toLowerCase();
14246                 switch(c){
14247                     case 'b':
14248                         cmd = 'bold';
14249                         break;
14250                     case 'i':
14251                         cmd = 'italic';
14252                         break;
14253                     
14254                     case 'u':
14255                         cmd = 'underline';
14256                         break;
14257                     
14258                     case 'v':
14259                         this.cleanUpPaste.defer(100, this);
14260                         return;
14261                         
14262                 }
14263                 if(cmd){
14264                     this.win.focus();
14265                     this.execCmd(cmd);
14266                     this.deferFocus();
14267                     e.preventDefault();
14268                 }
14269                 
14270             }
14271         }
14272     },
14273
14274     // private
14275     fixKeys : function(){ // load time branching for fastest keydown performance
14276         if(Roo.isIE){
14277             return function(e){
14278                 var k = e.getKey(), r;
14279                 if(k == e.TAB){
14280                     e.stopEvent();
14281                     r = this.doc.selection.createRange();
14282                     if(r){
14283                         r.collapse(true);
14284                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14285                         this.deferFocus();
14286                     }
14287                     return;
14288                 }
14289                 
14290                 if(k == e.ENTER){
14291                     r = this.doc.selection.createRange();
14292                     if(r){
14293                         var target = r.parentElement();
14294                         if(!target || target.tagName.toLowerCase() != 'li'){
14295                             e.stopEvent();
14296                             r.pasteHTML('<br />');
14297                             r.collapse(false);
14298                             r.select();
14299                         }
14300                     }
14301                 }
14302                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14303                     this.cleanUpPaste.defer(100, this);
14304                     return;
14305                 }
14306                 
14307                 
14308             };
14309         }else if(Roo.isOpera){
14310             return function(e){
14311                 var k = e.getKey();
14312                 if(k == e.TAB){
14313                     e.stopEvent();
14314                     this.win.focus();
14315                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14316                     this.deferFocus();
14317                 }
14318                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14319                     this.cleanUpPaste.defer(100, this);
14320                     return;
14321                 }
14322                 
14323             };
14324         }else if(Roo.isSafari){
14325             return function(e){
14326                 var k = e.getKey();
14327                 
14328                 if(k == e.TAB){
14329                     e.stopEvent();
14330                     this.execCmd('InsertText','\t');
14331                     this.deferFocus();
14332                     return;
14333                 }
14334                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14335                     this.cleanUpPaste.defer(100, this);
14336                     return;
14337                 }
14338                 
14339              };
14340         }
14341     }(),
14342     
14343     getAllAncestors: function()
14344     {
14345         var p = this.getSelectedNode();
14346         var a = [];
14347         if (!p) {
14348             a.push(p); // push blank onto stack..
14349             p = this.getParentElement();
14350         }
14351         
14352         
14353         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14354             a.push(p);
14355             p = p.parentNode;
14356         }
14357         a.push(this.doc.body);
14358         return a;
14359     },
14360     lastSel : false,
14361     lastSelNode : false,
14362     
14363     
14364     getSelection : function() 
14365     {
14366         this.assignDocWin();
14367         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14368     },
14369     
14370     getSelectedNode: function() 
14371     {
14372         // this may only work on Gecko!!!
14373         
14374         // should we cache this!!!!
14375         
14376         
14377         
14378          
14379         var range = this.createRange(this.getSelection()).cloneRange();
14380         
14381         if (Roo.isIE) {
14382             var parent = range.parentElement();
14383             while (true) {
14384                 var testRange = range.duplicate();
14385                 testRange.moveToElementText(parent);
14386                 if (testRange.inRange(range)) {
14387                     break;
14388                 }
14389                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14390                     break;
14391                 }
14392                 parent = parent.parentElement;
14393             }
14394             return parent;
14395         }
14396         
14397         // is ancestor a text element.
14398         var ac =  range.commonAncestorContainer;
14399         if (ac.nodeType == 3) {
14400             ac = ac.parentNode;
14401         }
14402         
14403         var ar = ac.childNodes;
14404          
14405         var nodes = [];
14406         var other_nodes = [];
14407         var has_other_nodes = false;
14408         for (var i=0;i<ar.length;i++) {
14409             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14410                 continue;
14411             }
14412             // fullly contained node.
14413             
14414             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14415                 nodes.push(ar[i]);
14416                 continue;
14417             }
14418             
14419             // probably selected..
14420             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14421                 other_nodes.push(ar[i]);
14422                 continue;
14423             }
14424             // outer..
14425             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14426                 continue;
14427             }
14428             
14429             
14430             has_other_nodes = true;
14431         }
14432         if (!nodes.length && other_nodes.length) {
14433             nodes= other_nodes;
14434         }
14435         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14436             return false;
14437         }
14438         
14439         return nodes[0];
14440     },
14441     createRange: function(sel)
14442     {
14443         // this has strange effects when using with 
14444         // top toolbar - not sure if it's a great idea.
14445         //this.editor.contentWindow.focus();
14446         if (typeof sel != "undefined") {
14447             try {
14448                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14449             } catch(e) {
14450                 return this.doc.createRange();
14451             }
14452         } else {
14453             return this.doc.createRange();
14454         }
14455     },
14456     getParentElement: function()
14457     {
14458         
14459         this.assignDocWin();
14460         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14461         
14462         var range = this.createRange(sel);
14463          
14464         try {
14465             var p = range.commonAncestorContainer;
14466             while (p.nodeType == 3) { // text node
14467                 p = p.parentNode;
14468             }
14469             return p;
14470         } catch (e) {
14471             return null;
14472         }
14473     
14474     },
14475     /***
14476      *
14477      * Range intersection.. the hard stuff...
14478      *  '-1' = before
14479      *  '0' = hits..
14480      *  '1' = after.
14481      *         [ -- selected range --- ]
14482      *   [fail]                        [fail]
14483      *
14484      *    basically..
14485      *      if end is before start or  hits it. fail.
14486      *      if start is after end or hits it fail.
14487      *
14488      *   if either hits (but other is outside. - then it's not 
14489      *   
14490      *    
14491      **/
14492     
14493     
14494     // @see http://www.thismuchiknow.co.uk/?p=64.
14495     rangeIntersectsNode : function(range, node)
14496     {
14497         var nodeRange = node.ownerDocument.createRange();
14498         try {
14499             nodeRange.selectNode(node);
14500         } catch (e) {
14501             nodeRange.selectNodeContents(node);
14502         }
14503     
14504         var rangeStartRange = range.cloneRange();
14505         rangeStartRange.collapse(true);
14506     
14507         var rangeEndRange = range.cloneRange();
14508         rangeEndRange.collapse(false);
14509     
14510         var nodeStartRange = nodeRange.cloneRange();
14511         nodeStartRange.collapse(true);
14512     
14513         var nodeEndRange = nodeRange.cloneRange();
14514         nodeEndRange.collapse(false);
14515     
14516         return rangeStartRange.compareBoundaryPoints(
14517                  Range.START_TO_START, nodeEndRange) == -1 &&
14518                rangeEndRange.compareBoundaryPoints(
14519                  Range.START_TO_START, nodeStartRange) == 1;
14520         
14521          
14522     },
14523     rangeCompareNode : function(range, node)
14524     {
14525         var nodeRange = node.ownerDocument.createRange();
14526         try {
14527             nodeRange.selectNode(node);
14528         } catch (e) {
14529             nodeRange.selectNodeContents(node);
14530         }
14531         
14532         
14533         range.collapse(true);
14534     
14535         nodeRange.collapse(true);
14536      
14537         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14538         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
14539          
14540         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14541         
14542         var nodeIsBefore   =  ss == 1;
14543         var nodeIsAfter    = ee == -1;
14544         
14545         if (nodeIsBefore && nodeIsAfter)
14546             return 0; // outer
14547         if (!nodeIsBefore && nodeIsAfter)
14548             return 1; //right trailed.
14549         
14550         if (nodeIsBefore && !nodeIsAfter)
14551             return 2;  // left trailed.
14552         // fully contined.
14553         return 3;
14554     },
14555
14556     // private? - in a new class?
14557     cleanUpPaste :  function()
14558     {
14559         // cleans up the whole document..
14560         Roo.log('cleanuppaste');
14561         
14562         this.cleanUpChildren(this.doc.body);
14563         var clean = this.cleanWordChars(this.doc.body.innerHTML);
14564         if (clean != this.doc.body.innerHTML) {
14565             this.doc.body.innerHTML = clean;
14566         }
14567         
14568     },
14569     
14570     cleanWordChars : function(input) {// change the chars to hex code
14571         var he = Roo.HtmlEditorCore;
14572         
14573         var output = input;
14574         Roo.each(he.swapCodes, function(sw) { 
14575             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14576             
14577             output = output.replace(swapper, sw[1]);
14578         });
14579         
14580         return output;
14581     },
14582     
14583     
14584     cleanUpChildren : function (n)
14585     {
14586         if (!n.childNodes.length) {
14587             return;
14588         }
14589         for (var i = n.childNodes.length-1; i > -1 ; i--) {
14590            this.cleanUpChild(n.childNodes[i]);
14591         }
14592     },
14593     
14594     
14595         
14596     
14597     cleanUpChild : function (node)
14598     {
14599         var ed = this;
14600         //console.log(node);
14601         if (node.nodeName == "#text") {
14602             // clean up silly Windows -- stuff?
14603             return; 
14604         }
14605         if (node.nodeName == "#comment") {
14606             node.parentNode.removeChild(node);
14607             // clean up silly Windows -- stuff?
14608             return; 
14609         }
14610         
14611         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14612             // remove node.
14613             node.parentNode.removeChild(node);
14614             return;
14615             
14616         }
14617         
14618         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14619         
14620         // remove <a name=....> as rendering on yahoo mailer is borked with this.
14621         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14622         
14623         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14624         //    remove_keep_children = true;
14625         //}
14626         
14627         if (remove_keep_children) {
14628             this.cleanUpChildren(node);
14629             // inserts everything just before this node...
14630             while (node.childNodes.length) {
14631                 var cn = node.childNodes[0];
14632                 node.removeChild(cn);
14633                 node.parentNode.insertBefore(cn, node);
14634             }
14635             node.parentNode.removeChild(node);
14636             return;
14637         }
14638         
14639         if (!node.attributes || !node.attributes.length) {
14640             this.cleanUpChildren(node);
14641             return;
14642         }
14643         
14644         function cleanAttr(n,v)
14645         {
14646             
14647             if (v.match(/^\./) || v.match(/^\//)) {
14648                 return;
14649             }
14650             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14651                 return;
14652             }
14653             if (v.match(/^#/)) {
14654                 return;
14655             }
14656 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14657             node.removeAttribute(n);
14658             
14659         }
14660         
14661         function cleanStyle(n,v)
14662         {
14663             if (v.match(/expression/)) { //XSS?? should we even bother..
14664                 node.removeAttribute(n);
14665                 return;
14666             }
14667             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14668             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14669             
14670             
14671             var parts = v.split(/;/);
14672             var clean = [];
14673             
14674             Roo.each(parts, function(p) {
14675                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14676                 if (!p.length) {
14677                     return true;
14678                 }
14679                 var l = p.split(':').shift().replace(/\s+/g,'');
14680                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14681                 
14682                 if ( cblack.indexOf(l) > -1) {
14683 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14684                     //node.removeAttribute(n);
14685                     return true;
14686                 }
14687                 //Roo.log()
14688                 // only allow 'c whitelisted system attributes'
14689                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14690 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14691                     //node.removeAttribute(n);
14692                     return true;
14693                 }
14694                 
14695                 
14696                  
14697                 
14698                 clean.push(p);
14699                 return true;
14700             });
14701             if (clean.length) { 
14702                 node.setAttribute(n, clean.join(';'));
14703             } else {
14704                 node.removeAttribute(n);
14705             }
14706             
14707         }
14708         
14709         
14710         for (var i = node.attributes.length-1; i > -1 ; i--) {
14711             var a = node.attributes[i];
14712             //console.log(a);
14713             
14714             if (a.name.toLowerCase().substr(0,2)=='on')  {
14715                 node.removeAttribute(a.name);
14716                 continue;
14717             }
14718             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14719                 node.removeAttribute(a.name);
14720                 continue;
14721             }
14722             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14723                 cleanAttr(a.name,a.value); // fixme..
14724                 continue;
14725             }
14726             if (a.name == 'style') {
14727                 cleanStyle(a.name,a.value);
14728                 continue;
14729             }
14730             /// clean up MS crap..
14731             // tecnically this should be a list of valid class'es..
14732             
14733             
14734             if (a.name == 'class') {
14735                 if (a.value.match(/^Mso/)) {
14736                     node.className = '';
14737                 }
14738                 
14739                 if (a.value.match(/body/)) {
14740                     node.className = '';
14741                 }
14742                 continue;
14743             }
14744             
14745             // style cleanup!?
14746             // class cleanup?
14747             
14748         }
14749         
14750         
14751         this.cleanUpChildren(node);
14752         
14753         
14754     }
14755     
14756     
14757     // hide stuff that is not compatible
14758     /**
14759      * @event blur
14760      * @hide
14761      */
14762     /**
14763      * @event change
14764      * @hide
14765      */
14766     /**
14767      * @event focus
14768      * @hide
14769      */
14770     /**
14771      * @event specialkey
14772      * @hide
14773      */
14774     /**
14775      * @cfg {String} fieldClass @hide
14776      */
14777     /**
14778      * @cfg {String} focusClass @hide
14779      */
14780     /**
14781      * @cfg {String} autoCreate @hide
14782      */
14783     /**
14784      * @cfg {String} inputType @hide
14785      */
14786     /**
14787      * @cfg {String} invalidClass @hide
14788      */
14789     /**
14790      * @cfg {String} invalidText @hide
14791      */
14792     /**
14793      * @cfg {String} msgFx @hide
14794      */
14795     /**
14796      * @cfg {String} validateOnBlur @hide
14797      */
14798 });
14799
14800 Roo.HtmlEditorCore.white = [
14801         'area', 'br', 'img', 'input', 'hr', 'wbr',
14802         
14803        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14804        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14805        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14806        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14807        'table',   'ul',         'xmp', 
14808        
14809        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14810       'thead',   'tr', 
14811      
14812       'dir', 'menu', 'ol', 'ul', 'dl',
14813        
14814       'embed',  'object'
14815 ];
14816
14817
14818 Roo.HtmlEditorCore.black = [
14819     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14820         'applet', // 
14821         'base',   'basefont', 'bgsound', 'blink',  'body', 
14822         'frame',  'frameset', 'head',    'html',   'ilayer', 
14823         'iframe', 'layer',  'link',     'meta',    'object',   
14824         'script', 'style' ,'title',  'xml' // clean later..
14825 ];
14826 Roo.HtmlEditorCore.clean = [
14827     'script', 'style', 'title', 'xml'
14828 ];
14829 Roo.HtmlEditorCore.remove = [
14830     'font'
14831 ];
14832 // attributes..
14833
14834 Roo.HtmlEditorCore.ablack = [
14835     'on'
14836 ];
14837     
14838 Roo.HtmlEditorCore.aclean = [ 
14839     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14840 ];
14841
14842 // protocols..
14843 Roo.HtmlEditorCore.pwhite= [
14844         'http',  'https',  'mailto'
14845 ];
14846
14847 // white listed style attributes.
14848 Roo.HtmlEditorCore.cwhite= [
14849       //  'text-align', /// default is to allow most things..
14850       
14851          
14852 //        'font-size'//??
14853 ];
14854
14855 // black listed style attributes.
14856 Roo.HtmlEditorCore.cblack= [
14857       //  'font-size' -- this can be set by the project 
14858 ];
14859
14860
14861 Roo.HtmlEditorCore.swapCodes   =[ 
14862     [    8211, "--" ], 
14863     [    8212, "--" ], 
14864     [    8216,  "'" ],  
14865     [    8217, "'" ],  
14866     [    8220, '"' ],  
14867     [    8221, '"' ],  
14868     [    8226, "*" ],  
14869     [    8230, "..." ]
14870 ]; 
14871
14872     /*
14873  * - LGPL
14874  *
14875  * HtmlEditor
14876  * 
14877  */
14878
14879 /**
14880  * @class Roo.bootstrap.HtmlEditor
14881  * @extends Roo.bootstrap.TextArea
14882  * Bootstrap HtmlEditor class
14883
14884  * @constructor
14885  * Create a new HtmlEditor
14886  * @param {Object} config The config object
14887  */
14888
14889 Roo.bootstrap.HtmlEditor = function(config){
14890     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14891     if (!this.toolbars) {
14892         this.toolbars = [];
14893     }
14894     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14895     this.addEvents({
14896             /**
14897              * @event initialize
14898              * Fires when the editor is fully initialized (including the iframe)
14899              * @param {HtmlEditor} this
14900              */
14901             initialize: true,
14902             /**
14903              * @event activate
14904              * Fires when the editor is first receives the focus. Any insertion must wait
14905              * until after this event.
14906              * @param {HtmlEditor} this
14907              */
14908             activate: true,
14909              /**
14910              * @event beforesync
14911              * Fires before the textarea is updated with content from the editor iframe. Return false
14912              * to cancel the sync.
14913              * @param {HtmlEditor} this
14914              * @param {String} html
14915              */
14916             beforesync: true,
14917              /**
14918              * @event beforepush
14919              * Fires before the iframe editor is updated with content from the textarea. Return false
14920              * to cancel the push.
14921              * @param {HtmlEditor} this
14922              * @param {String} html
14923              */
14924             beforepush: true,
14925              /**
14926              * @event sync
14927              * Fires when the textarea is updated with content from the editor iframe.
14928              * @param {HtmlEditor} this
14929              * @param {String} html
14930              */
14931             sync: true,
14932              /**
14933              * @event push
14934              * Fires when the iframe editor is updated with content from the textarea.
14935              * @param {HtmlEditor} this
14936              * @param {String} html
14937              */
14938             push: true,
14939              /**
14940              * @event editmodechange
14941              * Fires when the editor switches edit modes
14942              * @param {HtmlEditor} this
14943              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14944              */
14945             editmodechange: true,
14946             /**
14947              * @event editorevent
14948              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14949              * @param {HtmlEditor} this
14950              */
14951             editorevent: true,
14952             /**
14953              * @event firstfocus
14954              * Fires when on first focus - needed by toolbars..
14955              * @param {HtmlEditor} this
14956              */
14957             firstfocus: true,
14958             /**
14959              * @event autosave
14960              * Auto save the htmlEditor value as a file into Events
14961              * @param {HtmlEditor} this
14962              */
14963             autosave: true,
14964             /**
14965              * @event savedpreview
14966              * preview the saved version of htmlEditor
14967              * @param {HtmlEditor} this
14968              */
14969             savedpreview: true
14970         });
14971 };
14972
14973
14974 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14975     
14976     
14977       /**
14978      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14979      */
14980     toolbars : false,
14981    
14982      /**
14983      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14984      *                        Roo.resizable.
14985      */
14986     resizable : false,
14987      /**
14988      * @cfg {Number} height (in pixels)
14989      */   
14990     height: 300,
14991    /**
14992      * @cfg {Number} width (in pixels)
14993      */   
14994     width: false,
14995     
14996     /**
14997      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14998      * 
14999      */
15000     stylesheets: false,
15001     
15002     // id of frame..
15003     frameId: false,
15004     
15005     // private properties
15006     validationEvent : false,
15007     deferHeight: true,
15008     initialized : false,
15009     activated : false,
15010     
15011     onFocus : Roo.emptyFn,
15012     iframePad:3,
15013     hideMode:'offsets',
15014     
15015     
15016     tbContainer : false,
15017     
15018     toolbarContainer :function() {
15019         return this.wrap.select('.x-html-editor-tb',true).first();
15020     },
15021
15022     /**
15023      * Protected method that will not generally be called directly. It
15024      * is called when the editor creates its toolbar. Override this method if you need to
15025      * add custom toolbar buttons.
15026      * @param {HtmlEditor} editor
15027      */
15028     createToolbar : function(){
15029         
15030         Roo.log("create toolbars");
15031         
15032         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15033         this.toolbars[0].render(this.toolbarContainer());
15034         
15035         return;
15036         
15037 //        if (!editor.toolbars || !editor.toolbars.length) {
15038 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15039 //        }
15040 //        
15041 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15042 //            editor.toolbars[i] = Roo.factory(
15043 //                    typeof(editor.toolbars[i]) == 'string' ?
15044 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15045 //                Roo.bootstrap.HtmlEditor);
15046 //            editor.toolbars[i].init(editor);
15047 //        }
15048     },
15049
15050      
15051     // private
15052     onRender : function(ct, position)
15053     {
15054        // Roo.log("Call onRender: " + this.xtype);
15055         var _t = this;
15056         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15057       
15058         this.wrap = this.inputEl().wrap({
15059             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15060         });
15061         
15062         this.editorcore.onRender(ct, position);
15063          
15064         if (this.resizable) {
15065             this.resizeEl = new Roo.Resizable(this.wrap, {
15066                 pinned : true,
15067                 wrap: true,
15068                 dynamic : true,
15069                 minHeight : this.height,
15070                 height: this.height,
15071                 handles : this.resizable,
15072                 width: this.width,
15073                 listeners : {
15074                     resize : function(r, w, h) {
15075                         _t.onResize(w,h); // -something
15076                     }
15077                 }
15078             });
15079             
15080         }
15081         this.createToolbar(this);
15082        
15083         
15084         if(!this.width && this.resizable){
15085             this.setSize(this.wrap.getSize());
15086         }
15087         if (this.resizeEl) {
15088             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15089             // should trigger onReize..
15090         }
15091         
15092     },
15093
15094     // private
15095     onResize : function(w, h)
15096     {
15097         Roo.log('resize: ' +w + ',' + h );
15098         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15099         var ew = false;
15100         var eh = false;
15101         
15102         if(this.inputEl() ){
15103             if(typeof w == 'number'){
15104                 var aw = w - this.wrap.getFrameWidth('lr');
15105                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15106                 ew = aw;
15107             }
15108             if(typeof h == 'number'){
15109                  var tbh = -11;  // fixme it needs to tool bar size!
15110                 for (var i =0; i < this.toolbars.length;i++) {
15111                     // fixme - ask toolbars for heights?
15112                     tbh += this.toolbars[i].el.getHeight();
15113                     //if (this.toolbars[i].footer) {
15114                     //    tbh += this.toolbars[i].footer.el.getHeight();
15115                     //}
15116                 }
15117               
15118                 
15119                 
15120                 
15121                 
15122                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15123                 ah -= 5; // knock a few pixes off for look..
15124                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15125                 var eh = ah;
15126             }
15127         }
15128         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15129         this.editorcore.onResize(ew,eh);
15130         
15131     },
15132
15133     /**
15134      * Toggles the editor between standard and source edit mode.
15135      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15136      */
15137     toggleSourceEdit : function(sourceEditMode)
15138     {
15139         this.editorcore.toggleSourceEdit(sourceEditMode);
15140         
15141         if(this.editorcore.sourceEditMode){
15142             Roo.log('editor - showing textarea');
15143             
15144 //            Roo.log('in');
15145 //            Roo.log(this.syncValue());
15146             this.syncValue();
15147             this.inputEl().removeClass('hide');
15148             this.inputEl().dom.removeAttribute('tabIndex');
15149             this.inputEl().focus();
15150         }else{
15151             Roo.log('editor - hiding textarea');
15152 //            Roo.log('out')
15153 //            Roo.log(this.pushValue()); 
15154             this.pushValue();
15155             
15156             this.inputEl().addClass('hide');
15157             this.inputEl().dom.setAttribute('tabIndex', -1);
15158             //this.deferFocus();
15159         }
15160          
15161         if(this.resizable){
15162             this.setSize(this.wrap.getSize());
15163         }
15164         
15165         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15166     },
15167  
15168     // private (for BoxComponent)
15169     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15170
15171     // private (for BoxComponent)
15172     getResizeEl : function(){
15173         return this.wrap;
15174     },
15175
15176     // private (for BoxComponent)
15177     getPositionEl : function(){
15178         return this.wrap;
15179     },
15180
15181     // private
15182     initEvents : function(){
15183         this.originalValue = this.getValue();
15184     },
15185
15186 //    /**
15187 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15188 //     * @method
15189 //     */
15190 //    markInvalid : Roo.emptyFn,
15191 //    /**
15192 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15193 //     * @method
15194 //     */
15195 //    clearInvalid : Roo.emptyFn,
15196
15197     setValue : function(v){
15198         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15199         this.editorcore.pushValue();
15200     },
15201
15202      
15203     // private
15204     deferFocus : function(){
15205         this.focus.defer(10, this);
15206     },
15207
15208     // doc'ed in Field
15209     focus : function(){
15210         this.editorcore.focus();
15211         
15212     },
15213       
15214
15215     // private
15216     onDestroy : function(){
15217         
15218         
15219         
15220         if(this.rendered){
15221             
15222             for (var i =0; i < this.toolbars.length;i++) {
15223                 // fixme - ask toolbars for heights?
15224                 this.toolbars[i].onDestroy();
15225             }
15226             
15227             this.wrap.dom.innerHTML = '';
15228             this.wrap.remove();
15229         }
15230     },
15231
15232     // private
15233     onFirstFocus : function(){
15234         //Roo.log("onFirstFocus");
15235         this.editorcore.onFirstFocus();
15236          for (var i =0; i < this.toolbars.length;i++) {
15237             this.toolbars[i].onFirstFocus();
15238         }
15239         
15240     },
15241     
15242     // private
15243     syncValue : function()
15244     {   
15245         this.editorcore.syncValue();
15246     },
15247     
15248     pushValue : function()
15249     {   
15250         this.editorcore.pushValue();
15251     }
15252      
15253     
15254     // hide stuff that is not compatible
15255     /**
15256      * @event blur
15257      * @hide
15258      */
15259     /**
15260      * @event change
15261      * @hide
15262      */
15263     /**
15264      * @event focus
15265      * @hide
15266      */
15267     /**
15268      * @event specialkey
15269      * @hide
15270      */
15271     /**
15272      * @cfg {String} fieldClass @hide
15273      */
15274     /**
15275      * @cfg {String} focusClass @hide
15276      */
15277     /**
15278      * @cfg {String} autoCreate @hide
15279      */
15280     /**
15281      * @cfg {String} inputType @hide
15282      */
15283     /**
15284      * @cfg {String} invalidClass @hide
15285      */
15286     /**
15287      * @cfg {String} invalidText @hide
15288      */
15289     /**
15290      * @cfg {String} msgFx @hide
15291      */
15292     /**
15293      * @cfg {String} validateOnBlur @hide
15294      */
15295 });
15296  
15297     
15298    
15299    
15300    
15301       
15302
15303 /**
15304  * @class Roo.bootstrap.HtmlEditorToolbar1
15305  * Basic Toolbar
15306  * 
15307  * Usage:
15308  *
15309  new Roo.bootstrap.HtmlEditor({
15310     ....
15311     toolbars : [
15312         new Roo.bootstrap.HtmlEditorToolbar1({
15313             disable : { fonts: 1 , format: 1, ..., ... , ...],
15314             btns : [ .... ]
15315         })
15316     }
15317      
15318  * 
15319  * @cfg {Object} disable List of elements to disable..
15320  * @cfg {Array} btns List of additional buttons.
15321  * 
15322  * 
15323  * NEEDS Extra CSS? 
15324  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15325  */
15326  
15327 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15328 {
15329     
15330     Roo.apply(this, config);
15331     
15332     // default disabled, based on 'good practice'..
15333     this.disable = this.disable || {};
15334     Roo.applyIf(this.disable, {
15335         fontSize : true,
15336         colors : true,
15337         specialElements : true
15338     });
15339     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15340     
15341     this.editor = config.editor;
15342     this.editorcore = config.editor.editorcore;
15343     
15344     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15345     
15346     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15347     // dont call parent... till later.
15348 }
15349 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
15350     
15351     
15352     bar : true,
15353     
15354     editor : false,
15355     editorcore : false,
15356     
15357     
15358     formats : [
15359         "p" ,  
15360         "h1","h2","h3","h4","h5","h6", 
15361         "pre", "code", 
15362         "abbr", "acronym", "address", "cite", "samp", "var",
15363         'div','span'
15364     ],
15365     
15366     onRender : function(ct, position)
15367     {
15368        // Roo.log("Call onRender: " + this.xtype);
15369         
15370        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15371        Roo.log(this.el);
15372        this.el.dom.style.marginBottom = '0';
15373        var _this = this;
15374        var editorcore = this.editorcore;
15375        var editor= this.editor;
15376        
15377        var children = [];
15378        var btn = function(id,cmd , toggle, handler){
15379        
15380             var  event = toggle ? 'toggle' : 'click';
15381        
15382             var a = {
15383                 size : 'sm',
15384                 xtype: 'Button',
15385                 xns: Roo.bootstrap,
15386                 glyphicon : id,
15387                 cmd : id || cmd,
15388                 enableToggle:toggle !== false,
15389                 //html : 'submit'
15390                 pressed : toggle ? false : null,
15391                 listeners : {}
15392             }
15393             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15394                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15395             }
15396             children.push(a);
15397             return a;
15398        }
15399         
15400         var style = {
15401                 xtype: 'Button',
15402                 size : 'sm',
15403                 xns: Roo.bootstrap,
15404                 glyphicon : 'font',
15405                 //html : 'submit'
15406                 menu : {
15407                     xtype: 'Menu',
15408                     xns: Roo.bootstrap,
15409                     items:  []
15410                 }
15411         };
15412         Roo.each(this.formats, function(f) {
15413             style.menu.items.push({
15414                 xtype :'MenuItem',
15415                 xns: Roo.bootstrap,
15416                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15417                 tagname : f,
15418                 listeners : {
15419                     click : function()
15420                     {
15421                         editorcore.insertTag(this.tagname);
15422                         editor.focus();
15423                     }
15424                 }
15425                 
15426             });
15427         });
15428          children.push(style);   
15429             
15430             
15431         btn('bold',false,true);
15432         btn('italic',false,true);
15433         btn('align-left', 'justifyleft',true);
15434         btn('align-center', 'justifycenter',true);
15435         btn('align-right' , 'justifyright',true);
15436         btn('link', false, false, function(btn) {
15437             //Roo.log("create link?");
15438             var url = prompt(this.createLinkText, this.defaultLinkValue);
15439             if(url && url != 'http:/'+'/'){
15440                 this.editorcore.relayCmd('createlink', url);
15441             }
15442         }),
15443         btn('list','insertunorderedlist',true);
15444         btn('pencil', false,true, function(btn){
15445                 Roo.log(this);
15446                 
15447                 this.toggleSourceEdit(btn.pressed);
15448         });
15449         /*
15450         var cog = {
15451                 xtype: 'Button',
15452                 size : 'sm',
15453                 xns: Roo.bootstrap,
15454                 glyphicon : 'cog',
15455                 //html : 'submit'
15456                 menu : {
15457                     xtype: 'Menu',
15458                     xns: Roo.bootstrap,
15459                     items:  []
15460                 }
15461         };
15462         
15463         cog.menu.items.push({
15464             xtype :'MenuItem',
15465             xns: Roo.bootstrap,
15466             html : Clean styles,
15467             tagname : f,
15468             listeners : {
15469                 click : function()
15470                 {
15471                     editorcore.insertTag(this.tagname);
15472                     editor.focus();
15473                 }
15474             }
15475             
15476         });
15477        */
15478         
15479          
15480        this.xtype = 'Navbar';
15481         
15482         for(var i=0;i< children.length;i++) {
15483             
15484             this.buttons.add(this.addxtypeChild(children[i]));
15485             
15486         }
15487         
15488         editor.on('editorevent', this.updateToolbar, this);
15489     },
15490     onBtnClick : function(id)
15491     {
15492        this.editorcore.relayCmd(id);
15493        this.editorcore.focus();
15494     },
15495     
15496     /**
15497      * Protected method that will not generally be called directly. It triggers
15498      * a toolbar update by reading the markup state of the current selection in the editor.
15499      */
15500     updateToolbar: function(){
15501
15502         if(!this.editorcore.activated){
15503             this.editor.onFirstFocus(); // is this neeed?
15504             return;
15505         }
15506
15507         var btns = this.buttons; 
15508         var doc = this.editorcore.doc;
15509         btns.get('bold').setActive(doc.queryCommandState('bold'));
15510         btns.get('italic').setActive(doc.queryCommandState('italic'));
15511         //btns.get('underline').setActive(doc.queryCommandState('underline'));
15512         
15513         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15514         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15515         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15516         
15517         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15518         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15519          /*
15520         
15521         var ans = this.editorcore.getAllAncestors();
15522         if (this.formatCombo) {
15523             
15524             
15525             var store = this.formatCombo.store;
15526             this.formatCombo.setValue("");
15527             for (var i =0; i < ans.length;i++) {
15528                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15529                     // select it..
15530                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15531                     break;
15532                 }
15533             }
15534         }
15535         
15536         
15537         
15538         // hides menus... - so this cant be on a menu...
15539         Roo.bootstrap.MenuMgr.hideAll();
15540         */
15541         Roo.bootstrap.MenuMgr.hideAll();
15542         //this.editorsyncValue();
15543     },
15544     onFirstFocus: function() {
15545         this.buttons.each(function(item){
15546            item.enable();
15547         });
15548     },
15549     toggleSourceEdit : function(sourceEditMode){
15550         
15551           
15552         if(sourceEditMode){
15553             Roo.log("disabling buttons");
15554            this.buttons.each( function(item){
15555                 if(item.cmd != 'pencil'){
15556                     item.disable();
15557                 }
15558             });
15559           
15560         }else{
15561             Roo.log("enabling buttons");
15562             if(this.editorcore.initialized){
15563                 this.buttons.each( function(item){
15564                     item.enable();
15565                 });
15566             }
15567             
15568         }
15569         Roo.log("calling toggole on editor");
15570         // tell the editor that it's been pressed..
15571         this.editor.toggleSourceEdit(sourceEditMode);
15572        
15573     }
15574 });
15575
15576
15577
15578
15579
15580 /**
15581  * @class Roo.bootstrap.Table.AbstractSelectionModel
15582  * @extends Roo.util.Observable
15583  * Abstract base class for grid SelectionModels.  It provides the interface that should be
15584  * implemented by descendant classes.  This class should not be directly instantiated.
15585  * @constructor
15586  */
15587 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15588     this.locked = false;
15589     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15590 };
15591
15592
15593 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
15594     /** @ignore Called by the grid automatically. Do not call directly. */
15595     init : function(grid){
15596         this.grid = grid;
15597         this.initEvents();
15598     },
15599
15600     /**
15601      * Locks the selections.
15602      */
15603     lock : function(){
15604         this.locked = true;
15605     },
15606
15607     /**
15608      * Unlocks the selections.
15609      */
15610     unlock : function(){
15611         this.locked = false;
15612     },
15613
15614     /**
15615      * Returns true if the selections are locked.
15616      * @return {Boolean}
15617      */
15618     isLocked : function(){
15619         return this.locked;
15620     }
15621 });
15622 /**
15623  * @class Roo.bootstrap.Table.ColumnModel
15624  * @extends Roo.util.Observable
15625  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15626  * the columns in the table.
15627  
15628  * @constructor
15629  * @param {Object} config An Array of column config objects. See this class's
15630  * config objects for details.
15631 */
15632 Roo.bootstrap.Table.ColumnModel = function(config){
15633         /**
15634      * The config passed into the constructor
15635      */
15636     this.config = config;
15637     this.lookup = {};
15638
15639     // if no id, create one
15640     // if the column does not have a dataIndex mapping,
15641     // map it to the order it is in the config
15642     for(var i = 0, len = config.length; i < len; i++){
15643         var c = config[i];
15644         if(typeof c.dataIndex == "undefined"){
15645             c.dataIndex = i;
15646         }
15647         if(typeof c.renderer == "string"){
15648             c.renderer = Roo.util.Format[c.renderer];
15649         }
15650         if(typeof c.id == "undefined"){
15651             c.id = Roo.id();
15652         }
15653 //        if(c.editor && c.editor.xtype){
15654 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15655 //        }
15656 //        if(c.editor && c.editor.isFormField){
15657 //            c.editor = new Roo.grid.GridEditor(c.editor);
15658 //        }
15659
15660         this.lookup[c.id] = c;
15661     }
15662
15663     /**
15664      * The width of columns which have no width specified (defaults to 100)
15665      * @type Number
15666      */
15667     this.defaultWidth = 100;
15668
15669     /**
15670      * Default sortable of columns which have no sortable specified (defaults to false)
15671      * @type Boolean
15672      */
15673     this.defaultSortable = false;
15674
15675     this.addEvents({
15676         /**
15677              * @event widthchange
15678              * Fires when the width of a column changes.
15679              * @param {ColumnModel} this
15680              * @param {Number} columnIndex The column index
15681              * @param {Number} newWidth The new width
15682              */
15683             "widthchange": true,
15684         /**
15685              * @event headerchange
15686              * Fires when the text of a header changes.
15687              * @param {ColumnModel} this
15688              * @param {Number} columnIndex The column index
15689              * @param {Number} newText The new header text
15690              */
15691             "headerchange": true,
15692         /**
15693              * @event hiddenchange
15694              * Fires when a column is hidden or "unhidden".
15695              * @param {ColumnModel} this
15696              * @param {Number} columnIndex The column index
15697              * @param {Boolean} hidden true if hidden, false otherwise
15698              */
15699             "hiddenchange": true,
15700             /**
15701          * @event columnmoved
15702          * Fires when a column is moved.
15703          * @param {ColumnModel} this
15704          * @param {Number} oldIndex
15705          * @param {Number} newIndex
15706          */
15707         "columnmoved" : true,
15708         /**
15709          * @event columlockchange
15710          * Fires when a column's locked state is changed
15711          * @param {ColumnModel} this
15712          * @param {Number} colIndex
15713          * @param {Boolean} locked true if locked
15714          */
15715         "columnlockchange" : true
15716     });
15717     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15718 };
15719 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15720     /**
15721      * @cfg {String} header The header text to display in the Grid view.
15722      */
15723     /**
15724      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15725      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15726      * specified, the column's index is used as an index into the Record's data Array.
15727      */
15728     /**
15729      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15730      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15731      */
15732     /**
15733      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15734      * Defaults to the value of the {@link #defaultSortable} property.
15735      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15736      */
15737     /**
15738      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
15739      */
15740     /**
15741      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
15742      */
15743     /**
15744      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15745      */
15746     /**
15747      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15748      */
15749     /**
15750      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15751      * given the cell's data value. See {@link #setRenderer}. If not specified, the
15752      * default renderer uses the raw data value.
15753      */
15754     /**
15755      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
15756      */
15757
15758     /**
15759      * Returns the id of the column at the specified index.
15760      * @param {Number} index The column index
15761      * @return {String} the id
15762      */
15763     getColumnId : function(index){
15764         return this.config[index].id;
15765     },
15766
15767     /**
15768      * Returns the column for a specified id.
15769      * @param {String} id The column id
15770      * @return {Object} the column
15771      */
15772     getColumnById : function(id){
15773         return this.lookup[id];
15774     },
15775
15776     
15777     /**
15778      * Returns the column for a specified dataIndex.
15779      * @param {String} dataIndex The column dataIndex
15780      * @return {Object|Boolean} the column or false if not found
15781      */
15782     getColumnByDataIndex: function(dataIndex){
15783         var index = this.findColumnIndex(dataIndex);
15784         return index > -1 ? this.config[index] : false;
15785     },
15786     
15787     /**
15788      * Returns the index for a specified column id.
15789      * @param {String} id The column id
15790      * @return {Number} the index, or -1 if not found
15791      */
15792     getIndexById : function(id){
15793         for(var i = 0, len = this.config.length; i < len; i++){
15794             if(this.config[i].id == id){
15795                 return i;
15796             }
15797         }
15798         return -1;
15799     },
15800     
15801     /**
15802      * Returns the index for a specified column dataIndex.
15803      * @param {String} dataIndex The column dataIndex
15804      * @return {Number} the index, or -1 if not found
15805      */
15806     
15807     findColumnIndex : function(dataIndex){
15808         for(var i = 0, len = this.config.length; i < len; i++){
15809             if(this.config[i].dataIndex == dataIndex){
15810                 return i;
15811             }
15812         }
15813         return -1;
15814     },
15815     
15816     
15817     moveColumn : function(oldIndex, newIndex){
15818         var c = this.config[oldIndex];
15819         this.config.splice(oldIndex, 1);
15820         this.config.splice(newIndex, 0, c);
15821         this.dataMap = null;
15822         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15823     },
15824
15825     isLocked : function(colIndex){
15826         return this.config[colIndex].locked === true;
15827     },
15828
15829     setLocked : function(colIndex, value, suppressEvent){
15830         if(this.isLocked(colIndex) == value){
15831             return;
15832         }
15833         this.config[colIndex].locked = value;
15834         if(!suppressEvent){
15835             this.fireEvent("columnlockchange", this, colIndex, value);
15836         }
15837     },
15838
15839     getTotalLockedWidth : function(){
15840         var totalWidth = 0;
15841         for(var i = 0; i < this.config.length; i++){
15842             if(this.isLocked(i) && !this.isHidden(i)){
15843                 this.totalWidth += this.getColumnWidth(i);
15844             }
15845         }
15846         return totalWidth;
15847     },
15848
15849     getLockedCount : function(){
15850         for(var i = 0, len = this.config.length; i < len; i++){
15851             if(!this.isLocked(i)){
15852                 return i;
15853             }
15854         }
15855     },
15856
15857     /**
15858      * Returns the number of columns.
15859      * @return {Number}
15860      */
15861     getColumnCount : function(visibleOnly){
15862         if(visibleOnly === true){
15863             var c = 0;
15864             for(var i = 0, len = this.config.length; i < len; i++){
15865                 if(!this.isHidden(i)){
15866                     c++;
15867                 }
15868             }
15869             return c;
15870         }
15871         return this.config.length;
15872     },
15873
15874     /**
15875      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15876      * @param {Function} fn
15877      * @param {Object} scope (optional)
15878      * @return {Array} result
15879      */
15880     getColumnsBy : function(fn, scope){
15881         var r = [];
15882         for(var i = 0, len = this.config.length; i < len; i++){
15883             var c = this.config[i];
15884             if(fn.call(scope||this, c, i) === true){
15885                 r[r.length] = c;
15886             }
15887         }
15888         return r;
15889     },
15890
15891     /**
15892      * Returns true if the specified column is sortable.
15893      * @param {Number} col The column index
15894      * @return {Boolean}
15895      */
15896     isSortable : function(col){
15897         if(typeof this.config[col].sortable == "undefined"){
15898             return this.defaultSortable;
15899         }
15900         return this.config[col].sortable;
15901     },
15902
15903     /**
15904      * Returns the rendering (formatting) function defined for the column.
15905      * @param {Number} col The column index.
15906      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15907      */
15908     getRenderer : function(col){
15909         if(!this.config[col].renderer){
15910             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15911         }
15912         return this.config[col].renderer;
15913     },
15914
15915     /**
15916      * Sets the rendering (formatting) function for a column.
15917      * @param {Number} col The column index
15918      * @param {Function} fn The function to use to process the cell's raw data
15919      * to return HTML markup for the grid view. The render function is called with
15920      * the following parameters:<ul>
15921      * <li>Data value.</li>
15922      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15923      * <li>css A CSS style string to apply to the table cell.</li>
15924      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15925      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15926      * <li>Row index</li>
15927      * <li>Column index</li>
15928      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15929      */
15930     setRenderer : function(col, fn){
15931         this.config[col].renderer = fn;
15932     },
15933
15934     /**
15935      * Returns the width for the specified column.
15936      * @param {Number} col The column index
15937      * @return {Number}
15938      */
15939     getColumnWidth : function(col){
15940         return this.config[col].width * 1 || this.defaultWidth;
15941     },
15942
15943     /**
15944      * Sets the width for a column.
15945      * @param {Number} col The column index
15946      * @param {Number} width The new width
15947      */
15948     setColumnWidth : function(col, width, suppressEvent){
15949         this.config[col].width = width;
15950         this.totalWidth = null;
15951         if(!suppressEvent){
15952              this.fireEvent("widthchange", this, col, width);
15953         }
15954     },
15955
15956     /**
15957      * Returns the total width of all columns.
15958      * @param {Boolean} includeHidden True to include hidden column widths
15959      * @return {Number}
15960      */
15961     getTotalWidth : function(includeHidden){
15962         if(!this.totalWidth){
15963             this.totalWidth = 0;
15964             for(var i = 0, len = this.config.length; i < len; i++){
15965                 if(includeHidden || !this.isHidden(i)){
15966                     this.totalWidth += this.getColumnWidth(i);
15967                 }
15968             }
15969         }
15970         return this.totalWidth;
15971     },
15972
15973     /**
15974      * Returns the header for the specified column.
15975      * @param {Number} col The column index
15976      * @return {String}
15977      */
15978     getColumnHeader : function(col){
15979         return this.config[col].header;
15980     },
15981
15982     /**
15983      * Sets the header for a column.
15984      * @param {Number} col The column index
15985      * @param {String} header The new header
15986      */
15987     setColumnHeader : function(col, header){
15988         this.config[col].header = header;
15989         this.fireEvent("headerchange", this, col, header);
15990     },
15991
15992     /**
15993      * Returns the tooltip for the specified column.
15994      * @param {Number} col The column index
15995      * @return {String}
15996      */
15997     getColumnTooltip : function(col){
15998             return this.config[col].tooltip;
15999     },
16000     /**
16001      * Sets the tooltip for a column.
16002      * @param {Number} col The column index
16003      * @param {String} tooltip The new tooltip
16004      */
16005     setColumnTooltip : function(col, tooltip){
16006             this.config[col].tooltip = tooltip;
16007     },
16008
16009     /**
16010      * Returns the dataIndex for the specified column.
16011      * @param {Number} col The column index
16012      * @return {Number}
16013      */
16014     getDataIndex : function(col){
16015         return this.config[col].dataIndex;
16016     },
16017
16018     /**
16019      * Sets the dataIndex for a column.
16020      * @param {Number} col The column index
16021      * @param {Number} dataIndex The new dataIndex
16022      */
16023     setDataIndex : function(col, dataIndex){
16024         this.config[col].dataIndex = dataIndex;
16025     },
16026
16027     
16028     
16029     /**
16030      * Returns true if the cell is editable.
16031      * @param {Number} colIndex The column index
16032      * @param {Number} rowIndex The row index
16033      * @return {Boolean}
16034      */
16035     isCellEditable : function(colIndex, rowIndex){
16036         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16037     },
16038
16039     /**
16040      * Returns the editor defined for the cell/column.
16041      * return false or null to disable editing.
16042      * @param {Number} colIndex The column index
16043      * @param {Number} rowIndex The row index
16044      * @return {Object}
16045      */
16046     getCellEditor : function(colIndex, rowIndex){
16047         return this.config[colIndex].editor;
16048     },
16049
16050     /**
16051      * Sets if a column is editable.
16052      * @param {Number} col The column index
16053      * @param {Boolean} editable True if the column is editable
16054      */
16055     setEditable : function(col, editable){
16056         this.config[col].editable = editable;
16057     },
16058
16059
16060     /**
16061      * Returns true if the column is hidden.
16062      * @param {Number} colIndex The column index
16063      * @return {Boolean}
16064      */
16065     isHidden : function(colIndex){
16066         return this.config[colIndex].hidden;
16067     },
16068
16069
16070     /**
16071      * Returns true if the column width cannot be changed
16072      */
16073     isFixed : function(colIndex){
16074         return this.config[colIndex].fixed;
16075     },
16076
16077     /**
16078      * Returns true if the column can be resized
16079      * @return {Boolean}
16080      */
16081     isResizable : function(colIndex){
16082         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16083     },
16084     /**
16085      * Sets if a column is hidden.
16086      * @param {Number} colIndex The column index
16087      * @param {Boolean} hidden True if the column is hidden
16088      */
16089     setHidden : function(colIndex, hidden){
16090         this.config[colIndex].hidden = hidden;
16091         this.totalWidth = null;
16092         this.fireEvent("hiddenchange", this, colIndex, hidden);
16093     },
16094
16095     /**
16096      * Sets the editor for a column.
16097      * @param {Number} col The column index
16098      * @param {Object} editor The editor object
16099      */
16100     setEditor : function(col, editor){
16101         this.config[col].editor = editor;
16102     }
16103 });
16104
16105 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16106         if(typeof value == "string" && value.length < 1){
16107             return "&#160;";
16108         }
16109         return value;
16110 };
16111
16112 // Alias for backwards compatibility
16113 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16114
16115 /**
16116  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16117  * @class Roo.bootstrap.Table.RowSelectionModel
16118  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16119  * It supports multiple selections and keyboard selection/navigation. 
16120  * @constructor
16121  * @param {Object} config
16122  */
16123
16124 Roo.bootstrap.Table.RowSelectionModel = function(config){
16125     Roo.apply(this, config);
16126     this.selections = new Roo.util.MixedCollection(false, function(o){
16127         return o.id;
16128     });
16129
16130     this.last = false;
16131     this.lastActive = false;
16132
16133     this.addEvents({
16134         /**
16135              * @event selectionchange
16136              * Fires when the selection changes
16137              * @param {SelectionModel} this
16138              */
16139             "selectionchange" : true,
16140         /**
16141              * @event afterselectionchange
16142              * Fires after the selection changes (eg. by key press or clicking)
16143              * @param {SelectionModel} this
16144              */
16145             "afterselectionchange" : true,
16146         /**
16147              * @event beforerowselect
16148              * Fires when a row is selected being selected, return false to cancel.
16149              * @param {SelectionModel} this
16150              * @param {Number} rowIndex The selected index
16151              * @param {Boolean} keepExisting False if other selections will be cleared
16152              */
16153             "beforerowselect" : true,
16154         /**
16155              * @event rowselect
16156              * Fires when a row is selected.
16157              * @param {SelectionModel} this
16158              * @param {Number} rowIndex The selected index
16159              * @param {Roo.data.Record} r The record
16160              */
16161             "rowselect" : true,
16162         /**
16163              * @event rowdeselect
16164              * Fires when a row is deselected.
16165              * @param {SelectionModel} this
16166              * @param {Number} rowIndex The selected index
16167              */
16168         "rowdeselect" : true
16169     });
16170     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16171     this.locked = false;
16172 };
16173
16174 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16175     /**
16176      * @cfg {Boolean} singleSelect
16177      * True to allow selection of only one row at a time (defaults to false)
16178      */
16179     singleSelect : false,
16180
16181     // private
16182     initEvents : function(){
16183
16184         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16185             this.grid.on("mousedown", this.handleMouseDown, this);
16186         }else{ // allow click to work like normal
16187             this.grid.on("rowclick", this.handleDragableRowClick, this);
16188         }
16189
16190         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16191             "up" : function(e){
16192                 if(!e.shiftKey){
16193                     this.selectPrevious(e.shiftKey);
16194                 }else if(this.last !== false && this.lastActive !== false){
16195                     var last = this.last;
16196                     this.selectRange(this.last,  this.lastActive-1);
16197                     this.grid.getView().focusRow(this.lastActive);
16198                     if(last !== false){
16199                         this.last = last;
16200                     }
16201                 }else{
16202                     this.selectFirstRow();
16203                 }
16204                 this.fireEvent("afterselectionchange", this);
16205             },
16206             "down" : function(e){
16207                 if(!e.shiftKey){
16208                     this.selectNext(e.shiftKey);
16209                 }else if(this.last !== false && this.lastActive !== false){
16210                     var last = this.last;
16211                     this.selectRange(this.last,  this.lastActive+1);
16212                     this.grid.getView().focusRow(this.lastActive);
16213                     if(last !== false){
16214                         this.last = last;
16215                     }
16216                 }else{
16217                     this.selectFirstRow();
16218                 }
16219                 this.fireEvent("afterselectionchange", this);
16220             },
16221             scope: this
16222         });
16223
16224         var view = this.grid.view;
16225         view.on("refresh", this.onRefresh, this);
16226         view.on("rowupdated", this.onRowUpdated, this);
16227         view.on("rowremoved", this.onRemove, this);
16228     },
16229
16230     // private
16231     onRefresh : function(){
16232         var ds = this.grid.dataSource, i, v = this.grid.view;
16233         var s = this.selections;
16234         s.each(function(r){
16235             if((i = ds.indexOfId(r.id)) != -1){
16236                 v.onRowSelect(i);
16237             }else{
16238                 s.remove(r);
16239             }
16240         });
16241     },
16242
16243     // private
16244     onRemove : function(v, index, r){
16245         this.selections.remove(r);
16246     },
16247
16248     // private
16249     onRowUpdated : function(v, index, r){
16250         if(this.isSelected(r)){
16251             v.onRowSelect(index);
16252         }
16253     },
16254
16255     /**
16256      * Select records.
16257      * @param {Array} records The records to select
16258      * @param {Boolean} keepExisting (optional) True to keep existing selections
16259      */
16260     selectRecords : function(records, keepExisting){
16261         if(!keepExisting){
16262             this.clearSelections();
16263         }
16264         var ds = this.grid.dataSource;
16265         for(var i = 0, len = records.length; i < len; i++){
16266             this.selectRow(ds.indexOf(records[i]), true);
16267         }
16268     },
16269
16270     /**
16271      * Gets the number of selected rows.
16272      * @return {Number}
16273      */
16274     getCount : function(){
16275         return this.selections.length;
16276     },
16277
16278     /**
16279      * Selects the first row in the grid.
16280      */
16281     selectFirstRow : function(){
16282         this.selectRow(0);
16283     },
16284
16285     /**
16286      * Select the last row.
16287      * @param {Boolean} keepExisting (optional) True to keep existing selections
16288      */
16289     selectLastRow : function(keepExisting){
16290         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16291     },
16292
16293     /**
16294      * Selects the row immediately following the last selected row.
16295      * @param {Boolean} keepExisting (optional) True to keep existing selections
16296      */
16297     selectNext : function(keepExisting){
16298         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16299             this.selectRow(this.last+1, keepExisting);
16300             this.grid.getView().focusRow(this.last);
16301         }
16302     },
16303
16304     /**
16305      * Selects the row that precedes the last selected row.
16306      * @param {Boolean} keepExisting (optional) True to keep existing selections
16307      */
16308     selectPrevious : function(keepExisting){
16309         if(this.last){
16310             this.selectRow(this.last-1, keepExisting);
16311             this.grid.getView().focusRow(this.last);
16312         }
16313     },
16314
16315     /**
16316      * Returns the selected records
16317      * @return {Array} Array of selected records
16318      */
16319     getSelections : function(){
16320         return [].concat(this.selections.items);
16321     },
16322
16323     /**
16324      * Returns the first selected record.
16325      * @return {Record}
16326      */
16327     getSelected : function(){
16328         return this.selections.itemAt(0);
16329     },
16330
16331
16332     /**
16333      * Clears all selections.
16334      */
16335     clearSelections : function(fast){
16336         if(this.locked) return;
16337         if(fast !== true){
16338             var ds = this.grid.dataSource;
16339             var s = this.selections;
16340             s.each(function(r){
16341                 this.deselectRow(ds.indexOfId(r.id));
16342             }, this);
16343             s.clear();
16344         }else{
16345             this.selections.clear();
16346         }
16347         this.last = false;
16348     },
16349
16350
16351     /**
16352      * Selects all rows.
16353      */
16354     selectAll : function(){
16355         if(this.locked) return;
16356         this.selections.clear();
16357         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16358             this.selectRow(i, true);
16359         }
16360     },
16361
16362     /**
16363      * Returns True if there is a selection.
16364      * @return {Boolean}
16365      */
16366     hasSelection : function(){
16367         return this.selections.length > 0;
16368     },
16369
16370     /**
16371      * Returns True if the specified row is selected.
16372      * @param {Number/Record} record The record or index of the record to check
16373      * @return {Boolean}
16374      */
16375     isSelected : function(index){
16376         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16377         return (r && this.selections.key(r.id) ? true : false);
16378     },
16379
16380     /**
16381      * Returns True if the specified record id is selected.
16382      * @param {String} id The id of record to check
16383      * @return {Boolean}
16384      */
16385     isIdSelected : function(id){
16386         return (this.selections.key(id) ? true : false);
16387     },
16388
16389     // private
16390     handleMouseDown : function(e, t){
16391         var view = this.grid.getView(), rowIndex;
16392         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16393             return;
16394         };
16395         if(e.shiftKey && this.last !== false){
16396             var last = this.last;
16397             this.selectRange(last, rowIndex, e.ctrlKey);
16398             this.last = last; // reset the last
16399             view.focusRow(rowIndex);
16400         }else{
16401             var isSelected = this.isSelected(rowIndex);
16402             if(e.button !== 0 && isSelected){
16403                 view.focusRow(rowIndex);
16404             }else if(e.ctrlKey && isSelected){
16405                 this.deselectRow(rowIndex);
16406             }else if(!isSelected){
16407                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16408                 view.focusRow(rowIndex);
16409             }
16410         }
16411         this.fireEvent("afterselectionchange", this);
16412     },
16413     // private
16414     handleDragableRowClick :  function(grid, rowIndex, e) 
16415     {
16416         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16417             this.selectRow(rowIndex, false);
16418             grid.view.focusRow(rowIndex);
16419              this.fireEvent("afterselectionchange", this);
16420         }
16421     },
16422     
16423     /**
16424      * Selects multiple rows.
16425      * @param {Array} rows Array of the indexes of the row to select
16426      * @param {Boolean} keepExisting (optional) True to keep existing selections
16427      */
16428     selectRows : function(rows, keepExisting){
16429         if(!keepExisting){
16430             this.clearSelections();
16431         }
16432         for(var i = 0, len = rows.length; i < len; i++){
16433             this.selectRow(rows[i], true);
16434         }
16435     },
16436
16437     /**
16438      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16439      * @param {Number} startRow The index of the first row in the range
16440      * @param {Number} endRow The index of the last row in the range
16441      * @param {Boolean} keepExisting (optional) True to retain existing selections
16442      */
16443     selectRange : function(startRow, endRow, keepExisting){
16444         if(this.locked) return;
16445         if(!keepExisting){
16446             this.clearSelections();
16447         }
16448         if(startRow <= endRow){
16449             for(var i = startRow; i <= endRow; i++){
16450                 this.selectRow(i, true);
16451             }
16452         }else{
16453             for(var i = startRow; i >= endRow; i--){
16454                 this.selectRow(i, true);
16455             }
16456         }
16457     },
16458
16459     /**
16460      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16461      * @param {Number} startRow The index of the first row in the range
16462      * @param {Number} endRow The index of the last row in the range
16463      */
16464     deselectRange : function(startRow, endRow, preventViewNotify){
16465         if(this.locked) return;
16466         for(var i = startRow; i <= endRow; i++){
16467             this.deselectRow(i, preventViewNotify);
16468         }
16469     },
16470
16471     /**
16472      * Selects a row.
16473      * @param {Number} row The index of the row to select
16474      * @param {Boolean} keepExisting (optional) True to keep existing selections
16475      */
16476     selectRow : function(index, keepExisting, preventViewNotify){
16477         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16478         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16479             if(!keepExisting || this.singleSelect){
16480                 this.clearSelections();
16481             }
16482             var r = this.grid.dataSource.getAt(index);
16483             this.selections.add(r);
16484             this.last = this.lastActive = index;
16485             if(!preventViewNotify){
16486                 this.grid.getView().onRowSelect(index);
16487             }
16488             this.fireEvent("rowselect", this, index, r);
16489             this.fireEvent("selectionchange", this);
16490         }
16491     },
16492
16493     /**
16494      * Deselects a row.
16495      * @param {Number} row The index of the row to deselect
16496      */
16497     deselectRow : function(index, preventViewNotify){
16498         if(this.locked) return;
16499         if(this.last == index){
16500             this.last = false;
16501         }
16502         if(this.lastActive == index){
16503             this.lastActive = false;
16504         }
16505         var r = this.grid.dataSource.getAt(index);
16506         this.selections.remove(r);
16507         if(!preventViewNotify){
16508             this.grid.getView().onRowDeselect(index);
16509         }
16510         this.fireEvent("rowdeselect", this, index);
16511         this.fireEvent("selectionchange", this);
16512     },
16513
16514     // private
16515     restoreLast : function(){
16516         if(this._last){
16517             this.last = this._last;
16518         }
16519     },
16520
16521     // private
16522     acceptsNav : function(row, col, cm){
16523         return !cm.isHidden(col) && cm.isCellEditable(col, row);
16524     },
16525
16526     // private
16527     onEditorKey : function(field, e){
16528         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16529         if(k == e.TAB){
16530             e.stopEvent();
16531             ed.completeEdit();
16532             if(e.shiftKey){
16533                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16534             }else{
16535                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16536             }
16537         }else if(k == e.ENTER && !e.ctrlKey){
16538             e.stopEvent();
16539             ed.completeEdit();
16540             if(e.shiftKey){
16541                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16542             }else{
16543                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16544             }
16545         }else if(k == e.ESC){
16546             ed.cancelEdit();
16547         }
16548         if(newCell){
16549             g.startEditing(newCell[0], newCell[1]);
16550         }
16551     }
16552 });/*
16553  * - LGPL
16554  *
16555  * element
16556  * 
16557  */
16558
16559 /**
16560  * @class Roo.bootstrap.MessageBar
16561  * @extends Roo.bootstrap.Component
16562  * Bootstrap MessageBar class
16563  * @cfg {String} html contents of the MessageBar
16564  * @cfg {String} weight (info | success | warning | danger) default info
16565  * @cfg {String} beforeClass insert the bar before the given class
16566  * @cfg {Boolean} closable (true | false) default false
16567  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16568  * 
16569  * @constructor
16570  * Create a new Element
16571  * @param {Object} config The config object
16572  */
16573
16574 Roo.bootstrap.MessageBar = function(config){
16575     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16576 };
16577
16578 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
16579     
16580     html: '',
16581     weight: 'info',
16582     closable: false,
16583     fixed: false,
16584     beforeClass: 'bootstrap-sticky-wrap',
16585     
16586     getAutoCreate : function(){
16587         
16588         var cfg = {
16589             tag: 'div',
16590             cls: 'alert alert-dismissable alert-' + this.weight,
16591             cn: [
16592                 {
16593                     tag: 'span',
16594                     cls: 'message',
16595                     html: this.html || ''
16596                 }
16597             ]
16598         }
16599         
16600         if(this.fixed){
16601             cfg.cls += ' alert-messages-fixed';
16602         }
16603         
16604         if(this.closable){
16605             cfg.cn.push({
16606                 tag: 'button',
16607                 cls: 'close',
16608                 html: 'x'
16609             });
16610         }
16611         
16612         return cfg;
16613     },
16614     
16615     onRender : function(ct, position)
16616     {
16617         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16618         
16619         if(!this.el){
16620             var cfg = Roo.apply({},  this.getAutoCreate());
16621             cfg.id = Roo.id();
16622             
16623             if (this.cls) {
16624                 cfg.cls += ' ' + this.cls;
16625             }
16626             if (this.style) {
16627                 cfg.style = this.style;
16628             }
16629             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16630             
16631             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16632         }
16633         
16634         this.el.select('>button.close').on('click', this.hide, this);
16635         
16636     },
16637     
16638     show : function()
16639     {
16640         if (!this.rendered) {
16641             this.render();
16642         }
16643         
16644         this.el.show();
16645         
16646         this.fireEvent('show', this);
16647         
16648     },
16649     
16650     hide : function()
16651     {
16652         if (!this.rendered) {
16653             this.render();
16654         }
16655         
16656         this.el.hide();
16657         
16658         this.fireEvent('hide', this);
16659     },
16660     
16661     update : function()
16662     {
16663 //        var e = this.el.dom.firstChild;
16664 //        
16665 //        if(this.closable){
16666 //            e = e.nextSibling;
16667 //        }
16668 //        
16669 //        e.data = this.html || '';
16670
16671         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16672     }
16673    
16674 });
16675
16676  
16677
16678