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: (this.imgResponsive) ? 'img-responsive' : '',
984             html : null
985         }
986         
987         cfg.html = this.html || cfg.html;
988         
989         cfg.src = this.src || cfg.src;
990         
991         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
992             cfg.cls += ' img-' + this.border;
993         }
994         
995         if(this.alt){
996             cfg.alt = this.alt;
997         }
998         
999         if(this.href){
1000             var a = {
1001                 tag: 'a',
1002                 href: this.href,
1003                 cn: [
1004                     cfg
1005                 ]
1006             }
1007             
1008             if(this.target){
1009                 a.target = this.target;
1010             }
1011             
1012         }
1013         
1014         
1015         return (this.href) ? a : cfg;
1016     },
1017     
1018     initEvents: function() {
1019         
1020         if(!this.href){
1021             this.el.on('click', this.onClick, this);
1022         }
1023     },
1024     
1025     onClick : function(e)
1026     {
1027         Roo.log('img onclick');
1028         this.fireEvent('click', this, e);
1029     }
1030    
1031 });
1032
1033  /*
1034  * - LGPL
1035  *
1036  * header
1037  * 
1038  */
1039
1040 /**
1041  * @class Roo.bootstrap.Header
1042  * @extends Roo.bootstrap.Component
1043  * Bootstrap Header class
1044  * @cfg {String} html content of header
1045  * @cfg {Number} level (1|2|3|4|5|6) default 1
1046  * 
1047  * @constructor
1048  * Create a new Header
1049  * @param {Object} config The config object
1050  */
1051
1052
1053 Roo.bootstrap.Header  = function(config){
1054     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1058     
1059     //href : false,
1060     html : false,
1061     level : 1,
1062     
1063     
1064     
1065     getAutoCreate : function(){
1066         
1067         var cfg = {
1068             tag: 'h' + (1 *this.level),
1069             html: this.html || 'fill in html'
1070         } ;
1071         
1072         return cfg;
1073     }
1074    
1075 });
1076
1077  
1078
1079  /*
1080  * Based on:
1081  * Ext JS Library 1.1.1
1082  * Copyright(c) 2006-2007, Ext JS, LLC.
1083  *
1084  * Originally Released Under LGPL - original licence link has changed is not relivant.
1085  *
1086  * Fork - LGPL
1087  * <script type="text/javascript">
1088  */
1089  
1090 /**
1091  * @class Roo.bootstrap.MenuMgr
1092  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1093  * @singleton
1094  */
1095 Roo.bootstrap.MenuMgr = function(){
1096    var menus, active, groups = {}, attached = false, lastShow = new Date();
1097
1098    // private - called when first menu is created
1099    function init(){
1100        menus = {};
1101        active = new Roo.util.MixedCollection();
1102        Roo.get(document).addKeyListener(27, function(){
1103            if(active.length > 0){
1104                hideAll();
1105            }
1106        });
1107    }
1108
1109    // private
1110    function hideAll(){
1111        if(active && active.length > 0){
1112            var c = active.clone();
1113            c.each(function(m){
1114                m.hide();
1115            });
1116        }
1117    }
1118
1119    // private
1120    function onHide(m){
1121        active.remove(m);
1122        if(active.length < 1){
1123            Roo.get(document).un("mouseup", onMouseDown);
1124             
1125            attached = false;
1126        }
1127    }
1128
1129    // private
1130    function onShow(m){
1131        var last = active.last();
1132        lastShow = new Date();
1133        active.add(m);
1134        if(!attached){
1135           Roo.get(document).on("mouseup", onMouseDown);
1136            
1137            attached = true;
1138        }
1139        if(m.parentMenu){
1140           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1141           m.parentMenu.activeChild = m;
1142        }else if(last && last.isVisible()){
1143           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1144        }
1145    }
1146
1147    // private
1148    function onBeforeHide(m){
1149        if(m.activeChild){
1150            m.activeChild.hide();
1151        }
1152        if(m.autoHideTimer){
1153            clearTimeout(m.autoHideTimer);
1154            delete m.autoHideTimer;
1155        }
1156    }
1157
1158    // private
1159    function onBeforeShow(m){
1160        var pm = m.parentMenu;
1161        if(!pm && !m.allowOtherMenus){
1162            hideAll();
1163        }else if(pm && pm.activeChild && active != m){
1164            pm.activeChild.hide();
1165        }
1166    }
1167
1168    // private
1169    function onMouseDown(e){
1170         Roo.log("on MouseDown");
1171         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1172            hideAll();
1173         }
1174         
1175         
1176    }
1177
1178    // private
1179    function onBeforeCheck(mi, state){
1180        if(state){
1181            var g = groups[mi.group];
1182            for(var i = 0, l = g.length; i < l; i++){
1183                if(g[i] != mi){
1184                    g[i].setChecked(false);
1185                }
1186            }
1187        }
1188    }
1189
1190    return {
1191
1192        /**
1193         * Hides all menus that are currently visible
1194         */
1195        hideAll : function(){
1196             hideAll();  
1197        },
1198
1199        // private
1200        register : function(menu){
1201            if(!menus){
1202                init();
1203            }
1204            menus[menu.id] = menu;
1205            menu.on("beforehide", onBeforeHide);
1206            menu.on("hide", onHide);
1207            menu.on("beforeshow", onBeforeShow);
1208            menu.on("show", onShow);
1209            var g = menu.group;
1210            if(g && menu.events["checkchange"]){
1211                if(!groups[g]){
1212                    groups[g] = [];
1213                }
1214                groups[g].push(menu);
1215                menu.on("checkchange", onCheck);
1216            }
1217        },
1218
1219         /**
1220          * Returns a {@link Roo.menu.Menu} object
1221          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1222          * be used to generate and return a new Menu instance.
1223          */
1224        get : function(menu){
1225            if(typeof menu == "string"){ // menu id
1226                return menus[menu];
1227            }else if(menu.events){  // menu instance
1228                return menu;
1229            }
1230            /*else if(typeof menu.length == 'number'){ // array of menu items?
1231                return new Roo.bootstrap.Menu({items:menu});
1232            }else{ // otherwise, must be a config
1233                return new Roo.bootstrap.Menu(menu);
1234            }
1235            */
1236            return false;
1237        },
1238
1239        // private
1240        unregister : function(menu){
1241            delete menus[menu.id];
1242            menu.un("beforehide", onBeforeHide);
1243            menu.un("hide", onHide);
1244            menu.un("beforeshow", onBeforeShow);
1245            menu.un("show", onShow);
1246            var g = menu.group;
1247            if(g && menu.events["checkchange"]){
1248                groups[g].remove(menu);
1249                menu.un("checkchange", onCheck);
1250            }
1251        },
1252
1253        // private
1254        registerCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                if(!groups[g]){
1258                    groups[g] = [];
1259                }
1260                groups[g].push(menuItem);
1261                menuItem.on("beforecheckchange", onBeforeCheck);
1262            }
1263        },
1264
1265        // private
1266        unregisterCheckable : function(menuItem){
1267            var g = menuItem.group;
1268            if(g){
1269                groups[g].remove(menuItem);
1270                menuItem.un("beforecheckchange", onBeforeCheck);
1271            }
1272        }
1273    };
1274 }();/*
1275  * - LGPL
1276  *
1277  * menu
1278  * 
1279  */
1280
1281 /**
1282  * @class Roo.bootstrap.Menu
1283  * @extends Roo.bootstrap.Component
1284  * Bootstrap Menu class - container for MenuItems
1285  * @cfg {String} type type of menu
1286  * 
1287  * @constructor
1288  * Create a new Menu
1289  * @param {Object} config The config object
1290  */
1291
1292
1293 Roo.bootstrap.Menu = function(config){
1294     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1295     if (this.registerMenu) {
1296         Roo.bootstrap.MenuMgr.register(this);
1297     }
1298     this.addEvents({
1299         /**
1300          * @event beforeshow
1301          * Fires before this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         beforeshow : true,
1305         /**
1306          * @event beforehide
1307          * Fires before this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         beforehide : true,
1311         /**
1312          * @event show
1313          * Fires after this menu is displayed
1314          * @param {Roo.menu.Menu} this
1315          */
1316         show : true,
1317         /**
1318          * @event hide
1319          * Fires after this menu is hidden
1320          * @param {Roo.menu.Menu} this
1321          */
1322         hide : true,
1323         /**
1324          * @event click
1325          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1326          * @param {Roo.menu.Menu} this
1327          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1328          * @param {Roo.EventObject} e
1329          */
1330         click : true,
1331         /**
1332          * @event mouseover
1333          * Fires when the mouse is hovering over this menu
1334          * @param {Roo.menu.Menu} this
1335          * @param {Roo.EventObject} e
1336          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337          */
1338         mouseover : true,
1339         /**
1340          * @event mouseout
1341          * Fires when the mouse exits this menu
1342          * @param {Roo.menu.Menu} this
1343          * @param {Roo.EventObject} e
1344          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1345          */
1346         mouseout : true,
1347         /**
1348          * @event itemclick
1349          * Fires when a menu item contained in this menu is clicked
1350          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1351          * @param {Roo.EventObject} e
1352          */
1353         itemclick: true
1354     });
1355     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1356 };
1357
1358 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1359     
1360    /// html : false,
1361     //align : '',
1362     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1363     type: false,
1364     /**
1365      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1366      */
1367     registerMenu : true,
1368     
1369     menuItems :false, // stores the menu items..
1370     
1371     hidden:true,
1372     
1373     parentMenu : false,
1374     
1375     getChildContainer : function() {
1376         return this.el;  
1377     },
1378     
1379     getAutoCreate : function(){
1380          
1381         //if (['right'].indexOf(this.align)!==-1) {
1382         //    cfg.cn[1].cls += ' pull-right'
1383         //}
1384         var cfg = {
1385             tag : 'ul',
1386             cls : 'dropdown-menu' ,
1387             style : 'z-index:1000'
1388             
1389         }
1390         
1391         if (this.type === 'submenu') {
1392             cfg.cls = 'submenu active'
1393         }
1394         
1395         return cfg;
1396     },
1397     initEvents : function() {
1398         
1399        // Roo.log("ADD event");
1400        // Roo.log(this.triggerEl.dom);
1401         this.triggerEl.on('click', this.onTriggerPress, this);
1402         this.triggerEl.addClass('dropdown-toggle');
1403         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1404
1405         this.el.on("mouseover", this.onMouseOver, this);
1406         this.el.on("mouseout", this.onMouseOut, this);
1407         
1408         
1409     },
1410     findTargetItem : function(e){
1411         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1412         if(!t){
1413             return false;
1414         }
1415         //Roo.log(t);         Roo.log(t.id);
1416         if(t && t.id){
1417             //Roo.log(this.menuitems);
1418             return this.menuitems.get(t.id);
1419             
1420             //return this.items.get(t.menuItemId);
1421         }
1422         
1423         return false;
1424     },
1425     onClick : function(e){
1426         Roo.log("menu.onClick");
1427         var t = this.findTargetItem(e);
1428         if(!t){
1429             return;
1430         }
1431         Roo.log(e);
1432         /*
1433         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1434             if(t == this.activeItem && t.shouldDeactivate(e)){
1435                 this.activeItem.deactivate();
1436                 delete this.activeItem;
1437                 return;
1438             }
1439             if(t.canActivate){
1440                 this.setActiveItem(t, true);
1441             }
1442             return;
1443             
1444             
1445         }
1446         */
1447         Roo.log('pass click event');
1448         
1449         t.onClick(e);
1450         
1451         this.fireEvent("click", this, t, e);
1452         
1453         this.hide();
1454     },
1455      onMouseOver : function(e){
1456         var t  = this.findTargetItem(e);
1457         //Roo.log(t);
1458         //if(t){
1459         //    if(t.canActivate && !t.disabled){
1460         //        this.setActiveItem(t, true);
1461         //    }
1462         //}
1463         
1464         this.fireEvent("mouseover", this, e, t);
1465     },
1466     isVisible : function(){
1467         return !this.hidden;
1468     },
1469      onMouseOut : function(e){
1470         var t  = this.findTargetItem(e);
1471         
1472         //if(t ){
1473         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1474         //        this.activeItem.deactivate();
1475         //        delete this.activeItem;
1476         //    }
1477         //}
1478         this.fireEvent("mouseout", this, e, t);
1479     },
1480     
1481     
1482     /**
1483      * Displays this menu relative to another element
1484      * @param {String/HTMLElement/Roo.Element} element The element to align to
1485      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1486      * the element (defaults to this.defaultAlign)
1487      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1488      */
1489     show : function(el, pos, parentMenu){
1490         this.parentMenu = parentMenu;
1491         if(!this.el){
1492             this.render();
1493         }
1494         this.fireEvent("beforeshow", this);
1495         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1496     },
1497      /**
1498      * Displays this menu at a specific xy position
1499      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1500      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1501      */
1502     showAt : function(xy, parentMenu, /* private: */_e){
1503         this.parentMenu = parentMenu;
1504         if(!this.el){
1505             this.render();
1506         }
1507         if(_e !== false){
1508             this.fireEvent("beforeshow", this);
1509             
1510             //xy = this.el.adjustForConstraints(xy);
1511         }
1512         //this.el.setXY(xy);
1513         //this.el.show();
1514         this.hideMenuItems();
1515         this.hidden = false;
1516         this.triggerEl.addClass('open');
1517         this.focus();
1518         this.fireEvent("show", this);
1519     },
1520     
1521     focus : function(){
1522         return;
1523         if(!this.hidden){
1524             this.doFocus.defer(50, this);
1525         }
1526     },
1527
1528     doFocus : function(){
1529         if(!this.hidden){
1530             this.focusEl.focus();
1531         }
1532     },
1533
1534     /**
1535      * Hides this menu and optionally all parent menus
1536      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1537      */
1538     hide : function(deep){
1539         
1540         this.hideMenuItems();
1541         if(this.el && this.isVisible()){
1542             this.fireEvent("beforehide", this);
1543             if(this.activeItem){
1544                 this.activeItem.deactivate();
1545                 this.activeItem = null;
1546             }
1547             this.triggerEl.removeClass('open');;
1548             this.hidden = true;
1549             this.fireEvent("hide", this);
1550         }
1551         if(deep === true && this.parentMenu){
1552             this.parentMenu.hide(true);
1553         }
1554     },
1555     
1556     onTriggerPress  : function(e)
1557     {
1558         
1559         Roo.log('trigger press');
1560         //Roo.log(e.getTarget());
1561        // Roo.log(this.triggerEl.dom);
1562         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1563             return;
1564         }
1565         if (this.isVisible()) {
1566             Roo.log('hide');
1567             this.hide();
1568         } else {
1569             this.show(this.triggerEl, false, false);
1570         }
1571         
1572         
1573     },
1574     
1575          
1576        
1577     
1578     hideMenuItems : function()
1579     {
1580         //$(backdrop).remove()
1581         Roo.select('.open',true).each(function(aa) {
1582             
1583             aa.removeClass('open');
1584           //var parent = getParent($(this))
1585           //var relatedTarget = { relatedTarget: this }
1586           
1587            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1588           //if (e.isDefaultPrevented()) return
1589            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1590         })
1591     },
1592     addxtypeChild : function (tree, cntr) {
1593         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1594           
1595         this.menuitems.add(comp);
1596         return comp;
1597
1598     },
1599     getEl : function()
1600     {
1601         Roo.log(this.el);
1602         return this.el;
1603     }
1604 });
1605
1606  
1607  /*
1608  * - LGPL
1609  *
1610  * menu item
1611  * 
1612  */
1613
1614
1615 /**
1616  * @class Roo.bootstrap.MenuItem
1617  * @extends Roo.bootstrap.Component
1618  * Bootstrap MenuItem class
1619  * @cfg {String} html the menu label
1620  * @cfg {String} href the link
1621  * @cfg {Boolean} preventDefault (true | false) default true
1622  * 
1623  * 
1624  * @constructor
1625  * Create a new MenuItem
1626  * @param {Object} config The config object
1627  */
1628
1629
1630 Roo.bootstrap.MenuItem = function(config){
1631     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1632     this.addEvents({
1633         // raw events
1634         /**
1635          * @event click
1636          * The raw click event for the entire grid.
1637          * @param {Roo.EventObject} e
1638          */
1639         "click" : true
1640     });
1641 };
1642
1643 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1644     
1645     href : false,
1646     html : false,
1647     preventDefault: true,
1648     
1649     getAutoCreate : function(){
1650         var cfg= {
1651             tag: 'li',
1652         cls: 'dropdown-menu-item',
1653             cn: [
1654             {
1655                 tag : 'a',
1656                 href : '#',
1657                 html : 'Link'
1658             }
1659             ]
1660     };
1661         
1662         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1663         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1664         return cfg;
1665     },
1666     
1667     initEvents: function() {
1668         
1669         //this.el.select('a').on('click', this.onClick, this);
1670         
1671     },
1672     onClick : function(e)
1673     {
1674         Roo.log('item on click ');
1675         //if(this.preventDefault){
1676         //    e.preventDefault();
1677         //}
1678         //this.parent().hideMenuItems();
1679         
1680         this.fireEvent('click', this, e);
1681     },
1682     getEl : function()
1683     {
1684         return this.el;
1685     }
1686 });
1687
1688  
1689
1690  /*
1691  * - LGPL
1692  *
1693  * menu separator
1694  * 
1695  */
1696
1697
1698 /**
1699  * @class Roo.bootstrap.MenuSeparator
1700  * @extends Roo.bootstrap.Component
1701  * Bootstrap MenuSeparator class
1702  * 
1703  * @constructor
1704  * Create a new MenuItem
1705  * @param {Object} config The config object
1706  */
1707
1708
1709 Roo.bootstrap.MenuSeparator = function(config){
1710     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1711 };
1712
1713 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1714     
1715     getAutoCreate : function(){
1716         var cfg = {
1717             cls: 'divider',
1718             tag : 'li'
1719         };
1720         
1721         return cfg;
1722     }
1723    
1724 });
1725
1726  
1727
1728  
1729 /*
1730 <div class="modal fade">
1731   <div class="modal-dialog">
1732     <div class="modal-content">
1733       <div class="modal-header">
1734         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1735         <h4 class="modal-title">Modal title</h4>
1736       </div>
1737       <div class="modal-body">
1738         <p>One fine body&hellip;</p>
1739       </div>
1740       <div class="modal-footer">
1741         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1742         <button type="button" class="btn btn-primary">Save changes</button>
1743       </div>
1744     </div><!-- /.modal-content -->
1745   </div><!-- /.modal-dialog -->
1746 </div><!-- /.modal -->
1747 */
1748 /*
1749  * - LGPL
1750  *
1751  * page contgainer.
1752  * 
1753  */
1754
1755 /**
1756  * @class Roo.bootstrap.Modal
1757  * @extends Roo.bootstrap.Component
1758  * Bootstrap Modal class
1759  * @cfg {String} title Title of dialog
1760  * @cfg {Boolean} specificTitle (true|false) default false
1761  * @cfg {Array} buttons Array of buttons or standard button set..
1762  * @cfg {String} buttonPosition (left|right|center) default right
1763  * 
1764  * @constructor
1765  * Create a new Modal Dialog
1766  * @param {Object} config The config object
1767  */
1768
1769 Roo.bootstrap.Modal = function(config){
1770     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1771     this.addEvents({
1772         // raw events
1773         /**
1774          * @event btnclick
1775          * The raw btnclick event for the button
1776          * @param {Roo.EventObject} e
1777          */
1778         "btnclick" : true
1779     });
1780     this.buttons = this.buttons || [];
1781 };
1782
1783 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1784     
1785     title : 'test dialog',
1786    
1787     buttons : false,
1788     
1789     // set on load...
1790     body:  false,
1791     
1792     specificTitle: false,
1793     
1794     buttonPosition: 'right',
1795     
1796     onRender : function(ct, position)
1797     {
1798         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1799      
1800         if(!this.el){
1801             var cfg = Roo.apply({},  this.getAutoCreate());
1802             cfg.id = Roo.id();
1803             //if(!cfg.name){
1804             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1805             //}
1806             //if (!cfg.name.length) {
1807             //    delete cfg.name;
1808            // }
1809             if (this.cls) {
1810                 cfg.cls += ' ' + this.cls;
1811             }
1812             if (this.style) {
1813                 cfg.style = this.style;
1814             }
1815             this.el = Roo.get(document.body).createChild(cfg, position);
1816         }
1817         //var type = this.el.dom.type;
1818         
1819         if(this.tabIndex !== undefined){
1820             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1821         }
1822         
1823         
1824         
1825         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1826         this.maskEl.enableDisplayMode("block");
1827         this.maskEl.hide();
1828         //this.el.addClass("x-dlg-modal");
1829     
1830         if (this.buttons.length) {
1831             Roo.each(this.buttons, function(bb) {
1832                 b = Roo.apply({}, bb);
1833                 b.xns = b.xns || Roo.bootstrap;
1834                 b.xtype = b.xtype || 'Button';
1835                 if (typeof(b.listeners) == 'undefined') {
1836                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1837                 }
1838                 
1839                 var btn = Roo.factory(b);
1840                 
1841                 btn.onRender(this.el.select('.modal-footer div').first());
1842                 
1843             },this);
1844         }
1845         // render the children.
1846         var nitems = [];
1847         
1848         if(typeof(this.items) != 'undefined'){
1849             var items = this.items;
1850             delete this.items;
1851
1852             for(var i =0;i < items.length;i++) {
1853                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1854             }
1855         }
1856         
1857         this.items = nitems;
1858         
1859         this.body = this.el.select('.modal-body',true).first();
1860         this.close = this.el.select('.modal-header .close', true).first();
1861         this.footer = this.el.select('.modal-footer',true).first();
1862         this.initEvents();
1863         //this.el.addClass([this.fieldClass, this.cls]);
1864         
1865     },
1866     getAutoCreate : function(){
1867         
1868         
1869         var bdy = {
1870                 cls : 'modal-body',
1871                 html : this.html || ''
1872         };
1873         
1874         var title = {
1875             tag: 'h4',
1876             cls : 'modal-title',
1877             html : this.title
1878         };
1879         
1880         if(this.specificTitle){
1881             title = this.title;
1882         };
1883         
1884         return modal = {
1885             cls: "modal fade",
1886             style : 'display: none',
1887             cn : [
1888                 {
1889                     cls: "modal-dialog",
1890                     cn : [
1891                         {
1892                             cls : "modal-content",
1893                             cn : [
1894                                 {
1895                                     cls : 'modal-header',
1896                                     cn : [
1897                                         {
1898                                             tag: 'button',
1899                                             cls : 'close',
1900                                             html : '&times'
1901                                         },
1902                                         title
1903                                     ]
1904                                 },
1905                                 bdy,
1906                                 {
1907                                     cls : 'modal-footer',
1908                                     cn : [
1909                                         {
1910                                             tag: 'div',
1911                                             cls: 'btn-' + this.buttonPosition
1912                                         }
1913                                     ]
1914                                     
1915                                 }
1916                                 
1917                                 
1918                             ]
1919                             
1920                         }
1921                     ]
1922                         
1923                 }
1924             ]
1925             
1926             
1927         };
1928           
1929     },
1930     getChildContainer : function() {
1931          
1932          return this.el.select('.modal-body',true).first();
1933         
1934     },
1935     getButtonContainer : function() {
1936          return this.el.select('.modal-footer div',true).first();
1937         
1938     },
1939     initEvents : function()
1940     {
1941         this.el.select('.modal-header .close').on('click', this.hide, this);
1942 //        
1943 //        this.addxtype(this);
1944     },
1945     show : function() {
1946         
1947         if (!this.rendered) {
1948             this.render();
1949         }
1950        
1951         this.el.addClass('on');
1952         this.el.removeClass('fade');
1953         this.el.setStyle('display', 'block');
1954         Roo.get(document.body).addClass("x-body-masked");
1955         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1956         this.maskEl.show();
1957         this.el.setStyle('zIndex', '10001');
1958         this.fireEvent('show', this);
1959         
1960         
1961     },
1962     hide : function()
1963     {
1964         Roo.log('Modal hide?!');
1965         this.maskEl.hide();
1966         Roo.get(document.body).removeClass("x-body-masked");
1967         this.el.removeClass('on');
1968         this.el.addClass('fade');
1969         this.el.setStyle('display', 'none');
1970         this.fireEvent('hide', this);
1971     },
1972     
1973     addButton : function(str, cb)
1974     {
1975          
1976         
1977         var b = Roo.apply({}, { html : str } );
1978         b.xns = b.xns || Roo.bootstrap;
1979         b.xtype = b.xtype || 'Button';
1980         if (typeof(b.listeners) == 'undefined') {
1981             b.listeners = { click : cb.createDelegate(this)  };
1982         }
1983         
1984         var btn = Roo.factory(b);
1985            
1986         btn.onRender(this.el.select('.modal-footer div').first());
1987         
1988         return btn;   
1989        
1990     },
1991     
1992     setDefaultButton : function(btn)
1993     {
1994         //this.el.select('.modal-footer').()
1995     },
1996     resizeTo: function(w,h)
1997     {
1998         // skip..
1999     },
2000     setContentSize  : function(w, h)
2001     {
2002         
2003     },
2004     onButtonClick: function(btn,e)
2005     {
2006         //Roo.log([a,b,c]);
2007         this.fireEvent('btnclick', btn.name, e);
2008     },
2009     setTitle: function(str) {
2010         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2011         
2012     }
2013 });
2014
2015
2016 Roo.apply(Roo.bootstrap.Modal,  {
2017     /**
2018          * Button config that displays a single OK button
2019          * @type Object
2020          */
2021         OK :  [{
2022             name : 'ok',
2023             weight : 'primary',
2024             html : 'OK'
2025         }], 
2026         /**
2027          * Button config that displays Yes and No buttons
2028          * @type Object
2029          */
2030         YESNO : [
2031             {
2032                 name  : 'no',
2033                 html : 'No'
2034             },
2035             {
2036                 name  :'yes',
2037                 weight : 'primary',
2038                 html : 'Yes'
2039             }
2040         ],
2041         
2042         /**
2043          * Button config that displays OK and Cancel buttons
2044          * @type Object
2045          */
2046         OKCANCEL : [
2047             {
2048                name : 'cancel',
2049                 html : 'Cancel'
2050             },
2051             {
2052                 name : 'ok',
2053                 weight : 'primary',
2054                 html : 'OK'
2055             }
2056         ],
2057         /**
2058          * Button config that displays Yes, No and Cancel buttons
2059          * @type Object
2060          */
2061         YESNOCANCEL : [
2062             {
2063                 name : 'yes',
2064                 weight : 'primary',
2065                 html : 'Yes'
2066             },
2067             {
2068                 name : 'no',
2069                 html : 'No'
2070             },
2071             {
2072                 name : 'cancel',
2073                 html : 'Cancel'
2074             }
2075         ]
2076 });
2077  /*
2078  * - LGPL
2079  *
2080  * messagebox - can be used as a replace
2081  * 
2082  */
2083 /**
2084  * @class Roo.MessageBox
2085  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2086  * Example usage:
2087  *<pre><code>
2088 // Basic alert:
2089 Roo.Msg.alert('Status', 'Changes saved successfully.');
2090
2091 // Prompt for user data:
2092 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2093     if (btn == 'ok'){
2094         // process text value...
2095     }
2096 });
2097
2098 // Show a dialog using config options:
2099 Roo.Msg.show({
2100    title:'Save Changes?',
2101    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2102    buttons: Roo.Msg.YESNOCANCEL,
2103    fn: processResult,
2104    animEl: 'elId'
2105 });
2106 </code></pre>
2107  * @singleton
2108  */
2109 Roo.bootstrap.MessageBox = function(){
2110     var dlg, opt, mask, waitTimer;
2111     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2112     var buttons, activeTextEl, bwidth;
2113
2114     
2115     // private
2116     var handleButton = function(button){
2117         dlg.hide();
2118         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2119     };
2120
2121     // private
2122     var handleHide = function(){
2123         if(opt && opt.cls){
2124             dlg.el.removeClass(opt.cls);
2125         }
2126         //if(waitTimer){
2127         //    Roo.TaskMgr.stop(waitTimer);
2128         //    waitTimer = null;
2129         //}
2130     };
2131
2132     // private
2133     var updateButtons = function(b){
2134         var width = 0;
2135         if(!b){
2136             buttons["ok"].hide();
2137             buttons["cancel"].hide();
2138             buttons["yes"].hide();
2139             buttons["no"].hide();
2140             //dlg.footer.dom.style.display = 'none';
2141             return width;
2142         }
2143         dlg.footer.dom.style.display = '';
2144         for(var k in buttons){
2145             if(typeof buttons[k] != "function"){
2146                 if(b[k]){
2147                     buttons[k].show();
2148                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2149                     width += buttons[k].el.getWidth()+15;
2150                 }else{
2151                     buttons[k].hide();
2152                 }
2153             }
2154         }
2155         return width;
2156     };
2157
2158     // private
2159     var handleEsc = function(d, k, e){
2160         if(opt && opt.closable !== false){
2161             dlg.hide();
2162         }
2163         if(e){
2164             e.stopEvent();
2165         }
2166     };
2167
2168     return {
2169         /**
2170          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2171          * @return {Roo.BasicDialog} The BasicDialog element
2172          */
2173         getDialog : function(){
2174            if(!dlg){
2175                 dlg = new Roo.bootstrap.Modal( {
2176                     //draggable: true,
2177                     //resizable:false,
2178                     //constraintoviewport:false,
2179                     //fixedcenter:true,
2180                     //collapsible : false,
2181                     //shim:true,
2182                     //modal: true,
2183                   //  width:400,
2184                   //  height:100,
2185                     //buttonAlign:"center",
2186                     closeClick : function(){
2187                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2188                             handleButton("no");
2189                         }else{
2190                             handleButton("cancel");
2191                         }
2192                     }
2193                 });
2194                 dlg.render();
2195                 dlg.on("hide", handleHide);
2196                 mask = dlg.mask;
2197                 //dlg.addKeyListener(27, handleEsc);
2198                 buttons = {};
2199                 this.buttons = buttons;
2200                 var bt = this.buttonText;
2201                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2202                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2203                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2204                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2205                 Roo.log(buttons)
2206                 bodyEl = dlg.body.createChild({
2207
2208                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2209                         '<textarea class="roo-mb-textarea"></textarea>' +
2210                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2211                 });
2212                 msgEl = bodyEl.dom.firstChild;
2213                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2214                 textboxEl.enableDisplayMode();
2215                 textboxEl.addKeyListener([10,13], function(){
2216                     if(dlg.isVisible() && opt && opt.buttons){
2217                         if(opt.buttons.ok){
2218                             handleButton("ok");
2219                         }else if(opt.buttons.yes){
2220                             handleButton("yes");
2221                         }
2222                     }
2223                 });
2224                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2225                 textareaEl.enableDisplayMode();
2226                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2227                 progressEl.enableDisplayMode();
2228                 var pf = progressEl.dom.firstChild;
2229                 if (pf) {
2230                     pp = Roo.get(pf.firstChild);
2231                     pp.setHeight(pf.offsetHeight);
2232                 }
2233                 
2234             }
2235             return dlg;
2236         },
2237
2238         /**
2239          * Updates the message box body text
2240          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2241          * the XHTML-compliant non-breaking space character '&amp;#160;')
2242          * @return {Roo.MessageBox} This message box
2243          */
2244         updateText : function(text){
2245             if(!dlg.isVisible() && !opt.width){
2246                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2247             }
2248             msgEl.innerHTML = text || '&#160;';
2249       
2250             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2251             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2252             var w = Math.max(
2253                     Math.min(opt.width || cw , this.maxWidth), 
2254                     Math.max(opt.minWidth || this.minWidth, bwidth)
2255             );
2256             if(opt.prompt){
2257                 activeTextEl.setWidth(w);
2258             }
2259             if(dlg.isVisible()){
2260                 dlg.fixedcenter = false;
2261             }
2262             // to big, make it scroll. = But as usual stupid IE does not support
2263             // !important..
2264             
2265             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2266                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2267                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2268             } else {
2269                 bodyEl.dom.style.height = '';
2270                 bodyEl.dom.style.overflowY = '';
2271             }
2272             if (cw > w) {
2273                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2274             } else {
2275                 bodyEl.dom.style.overflowX = '';
2276             }
2277             
2278             dlg.setContentSize(w, bodyEl.getHeight());
2279             if(dlg.isVisible()){
2280                 dlg.fixedcenter = true;
2281             }
2282             return this;
2283         },
2284
2285         /**
2286          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2287          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2288          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2289          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2290          * @return {Roo.MessageBox} This message box
2291          */
2292         updateProgress : function(value, text){
2293             if(text){
2294                 this.updateText(text);
2295             }
2296             if (pp) { // weird bug on my firefox - for some reason this is not defined
2297                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2298             }
2299             return this;
2300         },        
2301
2302         /**
2303          * Returns true if the message box is currently displayed
2304          * @return {Boolean} True if the message box is visible, else false
2305          */
2306         isVisible : function(){
2307             return dlg && dlg.isVisible();  
2308         },
2309
2310         /**
2311          * Hides the message box if it is displayed
2312          */
2313         hide : function(){
2314             if(this.isVisible()){
2315                 dlg.hide();
2316             }  
2317         },
2318
2319         /**
2320          * Displays a new message box, or reinitializes an existing message box, based on the config options
2321          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2322          * The following config object properties are supported:
2323          * <pre>
2324 Property    Type             Description
2325 ----------  ---------------  ------------------------------------------------------------------------------------
2326 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2327                                    closes (defaults to undefined)
2328 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2329                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2330 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2331                                    progress and wait dialogs will ignore this property and always hide the
2332                                    close button as they can only be closed programmatically.
2333 cls               String           A custom CSS class to apply to the message box element
2334 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2335                                    displayed (defaults to 75)
2336 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2337                                    function will be btn (the name of the button that was clicked, if applicable,
2338                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2339                                    Progress and wait dialogs will ignore this option since they do not respond to
2340                                    user actions and can only be closed programmatically, so any required function
2341                                    should be called by the same code after it closes the dialog.
2342 icon              String           A CSS class that provides a background image to be used as an icon for
2343                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2344 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2345 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2346 modal             Boolean          False to allow user interaction with the page while the message box is
2347                                    displayed (defaults to true)
2348 msg               String           A string that will replace the existing message box body text (defaults
2349                                    to the XHTML-compliant non-breaking space character '&#160;')
2350 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2351 progress          Boolean          True to display a progress bar (defaults to false)
2352 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2353 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2354 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2355 title             String           The title text
2356 value             String           The string value to set into the active textbox element if displayed
2357 wait              Boolean          True to display a progress bar (defaults to false)
2358 width             Number           The width of the dialog in pixels
2359 </pre>
2360          *
2361          * Example usage:
2362          * <pre><code>
2363 Roo.Msg.show({
2364    title: 'Address',
2365    msg: 'Please enter your address:',
2366    width: 300,
2367    buttons: Roo.MessageBox.OKCANCEL,
2368    multiline: true,
2369    fn: saveAddress,
2370    animEl: 'addAddressBtn'
2371 });
2372 </code></pre>
2373          * @param {Object} config Configuration options
2374          * @return {Roo.MessageBox} This message box
2375          */
2376         show : function(options)
2377         {
2378             
2379             // this causes nightmares if you show one dialog after another
2380             // especially on callbacks..
2381              
2382             if(this.isVisible()){
2383                 
2384                 this.hide();
2385                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2386                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2387                 Roo.log("New Dialog Message:" +  options.msg )
2388                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2389                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2390                 
2391             }
2392             var d = this.getDialog();
2393             opt = options;
2394             d.setTitle(opt.title || "&#160;");
2395             d.close.setDisplayed(opt.closable !== false);
2396             activeTextEl = textboxEl;
2397             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2398             if(opt.prompt){
2399                 if(opt.multiline){
2400                     textboxEl.hide();
2401                     textareaEl.show();
2402                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2403                         opt.multiline : this.defaultTextHeight);
2404                     activeTextEl = textareaEl;
2405                 }else{
2406                     textboxEl.show();
2407                     textareaEl.hide();
2408                 }
2409             }else{
2410                 textboxEl.hide();
2411                 textareaEl.hide();
2412             }
2413             progressEl.setDisplayed(opt.progress === true);
2414             this.updateProgress(0);
2415             activeTextEl.dom.value = opt.value || "";
2416             if(opt.prompt){
2417                 dlg.setDefaultButton(activeTextEl);
2418             }else{
2419                 var bs = opt.buttons;
2420                 var db = null;
2421                 if(bs && bs.ok){
2422                     db = buttons["ok"];
2423                 }else if(bs && bs.yes){
2424                     db = buttons["yes"];
2425                 }
2426                 dlg.setDefaultButton(db);
2427             }
2428             bwidth = updateButtons(opt.buttons);
2429             this.updateText(opt.msg);
2430             if(opt.cls){
2431                 d.el.addClass(opt.cls);
2432             }
2433             d.proxyDrag = opt.proxyDrag === true;
2434             d.modal = opt.modal !== false;
2435             d.mask = opt.modal !== false ? mask : false;
2436             if(!d.isVisible()){
2437                 // force it to the end of the z-index stack so it gets a cursor in FF
2438                 document.body.appendChild(dlg.el.dom);
2439                 d.animateTarget = null;
2440                 d.show(options.animEl);
2441             }
2442             return this;
2443         },
2444
2445         /**
2446          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2447          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2448          * and closing the message box when the process is complete.
2449          * @param {String} title The title bar text
2450          * @param {String} msg The message box body text
2451          * @return {Roo.MessageBox} This message box
2452          */
2453         progress : function(title, msg){
2454             this.show({
2455                 title : title,
2456                 msg : msg,
2457                 buttons: false,
2458                 progress:true,
2459                 closable:false,
2460                 minWidth: this.minProgressWidth,
2461                 modal : true
2462             });
2463             return this;
2464         },
2465
2466         /**
2467          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2468          * If a callback function is passed it will be called after the user clicks the button, and the
2469          * id of the button that was clicked will be passed as the only parameter to the callback
2470          * (could also be the top-right close button).
2471          * @param {String} title The title bar text
2472          * @param {String} msg The message box body text
2473          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2474          * @param {Object} scope (optional) The scope of the callback function
2475          * @return {Roo.MessageBox} This message box
2476          */
2477         alert : function(title, msg, fn, scope){
2478             this.show({
2479                 title : title,
2480                 msg : msg,
2481                 buttons: this.OK,
2482                 fn: fn,
2483                 scope : scope,
2484                 modal : true
2485             });
2486             return this;
2487         },
2488
2489         /**
2490          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2491          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2492          * You are responsible for closing the message box when the process is complete.
2493          * @param {String} msg The message box body text
2494          * @param {String} title (optional) The title bar text
2495          * @return {Roo.MessageBox} This message box
2496          */
2497         wait : function(msg, title){
2498             this.show({
2499                 title : title,
2500                 msg : msg,
2501                 buttons: false,
2502                 closable:false,
2503                 progress:true,
2504                 modal:true,
2505                 width:300,
2506                 wait:true
2507             });
2508             waitTimer = Roo.TaskMgr.start({
2509                 run: function(i){
2510                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2511                 },
2512                 interval: 1000
2513             });
2514             return this;
2515         },
2516
2517         /**
2518          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2519          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2520          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2521          * @param {String} title The title bar text
2522          * @param {String} msg The message box body text
2523          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2524          * @param {Object} scope (optional) The scope of the callback function
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         confirm : function(title, msg, fn, scope){
2528             this.show({
2529                 title : title,
2530                 msg : msg,
2531                 buttons: this.YESNO,
2532                 fn: fn,
2533                 scope : scope,
2534                 modal : true
2535             });
2536             return this;
2537         },
2538
2539         /**
2540          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2541          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2542          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2543          * (could also be the top-right close button) and the text that was entered will be passed as the two
2544          * parameters to the callback.
2545          * @param {String} title The title bar text
2546          * @param {String} msg The message box body text
2547          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2548          * @param {Object} scope (optional) The scope of the callback function
2549          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2550          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2551          * @return {Roo.MessageBox} This message box
2552          */
2553         prompt : function(title, msg, fn, scope, multiline){
2554             this.show({
2555                 title : title,
2556                 msg : msg,
2557                 buttons: this.OKCANCEL,
2558                 fn: fn,
2559                 minWidth:250,
2560                 scope : scope,
2561                 prompt:true,
2562                 multiline: multiline,
2563                 modal : true
2564             });
2565             return this;
2566         },
2567
2568         /**
2569          * Button config that displays a single OK button
2570          * @type Object
2571          */
2572         OK : {ok:true},
2573         /**
2574          * Button config that displays Yes and No buttons
2575          * @type Object
2576          */
2577         YESNO : {yes:true, no:true},
2578         /**
2579          * Button config that displays OK and Cancel buttons
2580          * @type Object
2581          */
2582         OKCANCEL : {ok:true, cancel:true},
2583         /**
2584          * Button config that displays Yes, No and Cancel buttons
2585          * @type Object
2586          */
2587         YESNOCANCEL : {yes:true, no:true, cancel:true},
2588
2589         /**
2590          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2591          * @type Number
2592          */
2593         defaultTextHeight : 75,
2594         /**
2595          * The maximum width in pixels of the message box (defaults to 600)
2596          * @type Number
2597          */
2598         maxWidth : 600,
2599         /**
2600          * The minimum width in pixels of the message box (defaults to 100)
2601          * @type Number
2602          */
2603         minWidth : 100,
2604         /**
2605          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2606          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2607          * @type Number
2608          */
2609         minProgressWidth : 250,
2610         /**
2611          * An object containing the default button text strings that can be overriden for localized language support.
2612          * Supported properties are: ok, cancel, yes and no.
2613          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2614          * @type Object
2615          */
2616         buttonText : {
2617             ok : "OK",
2618             cancel : "Cancel",
2619             yes : "Yes",
2620             no : "No"
2621         }
2622     };
2623 }();
2624
2625 /**
2626  * Shorthand for {@link Roo.MessageBox}
2627  */
2628 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2629 Roo.Msg = Roo.Msg || Roo.MessageBox;
2630 /*
2631  * - LGPL
2632  *
2633  * navbar
2634  * 
2635  */
2636
2637 /**
2638  * @class Roo.bootstrap.Navbar
2639  * @extends Roo.bootstrap.Component
2640  * Bootstrap Navbar class
2641  * @cfg {Boolean} sidebar has side bar
2642  * @cfg {Boolean} bar is a bar?
2643  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2644  * @cfg {String} brand what is brand
2645  * @cfg {Boolean} inverse is inverted color
2646  * @cfg {String} type (nav | pills | tabs)
2647  * @cfg {Boolean} arrangement stacked | justified
2648  * @cfg {String} align (left | right) alignment
2649  * @cfg {String} brand_href href of the brand
2650  * @cfg {Boolean} main (true|false) main nav bar? default false
2651  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2652  *
2653  * 
2654  * @constructor
2655  * Create a new Navbar
2656  * @param {Object} config The config object
2657  */
2658
2659
2660 Roo.bootstrap.Navbar = function(config){
2661     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2662    
2663     
2664 };
2665
2666 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2667     
2668     sidebar: false,
2669     
2670     bar: false,
2671     brand: '',
2672     inverse: false,
2673     position: '',
2674     align : false,
2675     type: 'nav',
2676     arrangement: '',
2677     brand_href: false,
2678     main : false,
2679     loadMask : false,
2680     
2681     
2682     // private
2683     navItems : false,
2684     
2685     getAutoCreate : function(){
2686         var cfg = {
2687             cls : 'navbar'
2688         };
2689         
2690         if (this.sidebar === true) {
2691             cfg = {
2692                 tag: 'div',
2693                 cls: 'sidebar-nav'
2694             };
2695             return cfg;
2696         }
2697         
2698         if (this.bar === true) {
2699             cfg = {
2700                 tag: 'nav',
2701                 cls: 'navbar',
2702                 role: 'navigation',
2703                 cn: [
2704                     {
2705                         tag: 'div',
2706                         cls: 'navbar-header',
2707                         cn: [
2708                             {
2709                             tag: 'button',
2710                             type: 'button',
2711                             cls: 'navbar-toggle',
2712                             'data-toggle': 'collapse',
2713                             cn: [
2714                                 {
2715                                     tag: 'span',
2716                                     cls: 'sr-only',
2717                                     html: 'Toggle navigation'
2718                                 },
2719                                 {
2720                                     tag: 'span',
2721                                     cls: 'icon-bar'
2722                                 },
2723                                 {
2724                                     tag: 'span',
2725                                     cls: 'icon-bar'
2726                                 },
2727                                 {
2728                                     tag: 'span',
2729                                     cls: 'icon-bar'
2730                                 }
2731                             ]
2732                             }
2733                         ]
2734                     },
2735                     {
2736                     tag: 'div',
2737                     cls: 'collapse navbar-collapse'
2738                     }
2739                 ]
2740             };
2741             
2742             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2743             
2744             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2745                 cfg.cls += ' navbar-' + this.position;
2746                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2747             }
2748             
2749             if (this.brand !== '') {
2750                 cfg.cn[0].cn.push({
2751                     tag: 'a',
2752                     href: this.brand_href ? this.brand_href : '#',
2753                     cls: 'navbar-brand',
2754                     cn: [
2755                     this.brand
2756                     ]
2757                 });
2758             }
2759             
2760             if(this.main){
2761                 cfg.cls += ' main-nav';
2762             }
2763             
2764             
2765             return cfg;
2766         
2767         } else if (this.bar === false) {
2768             
2769         } else {
2770             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2771         }
2772         
2773         cfg.cn = [
2774             {
2775                 cls: 'nav',
2776                 tag : 'ul'
2777             }
2778         ];
2779         
2780         if (['tabs','pills'].indexOf(this.type)!==-1) {
2781             cfg.cn[0].cls += ' nav-' + this.type
2782         } else {
2783             if (this.type!=='nav') {
2784             Roo.log('nav type must be nav/tabs/pills')
2785             }
2786             cfg.cn[0].cls += ' navbar-nav'
2787         }
2788         
2789         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2790             cfg.cn[0].cls += ' nav-' + this.arrangement;
2791         }
2792         
2793         if (this.align === 'right') {
2794             cfg.cn[0].cls += ' navbar-right';
2795         }
2796         if (this.inverse) {
2797             cfg.cls += ' navbar-inverse';
2798             
2799         }
2800         
2801         
2802         return cfg;
2803     },
2804     
2805     initEvents :function ()
2806     {
2807         //Roo.log(this.el.select('.navbar-toggle',true));
2808         this.el.select('.navbar-toggle',true).on('click', function() {
2809            // Roo.log('click');
2810             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2811         }, this);
2812         
2813         var mark = {
2814             tag: "div",
2815             cls:"x-dlg-mask"
2816         }
2817         
2818         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2819         
2820         var size = this.el.getSize();
2821         this.maskEl.setSize(size.width, size.height);
2822         this.maskEl.enableDisplayMode("block");
2823         this.maskEl.hide();
2824         
2825         if(this.loadMask){
2826             this.maskEl.show();
2827         }
2828     },
2829     
2830     
2831     getChildContainer : function()
2832     {
2833         if (this.bar === true) {
2834             return this.el.select('.collapse',true).first();
2835         }
2836         
2837         return this.el;
2838     },
2839     
2840     mask : function()
2841     {
2842         this.maskEl.show();
2843     },
2844     
2845     unmask : function()
2846     {
2847         this.maskEl.hide();
2848     }
2849     
2850     
2851     
2852 });
2853
2854
2855
2856  
2857
2858  /*
2859  * - LGPL
2860  *
2861  * nav group
2862  * 
2863  */
2864
2865 /**
2866  * @class Roo.bootstrap.NavGroup
2867  * @extends Roo.bootstrap.Component
2868  * Bootstrap NavGroup class
2869  * @cfg {String} align left | right
2870  * @cfg {Boolean} inverse false | true
2871  * @cfg {String} type (nav|pills|tab) default nav
2872  * @cfg {String} navId - reference Id for navbar.
2873
2874  * 
2875  * @constructor
2876  * Create a new nav group
2877  * @param {Object} config The config object
2878  */
2879
2880 Roo.bootstrap.NavGroup = function(config){
2881     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2882     this.navItems = [];
2883     Roo.bootstrap.NavGroup.register(this);
2884      this.addEvents({
2885         /**
2886              * @event changed
2887              * Fires when the active item changes
2888              * @param {Roo.bootstrap.NavGroup} this
2889              * @param {Roo.bootstrap.Navbar.Item} item The item selected
2890              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
2891          */
2892         'changed': true
2893      });
2894     
2895 };
2896
2897 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2898     
2899     align: '',
2900     inverse: false,
2901     form: false,
2902     type: 'nav',
2903     navId : '',
2904     // private
2905     
2906     navItems : false,
2907     
2908     getAutoCreate : function()
2909     {
2910         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2911         
2912         cfg = {
2913             tag : 'ul',
2914             cls: 'nav' 
2915         }
2916         
2917         if (['tabs','pills'].indexOf(this.type)!==-1) {
2918             cfg.cls += ' nav-' + this.type
2919         } else {
2920             if (this.type!=='nav') {
2921                 Roo.log('nav type must be nav/tabs/pills')
2922             }
2923             cfg.cls += ' navbar-nav'
2924         }
2925         
2926         if (this.parent().sidebar === true) {
2927             cfg = {
2928                 tag: 'ul',
2929                 cls: 'dashboard-menu'
2930             }
2931             
2932             return cfg;
2933         }
2934         
2935         if (this.form === true) {
2936             cfg = {
2937                 tag: 'form',
2938                 cls: 'navbar-form'
2939             }
2940             
2941             if (this.align === 'right') {
2942                 cfg.cls += ' navbar-right';
2943             } else {
2944                 cfg.cls += ' navbar-left';
2945             }
2946         }
2947         
2948         if (this.align === 'right') {
2949             cfg.cls += ' navbar-right';
2950         }
2951         
2952         if (this.inverse) {
2953             cfg.cls += ' navbar-inverse';
2954             
2955         }
2956         
2957         
2958         return cfg;
2959     },
2960     
2961     setActiveItem : function(item)
2962     {
2963         var prev = false;
2964         Roo.each(this.navItems, function(v){
2965             if (v == item) {
2966                 return ;
2967             }
2968             if (v.isActive()) {
2969                 v.setActive(false, true);
2970                 prev = v;
2971                 
2972             }
2973             
2974         });
2975
2976         item.setActive(true, true);
2977         this.fireEvent('changed', this, item, prev);
2978         
2979         
2980     },
2981     
2982     
2983     register : function(item)
2984     {
2985         this.navItems.push( item);
2986         item.navId = this.navId;
2987     
2988     },
2989     getNavItem: function(tabId)
2990     {
2991         var ret = false;
2992         Roo.each(this.navItems, function(e) {
2993             if (e.tabId == tabId) {
2994                ret =  e;
2995                return false;
2996             }
2997             return true;
2998             
2999         });
3000         return ret;
3001     }
3002 });
3003
3004  
3005 Roo.apply(Roo.bootstrap.NavGroup, {
3006     
3007     groups: {},
3008     
3009     register : function(navgrp)
3010     {
3011         this.groups[navgrp.navId] = navgrp;
3012         
3013     },
3014     get: function(navId) {
3015         return this.groups[navId];
3016     }
3017     
3018     
3019     
3020 });
3021
3022  /*
3023  * - LGPL
3024  *
3025  * row
3026  * 
3027  */
3028
3029 /**
3030  * @class Roo.bootstrap.Navbar.Item
3031  * @extends Roo.bootstrap.Component
3032  * Bootstrap Navbar.Button class
3033  * @cfg {String} href  link to
3034  * @cfg {String} html content of button
3035  * @cfg {String} badge text inside badge
3036  * @cfg {String} glyphicon name of glyphicon
3037  * @cfg {String} icon name of font awesome icon
3038  * @cfg {Boolean} active Is item active
3039  * @cfg {Boolean} preventDefault (true | false) default false
3040  * @cfg {String} tabId the tab that this item activates.
3041   
3042  * @constructor
3043  * Create a new Navbar Button
3044  * @param {Object} config The config object
3045  */
3046 Roo.bootstrap.Navbar.Item = function(config){
3047     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3048     this.addEvents({
3049         // raw events
3050         /**
3051          * @event click
3052          * The raw click event for the entire grid.
3053          * @param {Roo.EventObject} e
3054          */
3055         "click" : true,
3056          /**
3057             * @event changed
3058             * Fires when the active item active state changes
3059             * @param {Roo.bootstrap.Navbar.Item} this
3060             * @param {boolean} state the new state
3061              
3062          */
3063         'changed': true
3064     });
3065    
3066 };
3067
3068 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3069     
3070     href: false,
3071     html: '',
3072     badge: '',
3073     icon: false,
3074     glyphicon: false,
3075     active: false,
3076     preventDefault : false,
3077     tabId : false,
3078     
3079     getAutoCreate : function(){
3080         
3081         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3082         
3083         if (this.parent().parent().sidebar === true) {
3084             cfg = {
3085                 tag: 'li',
3086                 cls: '',
3087                 cn: [
3088                     {
3089                         tag: 'p',
3090                         cls: ''
3091                     }
3092                 ]
3093             }
3094             
3095             if (this.html) {
3096                 cfg.cn[0].html = this.html;
3097             }
3098             
3099             if (this.active) {
3100                 this.cls += ' active';
3101             }
3102             
3103             if (this.menu) {
3104                 cfg.cn[0].cls += ' dropdown-toggle';
3105                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3106             }
3107             
3108             if (this.href) {
3109                 cfg.cn[0].tag = 'a',
3110                 cfg.cn[0].href = this.href;
3111             }
3112             
3113             if (this.glyphicon) {
3114                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3115             }
3116             
3117             if (this.icon) {
3118                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3119             }
3120             
3121             return cfg;
3122         }
3123         
3124         cfg = {
3125             tag: 'li',
3126             cls: 'nav-item'
3127         }
3128         
3129         if (this.active) {
3130             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3131         }
3132             
3133         cfg.cn = [
3134             {
3135                 tag: 'p',
3136                 html: 'Text'
3137             }
3138         ];
3139         
3140         if (this.glyphicon) {
3141             if(cfg.html){cfg.html = ' ' + this.html};
3142             cfg.cn=[
3143                 {
3144                     tag: 'span',
3145                     cls: 'glyphicon glyphicon-' + this.glyphicon
3146                 }
3147             ];
3148         }
3149         
3150         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3151         
3152         if (this.menu) {
3153             cfg.cn[0].tag='a';
3154             cfg.cn[0].href='#';
3155             cfg.cn[0].html += " <span class='caret'></span>";
3156         //}else if (!this.href) {
3157         //    cfg.cn[0].tag='p';
3158         //    cfg.cn[0].cls='navbar-text';
3159         } else {
3160             cfg.cn[0].tag='a';
3161             cfg.cn[0].href=this.href||'#';
3162             cfg.cn[0].html=this.html;
3163         }
3164         
3165         if (this.badge !== '') {
3166             
3167             cfg.cn[0].cn=[
3168                 cfg.cn[0].html + ' ',
3169                 {
3170                     tag: 'span',
3171                     cls: 'badge',
3172                     html: this.badge
3173                 }
3174             ];
3175             cfg.cn[0].html=''
3176         }
3177          
3178         if (this.icon) {
3179             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3180         }
3181         
3182         return cfg;
3183     },
3184     initEvents: function() {
3185        // Roo.log('init events?');
3186        // Roo.log(this.el.dom);
3187         this.el.select('a',true).on('click', this.onClick, this);
3188         // at this point parent should be available..
3189         this.parent().register(this);
3190     },
3191     
3192     onClick : function(e)
3193     {
3194         if(this.preventDefault){
3195             e.preventDefault();
3196         }
3197         
3198         if(this.fireEvent('click', this, e) === false){
3199             return;
3200         };
3201         
3202         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3203              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3204                 this.parent().setActiveItem(this);
3205             }
3206             
3207             
3208             
3209         } 
3210     },
3211     
3212     isActive: function () {
3213         return this.active
3214     },
3215     setActive : function(state, fire)
3216     {
3217         this.active = state;
3218         if (!state ) {
3219             this.el.removeClass('active');
3220         } else if (!this.el.hasClass('active')) {
3221             this.el.addClass('active');
3222         }
3223         if (fire) {
3224             this.fireEvent('changed', this, state);
3225         }
3226         
3227         
3228     }
3229      // this should not be here...
3230  
3231 });
3232  
3233
3234  /*
3235  * - LGPL
3236  *
3237  * row
3238  * 
3239  */
3240
3241 /**
3242  * @class Roo.bootstrap.Row
3243  * @extends Roo.bootstrap.Component
3244  * Bootstrap Row class (contains columns...)
3245  * 
3246  * @constructor
3247  * Create a new Row
3248  * @param {Object} config The config object
3249  */
3250
3251 Roo.bootstrap.Row = function(config){
3252     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3253 };
3254
3255 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3256     
3257     getAutoCreate : function(){
3258        return {
3259             cls: 'row clearfix'
3260        };
3261     }
3262     
3263     
3264 });
3265
3266  
3267
3268  /*
3269  * - LGPL
3270  *
3271  * element
3272  * 
3273  */
3274
3275 /**
3276  * @class Roo.bootstrap.Element
3277  * @extends Roo.bootstrap.Component
3278  * Bootstrap Element class
3279  * @cfg {String} html contents of the element
3280  * @cfg {String} tag tag of the element
3281  * @cfg {String} cls class of the element
3282  * 
3283  * @constructor
3284  * Create a new Element
3285  * @param {Object} config The config object
3286  */
3287
3288 Roo.bootstrap.Element = function(config){
3289     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3290 };
3291
3292 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3293     
3294     tag: 'div',
3295     cls: '',
3296     html: '',
3297      
3298     
3299     getAutoCreate : function(){
3300         
3301         var cfg = {
3302             tag: this.tag,
3303             cls: this.cls,
3304             html: this.html
3305         }
3306         
3307         
3308         
3309         return cfg;
3310     }
3311    
3312 });
3313
3314  
3315
3316  /*
3317  * - LGPL
3318  *
3319  * pagination
3320  * 
3321  */
3322
3323 /**
3324  * @class Roo.bootstrap.Pagination
3325  * @extends Roo.bootstrap.Component
3326  * Bootstrap Pagination class
3327  * @cfg {String} size xs | sm | md | lg
3328  * @cfg {Boolean} inverse false | true
3329  * 
3330  * @constructor
3331  * Create a new Pagination
3332  * @param {Object} config The config object
3333  */
3334
3335 Roo.bootstrap.Pagination = function(config){
3336     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3337 };
3338
3339 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3340     
3341     cls: false,
3342     size: false,
3343     inverse: false,
3344     
3345     getAutoCreate : function(){
3346         var cfg = {
3347             tag: 'ul',
3348                 cls: 'pagination'
3349         };
3350         if (this.inverse) {
3351             cfg.cls += ' inverse';
3352         }
3353         if (this.html) {
3354             cfg.html=this.html;
3355         }
3356         if (this.cls) {
3357             cfg.cls += " " + this.cls;
3358         }
3359         return cfg;
3360     }
3361    
3362 });
3363
3364  
3365
3366  /*
3367  * - LGPL
3368  *
3369  * Pagination item
3370  * 
3371  */
3372
3373
3374 /**
3375  * @class Roo.bootstrap.PaginationItem
3376  * @extends Roo.bootstrap.Component
3377  * Bootstrap PaginationItem class
3378  * @cfg {String} html text
3379  * @cfg {String} href the link
3380  * @cfg {Boolean} preventDefault (true | false) default true
3381  * @cfg {Boolean} active (true | false) default false
3382  * 
3383  * 
3384  * @constructor
3385  * Create a new PaginationItem
3386  * @param {Object} config The config object
3387  */
3388
3389
3390 Roo.bootstrap.PaginationItem = function(config){
3391     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3392     this.addEvents({
3393         // raw events
3394         /**
3395          * @event click
3396          * The raw click event for the entire grid.
3397          * @param {Roo.EventObject} e
3398          */
3399         "click" : true
3400     });
3401 };
3402
3403 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3404     
3405     href : false,
3406     html : false,
3407     preventDefault: true,
3408     active : false,
3409     cls : false,
3410     
3411     getAutoCreate : function(){
3412         var cfg= {
3413             tag: 'li',
3414             cn: [
3415                 {
3416                     tag : 'a',
3417                     href : this.href ? this.href : '#',
3418                     html : this.html ? this.html : ''
3419                 }
3420             ]
3421         };
3422         
3423         if(this.cls){
3424             cfg.cls = this.cls;
3425         }
3426         
3427         if(this.active){
3428             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3429         }
3430         
3431         return cfg;
3432     },
3433     
3434     initEvents: function() {
3435         
3436         this.el.on('click', this.onClick, this);
3437         
3438     },
3439     onClick : function(e)
3440     {
3441         Roo.log('PaginationItem on click ');
3442         if(this.preventDefault){
3443             e.preventDefault();
3444         }
3445         
3446         this.fireEvent('click', this, e);
3447     }
3448    
3449 });
3450
3451  
3452
3453  /*
3454  * - LGPL
3455  *
3456  * slider
3457  * 
3458  */
3459
3460
3461 /**
3462  * @class Roo.bootstrap.Slider
3463  * @extends Roo.bootstrap.Component
3464  * Bootstrap Slider class
3465  *    
3466  * @constructor
3467  * Create a new Slider
3468  * @param {Object} config The config object
3469  */
3470
3471 Roo.bootstrap.Slider = function(config){
3472     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3473 };
3474
3475 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3476     
3477     getAutoCreate : function(){
3478         
3479         var cfg = {
3480             tag: 'div',
3481             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3482             cn: [
3483                 {
3484                     tag: 'a',
3485                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3486                 }
3487             ]
3488         }
3489         
3490         return cfg;
3491     }
3492    
3493 });
3494
3495  /*
3496  * - LGPL
3497  *
3498  * table
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.Table
3504  * @extends Roo.bootstrap.Component
3505  * Bootstrap Table class
3506  * @cfg {String} cls table class
3507  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3508  * @cfg {String} bgcolor Specifies the background color for a table
3509  * @cfg {Number} border Specifies whether the table cells should have borders or not
3510  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3511  * @cfg {Number} cellspacing Specifies the space between cells
3512  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3513  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3514  * @cfg {String} sortable Specifies that the table should be sortable
3515  * @cfg {String} summary Specifies a summary of the content of a table
3516  * @cfg {Number} width Specifies the width of a table
3517  * 
3518  * @cfg {boolean} striped Should the rows be alternative striped
3519  * @cfg {boolean} bordered Add borders to the table
3520  * @cfg {boolean} hover Add hover highlighting
3521  * @cfg {boolean} condensed Format condensed
3522  * @cfg {boolean} responsive Format condensed
3523  *
3524  
3525  
3526  * 
3527  * @constructor
3528  * Create a new Table
3529  * @param {Object} config The config object
3530  */
3531
3532 Roo.bootstrap.Table = function(config){
3533     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3534     
3535     if (this.sm) {
3536         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3537         this.sm = this.selModel;
3538         this.sm.xmodule = this.xmodule || false;
3539     }
3540     if (this.cm && typeof(this.cm.config) == 'undefined') {
3541         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3542         this.cm = this.colModel;
3543         this.cm.xmodule = this.xmodule || false;
3544     }
3545     if (this.store) {
3546         this.store= Roo.factory(this.store, Roo.data);
3547         this.ds = this.store;
3548         this.ds.xmodule = this.xmodule || false;
3549          
3550     }
3551 };
3552
3553 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3554     
3555     cls: false,
3556     align: false,
3557     bgcolor: false,
3558     border: false,
3559     cellpadding: false,
3560     cellspacing: false,
3561     frame: false,
3562     rules: false,
3563     sortable: false,
3564     summary: false,
3565     width: false,
3566     striped : false,
3567     bordered: false,
3568     hover:  false,
3569     condensed : false,
3570     responsive : false,
3571     sm : false,
3572     cm : false,
3573     store : false,
3574     
3575     getAutoCreate : function(){
3576         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3577         
3578         cfg = {
3579             tag: 'table',
3580             cls : 'table',
3581             cn : []
3582         }
3583             
3584         if (this.striped) {
3585             cfg.cls += ' table-striped';
3586         }
3587         if (this.hover) {
3588             cfg.cls += ' table-hover';
3589         }
3590         if (this.bordered) {
3591             cfg.cls += ' table-bordered';
3592         }
3593         if (this.condensed) {
3594             cfg.cls += ' table-condensed';
3595         }
3596         if (this.responsive) {
3597             cfg.cls += ' table-responsive';
3598         }
3599         
3600           
3601         
3602         
3603         if (this.cls) {
3604             cfg.cls+=  ' ' +this.cls;
3605         }
3606         
3607         // this lot should be simplifed...
3608         
3609         if (this.align) {
3610             cfg.align=this.align;
3611         }
3612         if (this.bgcolor) {
3613             cfg.bgcolor=this.bgcolor;
3614         }
3615         if (this.border) {
3616             cfg.border=this.border;
3617         }
3618         if (this.cellpadding) {
3619             cfg.cellpadding=this.cellpadding;
3620         }
3621         if (this.cellspacing) {
3622             cfg.cellspacing=this.cellspacing;
3623         }
3624         if (this.frame) {
3625             cfg.frame=this.frame;
3626         }
3627         if (this.rules) {
3628             cfg.rules=this.rules;
3629         }
3630         if (this.sortable) {
3631             cfg.sortable=this.sortable;
3632         }
3633         if (this.summary) {
3634             cfg.summary=this.summary;
3635         }
3636         if (this.width) {
3637             cfg.width=this.width;
3638         }
3639         
3640         if(this.store || this.cm){
3641             cfg.cn.push(this.renderHeader());
3642             cfg.cn.push(this.renderBody());
3643             cfg.cn.push(this.renderFooter());
3644             
3645             cfg.cls+=  ' TableGrid';
3646         }
3647         
3648         return cfg;
3649     },
3650 //    
3651 //    initTableGrid : function()
3652 //    {
3653 //        var cfg = {};
3654 //        
3655 //        var header = {
3656 //            tag: 'thead',
3657 //            cn : []
3658 //        };
3659 //        
3660 //        var cm = this.cm;
3661 //        
3662 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3663 //            header.cn.push({
3664 //                tag: 'th',
3665 //                html: cm.getColumnHeader(i)
3666 //            })
3667 //        }
3668 //        
3669 //        cfg.push(header);
3670 //        
3671 //        return cfg;
3672 //        
3673 //        
3674 //    },
3675     
3676     initEvents : function()
3677     {   
3678         if(!this.store || !this.cm){
3679             return;
3680         }
3681         
3682         Roo.log('initEvents with ds!!!!');
3683         
3684         var _this = this;
3685         
3686         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3687             e.on('click', _this.sort, _this);
3688         });
3689 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3690 //        this.maskEl.enableDisplayMode("block");
3691 //        this.maskEl.show();
3692         
3693         this.store.on('load', this.onLoad, this);
3694         this.store.on('beforeload', this.onBeforeLoad, this);
3695         
3696         this.store.load();
3697         
3698         
3699         
3700     },
3701     
3702     sort : function(e,el)
3703     {
3704         var col = Roo.get(el)
3705         
3706         if(!col.hasClass('sortable')){
3707             return;
3708         }
3709         
3710         var sort = col.attr('sort');
3711         var dir = 'ASC';
3712         
3713         if(col.hasClass('glyphicon-arrow-up')){
3714             dir = 'DESC';
3715         }
3716         
3717         this.store.sortInfo = {field : sort, direction : dir};
3718         
3719         this.store.load();
3720     },
3721     
3722     renderHeader : function()
3723     {
3724         var header = {
3725             tag: 'thead',
3726             cn : []
3727         };
3728         
3729         var cm = this.cm;
3730         
3731         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3732             
3733             var config = cm.config[i];
3734             
3735             var c = {
3736                 tag: 'th',
3737                 html: cm.getColumnHeader(i)
3738             };
3739             
3740             if(typeof(config.dataIndex) != 'undefined'){
3741                 c.sort = config.dataIndex;
3742             }
3743             
3744             if(typeof(config.sortable) != 'undefined' && config.sortable){
3745                 c.cls = 'sortable';
3746             }
3747             
3748             if(typeof(config.width) != 'undefined'){
3749                 c.style = 'width:' + config.width + 'px';
3750             }
3751             
3752             header.cn.push(c)
3753         }
3754         
3755         return header;
3756     },
3757     
3758     renderBody : function()
3759     {
3760         var body = {
3761             tag: 'tbody',
3762             cn : []
3763         };
3764         
3765         return body;
3766     },
3767     
3768     renderFooter : function()
3769     {
3770         var footer = {
3771             tag: 'tfoot',
3772             cn : []
3773         };
3774         
3775         return footer;
3776     },
3777     
3778     onLoad : function()
3779     {
3780         Roo.log('ds onload');
3781         
3782         var _this = this;
3783         var cm = this.cm;
3784         
3785         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3786             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3787             
3788             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3789                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3790             }
3791             
3792             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3793                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3794             }
3795         });
3796         
3797         var tbody = this.el.select('tbody', true).first();
3798         
3799         var renders = [];
3800         
3801         if(this.store.getCount() > 0){
3802             this.store.data.each(function(d){
3803                 var row = {
3804                     tag : 'tr',
3805                     cn : []
3806                 };
3807                 
3808                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3809                     var renderer = cm.getRenderer(i);
3810                     var config = cm.config[i];
3811                     var value = '';
3812                     var id = Roo.id();
3813                     
3814                     if(typeof(renderer) !== 'undefined'){
3815                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3816                     }
3817                     
3818                     if(typeof(value) === 'object'){
3819                         renders.push({
3820                             id : id,
3821                             cfg : value 
3822                         })
3823                     }
3824                     
3825                     var td = {
3826                         tag: 'td',
3827                         id: id,
3828                         html: (typeof(value) === 'object') ? '' : value
3829                     };
3830                     
3831                     if(typeof(config.width) != 'undefined'){
3832                         td.style = 'width:' +  config.width + 'px';
3833                     }
3834                     
3835                     row.cn.push(td);
3836                    
3837                 }
3838                 
3839                 tbody.createChild(row);
3840                 
3841             });
3842         }
3843         
3844         
3845         if(renders.length){
3846             var _this = this;
3847             Roo.each(renders, function(r){
3848                 _this.renderColumn(r);
3849             })
3850         }
3851 //        
3852 //        if(this.loadMask){
3853 //            this.maskEl.hide();
3854 //        }
3855     },
3856     
3857     onBeforeLoad : function()
3858     {
3859         Roo.log('ds onBeforeLoad');
3860         
3861         this.clear();
3862         
3863 //        if(this.loadMask){
3864 //            this.maskEl.show();
3865 //        }
3866     },
3867     
3868     clear : function()
3869     {
3870         this.el.select('tbody', true).first().dom.innerHTML = '';
3871     },
3872     
3873     getSelectionModel : function(){
3874         if(!this.selModel){
3875             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3876         }
3877         return this.selModel;
3878     },
3879     
3880     renderColumn : function(r)
3881     {
3882         var _this = this;
3883         r.cfg.render(Roo.get(r.id));
3884         
3885         if(r.cfg.cn){
3886             Roo.each(r.cfg.cn, function(c){
3887                 var child = {
3888                     id: r.id,
3889                     cfg: c
3890                 }
3891                 _this.renderColumn(child);
3892             })
3893         }
3894     }
3895    
3896 });
3897
3898  
3899
3900  /*
3901  * - LGPL
3902  *
3903  * table cell
3904  * 
3905  */
3906
3907 /**
3908  * @class Roo.bootstrap.TableCell
3909  * @extends Roo.bootstrap.Component
3910  * Bootstrap TableCell class
3911  * @cfg {String} html cell contain text
3912  * @cfg {String} cls cell class
3913  * @cfg {String} tag cell tag (td|th) default td
3914  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3915  * @cfg {String} align Aligns the content in a cell
3916  * @cfg {String} axis Categorizes cells
3917  * @cfg {String} bgcolor Specifies the background color of a cell
3918  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3919  * @cfg {Number} colspan Specifies the number of columns a cell should span
3920  * @cfg {String} headers Specifies one or more header cells a cell is related to
3921  * @cfg {Number} height Sets the height of a cell
3922  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3923  * @cfg {Number} rowspan Sets the number of rows a cell should span
3924  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3925  * @cfg {String} valign Vertical aligns the content in a cell
3926  * @cfg {Number} width Specifies the width of a cell
3927  * 
3928  * @constructor
3929  * Create a new TableCell
3930  * @param {Object} config The config object
3931  */
3932
3933 Roo.bootstrap.TableCell = function(config){
3934     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3935 };
3936
3937 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3938     
3939     html: false,
3940     cls: false,
3941     tag: false,
3942     abbr: false,
3943     align: false,
3944     axis: false,
3945     bgcolor: false,
3946     charoff: false,
3947     colspan: false,
3948     headers: false,
3949     height: false,
3950     nowrap: false,
3951     rowspan: false,
3952     scope: false,
3953     valign: false,
3954     width: false,
3955     
3956     
3957     getAutoCreate : function(){
3958         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3959         
3960         cfg = {
3961             tag: 'td'
3962         }
3963         
3964         if(this.tag){
3965             cfg.tag = this.tag;
3966         }
3967         
3968         if (this.html) {
3969             cfg.html=this.html
3970         }
3971         if (this.cls) {
3972             cfg.cls=this.cls
3973         }
3974         if (this.abbr) {
3975             cfg.abbr=this.abbr
3976         }
3977         if (this.align) {
3978             cfg.align=this.align
3979         }
3980         if (this.axis) {
3981             cfg.axis=this.axis
3982         }
3983         if (this.bgcolor) {
3984             cfg.bgcolor=this.bgcolor
3985         }
3986         if (this.charoff) {
3987             cfg.charoff=this.charoff
3988         }
3989         if (this.colspan) {
3990             cfg.colspan=this.colspan
3991         }
3992         if (this.headers) {
3993             cfg.headers=this.headers
3994         }
3995         if (this.height) {
3996             cfg.height=this.height
3997         }
3998         if (this.nowrap) {
3999             cfg.nowrap=this.nowrap
4000         }
4001         if (this.rowspan) {
4002             cfg.rowspan=this.rowspan
4003         }
4004         if (this.scope) {
4005             cfg.scope=this.scope
4006         }
4007         if (this.valign) {
4008             cfg.valign=this.valign
4009         }
4010         if (this.width) {
4011             cfg.width=this.width
4012         }
4013         
4014         
4015         return cfg;
4016     }
4017    
4018 });
4019
4020  
4021
4022  /*
4023  * - LGPL
4024  *
4025  * table row
4026  * 
4027  */
4028
4029 /**
4030  * @class Roo.bootstrap.TableRow
4031  * @extends Roo.bootstrap.Component
4032  * Bootstrap TableRow class
4033  * @cfg {String} cls row class
4034  * @cfg {String} align Aligns the content in a table row
4035  * @cfg {String} bgcolor Specifies a background color for a table row
4036  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4037  * @cfg {String} valign Vertical aligns the content in a table row
4038  * 
4039  * @constructor
4040  * Create a new TableRow
4041  * @param {Object} config The config object
4042  */
4043
4044 Roo.bootstrap.TableRow = function(config){
4045     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4046 };
4047
4048 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4049     
4050     cls: false,
4051     align: false,
4052     bgcolor: false,
4053     charoff: false,
4054     valign: false,
4055     
4056     getAutoCreate : function(){
4057         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4058         
4059         cfg = {
4060             tag: 'tr'
4061         }
4062             
4063         if(this.cls){
4064             cfg.cls = this.cls;
4065         }
4066         if(this.align){
4067             cfg.align = this.align;
4068         }
4069         if(this.bgcolor){
4070             cfg.bgcolor = this.bgcolor;
4071         }
4072         if(this.charoff){
4073             cfg.charoff = this.charoff;
4074         }
4075         if(this.valign){
4076             cfg.valign = this.valign;
4077         }
4078         
4079         return cfg;
4080     }
4081    
4082 });
4083
4084  
4085
4086  /*
4087  * - LGPL
4088  *
4089  * table body
4090  * 
4091  */
4092
4093 /**
4094  * @class Roo.bootstrap.TableBody
4095  * @extends Roo.bootstrap.Component
4096  * Bootstrap TableBody class
4097  * @cfg {String} cls element class
4098  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4099  * @cfg {String} align Aligns the content inside the element
4100  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4101  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4102  * 
4103  * @constructor
4104  * Create a new TableBody
4105  * @param {Object} config The config object
4106  */
4107
4108 Roo.bootstrap.TableBody = function(config){
4109     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4110 };
4111
4112 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4113     
4114     cls: false,
4115     tag: false,
4116     align: false,
4117     charoff: false,
4118     valign: false,
4119     
4120     getAutoCreate : function(){
4121         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4122         
4123         cfg = {
4124             tag: 'tbody'
4125         }
4126             
4127         if (this.cls) {
4128             cfg.cls=this.cls
4129         }
4130         if(this.tag){
4131             cfg.tag = this.tag;
4132         }
4133         
4134         if(this.align){
4135             cfg.align = this.align;
4136         }
4137         if(this.charoff){
4138             cfg.charoff = this.charoff;
4139         }
4140         if(this.valign){
4141             cfg.valign = this.valign;
4142         }
4143         
4144         return cfg;
4145     }
4146     
4147     
4148 //    initEvents : function()
4149 //    {
4150 //        
4151 //        if(!this.store){
4152 //            return;
4153 //        }
4154 //        
4155 //        this.store = Roo.factory(this.store, Roo.data);
4156 //        this.store.on('load', this.onLoad, this);
4157 //        
4158 //        this.store.load();
4159 //        
4160 //    },
4161 //    
4162 //    onLoad: function () 
4163 //    {   
4164 //        this.fireEvent('load', this);
4165 //    }
4166 //    
4167 //   
4168 });
4169
4170  
4171
4172  /*
4173  * Based on:
4174  * Ext JS Library 1.1.1
4175  * Copyright(c) 2006-2007, Ext JS, LLC.
4176  *
4177  * Originally Released Under LGPL - original licence link has changed is not relivant.
4178  *
4179  * Fork - LGPL
4180  * <script type="text/javascript">
4181  */
4182
4183 // as we use this in bootstrap.
4184 Roo.namespace('Roo.form');
4185  /**
4186  * @class Roo.form.Action
4187  * Internal Class used to handle form actions
4188  * @constructor
4189  * @param {Roo.form.BasicForm} el The form element or its id
4190  * @param {Object} config Configuration options
4191  */
4192
4193  
4194  
4195 // define the action interface
4196 Roo.form.Action = function(form, options){
4197     this.form = form;
4198     this.options = options || {};
4199 };
4200 /**
4201  * Client Validation Failed
4202  * @const 
4203  */
4204 Roo.form.Action.CLIENT_INVALID = 'client';
4205 /**
4206  * Server Validation Failed
4207  * @const 
4208  */
4209 Roo.form.Action.SERVER_INVALID = 'server';
4210  /**
4211  * Connect to Server Failed
4212  * @const 
4213  */
4214 Roo.form.Action.CONNECT_FAILURE = 'connect';
4215 /**
4216  * Reading Data from Server Failed
4217  * @const 
4218  */
4219 Roo.form.Action.LOAD_FAILURE = 'load';
4220
4221 Roo.form.Action.prototype = {
4222     type : 'default',
4223     failureType : undefined,
4224     response : undefined,
4225     result : undefined,
4226
4227     // interface method
4228     run : function(options){
4229
4230     },
4231
4232     // interface method
4233     success : function(response){
4234
4235     },
4236
4237     // interface method
4238     handleResponse : function(response){
4239
4240     },
4241
4242     // default connection failure
4243     failure : function(response){
4244         
4245         this.response = response;
4246         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4247         this.form.afterAction(this, false);
4248     },
4249
4250     processResponse : function(response){
4251         this.response = response;
4252         if(!response.responseText){
4253             return true;
4254         }
4255         this.result = this.handleResponse(response);
4256         return this.result;
4257     },
4258
4259     // utility functions used internally
4260     getUrl : function(appendParams){
4261         var url = this.options.url || this.form.url || this.form.el.dom.action;
4262         if(appendParams){
4263             var p = this.getParams();
4264             if(p){
4265                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4266             }
4267         }
4268         return url;
4269     },
4270
4271     getMethod : function(){
4272         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4273     },
4274
4275     getParams : function(){
4276         var bp = this.form.baseParams;
4277         var p = this.options.params;
4278         if(p){
4279             if(typeof p == "object"){
4280                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4281             }else if(typeof p == 'string' && bp){
4282                 p += '&' + Roo.urlEncode(bp);
4283             }
4284         }else if(bp){
4285             p = Roo.urlEncode(bp);
4286         }
4287         return p;
4288     },
4289
4290     createCallback : function(){
4291         return {
4292             success: this.success,
4293             failure: this.failure,
4294             scope: this,
4295             timeout: (this.form.timeout*1000),
4296             upload: this.form.fileUpload ? this.success : undefined
4297         };
4298     }
4299 };
4300
4301 Roo.form.Action.Submit = function(form, options){
4302     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4303 };
4304
4305 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4306     type : 'submit',
4307
4308     haveProgress : false,
4309     uploadComplete : false,
4310     
4311     // uploadProgress indicator.
4312     uploadProgress : function()
4313     {
4314         if (!this.form.progressUrl) {
4315             return;
4316         }
4317         
4318         if (!this.haveProgress) {
4319             Roo.MessageBox.progress("Uploading", "Uploading");
4320         }
4321         if (this.uploadComplete) {
4322            Roo.MessageBox.hide();
4323            return;
4324         }
4325         
4326         this.haveProgress = true;
4327    
4328         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4329         
4330         var c = new Roo.data.Connection();
4331         c.request({
4332             url : this.form.progressUrl,
4333             params: {
4334                 id : uid
4335             },
4336             method: 'GET',
4337             success : function(req){
4338                //console.log(data);
4339                 var rdata = false;
4340                 var edata;
4341                 try  {
4342                    rdata = Roo.decode(req.responseText)
4343                 } catch (e) {
4344                     Roo.log("Invalid data from server..");
4345                     Roo.log(edata);
4346                     return;
4347                 }
4348                 if (!rdata || !rdata.success) {
4349                     Roo.log(rdata);
4350                     Roo.MessageBox.alert(Roo.encode(rdata));
4351                     return;
4352                 }
4353                 var data = rdata.data;
4354                 
4355                 if (this.uploadComplete) {
4356                    Roo.MessageBox.hide();
4357                    return;
4358                 }
4359                    
4360                 if (data){
4361                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4362                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4363                     );
4364                 }
4365                 this.uploadProgress.defer(2000,this);
4366             },
4367        
4368             failure: function(data) {
4369                 Roo.log('progress url failed ');
4370                 Roo.log(data);
4371             },
4372             scope : this
4373         });
4374            
4375     },
4376     
4377     
4378     run : function()
4379     {
4380         // run get Values on the form, so it syncs any secondary forms.
4381         this.form.getValues();
4382         
4383         var o = this.options;
4384         var method = this.getMethod();
4385         var isPost = method == 'POST';
4386         if(o.clientValidation === false || this.form.isValid()){
4387             
4388             if (this.form.progressUrl) {
4389                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4390                     (new Date() * 1) + '' + Math.random());
4391                     
4392             } 
4393             
4394             
4395             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4396                 form:this.form.el.dom,
4397                 url:this.getUrl(!isPost),
4398                 method: method,
4399                 params:isPost ? this.getParams() : null,
4400                 isUpload: this.form.fileUpload
4401             }));
4402             
4403             this.uploadProgress();
4404
4405         }else if (o.clientValidation !== false){ // client validation failed
4406             this.failureType = Roo.form.Action.CLIENT_INVALID;
4407             this.form.afterAction(this, false);
4408         }
4409     },
4410
4411     success : function(response)
4412     {
4413         this.uploadComplete= true;
4414         if (this.haveProgress) {
4415             Roo.MessageBox.hide();
4416         }
4417         
4418         
4419         var result = this.processResponse(response);
4420         if(result === true || result.success){
4421             this.form.afterAction(this, true);
4422             return;
4423         }
4424         if(result.errors){
4425             this.form.markInvalid(result.errors);
4426             this.failureType = Roo.form.Action.SERVER_INVALID;
4427         }
4428         this.form.afterAction(this, false);
4429     },
4430     failure : function(response)
4431     {
4432         this.uploadComplete= true;
4433         if (this.haveProgress) {
4434             Roo.MessageBox.hide();
4435         }
4436         
4437         this.response = response;
4438         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4439         this.form.afterAction(this, false);
4440     },
4441     
4442     handleResponse : function(response){
4443         if(this.form.errorReader){
4444             var rs = this.form.errorReader.read(response);
4445             var errors = [];
4446             if(rs.records){
4447                 for(var i = 0, len = rs.records.length; i < len; i++) {
4448                     var r = rs.records[i];
4449                     errors[i] = r.data;
4450                 }
4451             }
4452             if(errors.length < 1){
4453                 errors = null;
4454             }
4455             return {
4456                 success : rs.success,
4457                 errors : errors
4458             };
4459         }
4460         var ret = false;
4461         try {
4462             ret = Roo.decode(response.responseText);
4463         } catch (e) {
4464             ret = {
4465                 success: false,
4466                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4467                 errors : []
4468             };
4469         }
4470         return ret;
4471         
4472     }
4473 });
4474
4475
4476 Roo.form.Action.Load = function(form, options){
4477     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4478     this.reader = this.form.reader;
4479 };
4480
4481 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4482     type : 'load',
4483
4484     run : function(){
4485         
4486         Roo.Ajax.request(Roo.apply(
4487                 this.createCallback(), {
4488                     method:this.getMethod(),
4489                     url:this.getUrl(false),
4490                     params:this.getParams()
4491         }));
4492     },
4493
4494     success : function(response){
4495         
4496         var result = this.processResponse(response);
4497         if(result === true || !result.success || !result.data){
4498             this.failureType = Roo.form.Action.LOAD_FAILURE;
4499             this.form.afterAction(this, false);
4500             return;
4501         }
4502         this.form.clearInvalid();
4503         this.form.setValues(result.data);
4504         this.form.afterAction(this, true);
4505     },
4506
4507     handleResponse : function(response){
4508         if(this.form.reader){
4509             var rs = this.form.reader.read(response);
4510             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4511             return {
4512                 success : rs.success,
4513                 data : data
4514             };
4515         }
4516         return Roo.decode(response.responseText);
4517     }
4518 });
4519
4520 Roo.form.Action.ACTION_TYPES = {
4521     'load' : Roo.form.Action.Load,
4522     'submit' : Roo.form.Action.Submit
4523 };/*
4524  * - LGPL
4525  *
4526  * form
4527  * 
4528  */
4529
4530 /**
4531  * @class Roo.bootstrap.Form
4532  * @extends Roo.bootstrap.Component
4533  * Bootstrap Form class
4534  * @cfg {String} method  GET | POST (default POST)
4535  * @cfg {String} labelAlign top | left (default top)
4536   * @cfg {String} align left  | right - for navbars
4537
4538  * 
4539  * @constructor
4540  * Create a new Form
4541  * @param {Object} config The config object
4542  */
4543
4544
4545 Roo.bootstrap.Form = function(config){
4546     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4547     this.addEvents({
4548         /**
4549          * @event clientvalidation
4550          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4551          * @param {Form} this
4552          * @param {Boolean} valid true if the form has passed client-side validation
4553          */
4554         clientvalidation: true,
4555         /**
4556          * @event beforeaction
4557          * Fires before any action is performed. Return false to cancel the action.
4558          * @param {Form} this
4559          * @param {Action} action The action to be performed
4560          */
4561         beforeaction: true,
4562         /**
4563          * @event actionfailed
4564          * Fires when an action fails.
4565          * @param {Form} this
4566          * @param {Action} action The action that failed
4567          */
4568         actionfailed : true,
4569         /**
4570          * @event actioncomplete
4571          * Fires when an action is completed.
4572          * @param {Form} this
4573          * @param {Action} action The action that completed
4574          */
4575         actioncomplete : true
4576     });
4577     
4578 };
4579
4580 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4581       
4582      /**
4583      * @cfg {String} method
4584      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4585      */
4586     method : 'POST',
4587     /**
4588      * @cfg {String} url
4589      * The URL to use for form actions if one isn't supplied in the action options.
4590      */
4591     /**
4592      * @cfg {Boolean} fileUpload
4593      * Set to true if this form is a file upload.
4594      */
4595      
4596     /**
4597      * @cfg {Object} baseParams
4598      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4599      */
4600       
4601     /**
4602      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4603      */
4604     timeout: 30,
4605     /**
4606      * @cfg {Sting} align (left|right) for navbar forms
4607      */
4608     align : 'left',
4609
4610     // private
4611     activeAction : null,
4612  
4613     /**
4614      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4615      * element by passing it or its id or mask the form itself by passing in true.
4616      * @type Mixed
4617      */
4618     waitMsgTarget : false,
4619     
4620      
4621     
4622     /**
4623      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4624      * element by passing it or its id or mask the form itself by passing in true.
4625      * @type Mixed
4626      */
4627     
4628     getAutoCreate : function(){
4629         
4630         var cfg = {
4631             tag: 'form',
4632             method : this.method || 'POST',
4633             id : this.id || Roo.id(),
4634             cls : ''
4635         }
4636         if (this.parent().xtype.match(/^Nav/)) {
4637             cfg.cls = 'navbar-form navbar-' + this.align;
4638             
4639         }
4640         
4641         if (this.labelAlign == 'left' ) {
4642             cfg.cls += ' form-horizontal';
4643         }
4644         
4645         
4646         return cfg;
4647     },
4648     initEvents : function()
4649     {
4650         this.el.on('submit', this.onSubmit, this);
4651         
4652         
4653     },
4654     // private
4655     onSubmit : function(e){
4656         e.stopEvent();
4657     },
4658     
4659      /**
4660      * Returns true if client-side validation on the form is successful.
4661      * @return Boolean
4662      */
4663     isValid : function(){
4664         var items = this.getItems();
4665         var valid = true;
4666         items.each(function(f){
4667            if(!f.validate()){
4668                valid = false;
4669                
4670            }
4671         });
4672         return valid;
4673     },
4674     /**
4675      * Returns true if any fields in this form have changed since their original load.
4676      * @return Boolean
4677      */
4678     isDirty : function(){
4679         var dirty = false;
4680         var items = this.getItems();
4681         items.each(function(f){
4682            if(f.isDirty()){
4683                dirty = true;
4684                return false;
4685            }
4686            return true;
4687         });
4688         return dirty;
4689     },
4690      /**
4691      * Performs a predefined action (submit or load) or custom actions you define on this form.
4692      * @param {String} actionName The name of the action type
4693      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
4694      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4695      * accept other config options):
4696      * <pre>
4697 Property          Type             Description
4698 ----------------  ---------------  ----------------------------------------------------------------------------------
4699 url               String           The url for the action (defaults to the form's url)
4700 method            String           The form method to use (defaults to the form's method, or POST if not defined)
4701 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
4702 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
4703                                    validate the form on the client (defaults to false)
4704      * </pre>
4705      * @return {BasicForm} this
4706      */
4707     doAction : function(action, options){
4708         if(typeof action == 'string'){
4709             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4710         }
4711         if(this.fireEvent('beforeaction', this, action) !== false){
4712             this.beforeAction(action);
4713             action.run.defer(100, action);
4714         }
4715         return this;
4716     },
4717     
4718     // private
4719     beforeAction : function(action){
4720         var o = action.options;
4721         
4722         // not really supported yet.. ??
4723         
4724         //if(this.waitMsgTarget === true){
4725             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4726         //}else if(this.waitMsgTarget){
4727         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4728         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4729         //}else {
4730         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4731        // }
4732          
4733     },
4734
4735     // private
4736     afterAction : function(action, success){
4737         this.activeAction = null;
4738         var o = action.options;
4739         
4740         //if(this.waitMsgTarget === true){
4741             this.el.unmask();
4742         //}else if(this.waitMsgTarget){
4743         //    this.waitMsgTarget.unmask();
4744         //}else{
4745         //    Roo.MessageBox.updateProgress(1);
4746         //    Roo.MessageBox.hide();
4747        // }
4748         // 
4749         if(success){
4750             if(o.reset){
4751                 this.reset();
4752             }
4753             Roo.callback(o.success, o.scope, [this, action]);
4754             this.fireEvent('actioncomplete', this, action);
4755             
4756         }else{
4757             
4758             // failure condition..
4759             // we have a scenario where updates need confirming.
4760             // eg. if a locking scenario exists..
4761             // we look for { errors : { needs_confirm : true }} in the response.
4762             if (
4763                 (typeof(action.result) != 'undefined')  &&
4764                 (typeof(action.result.errors) != 'undefined')  &&
4765                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4766            ){
4767                 var _t = this;
4768                 Roo.log("not supported yet");
4769                  /*
4770                 
4771                 Roo.MessageBox.confirm(
4772                     "Change requires confirmation",
4773                     action.result.errorMsg,
4774                     function(r) {
4775                         if (r != 'yes') {
4776                             return;
4777                         }
4778                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4779                     }
4780                     
4781                 );
4782                 */
4783                 
4784                 
4785                 return;
4786             }
4787             
4788             Roo.callback(o.failure, o.scope, [this, action]);
4789             // show an error message if no failed handler is set..
4790             if (!this.hasListener('actionfailed')) {
4791                 Roo.log("need to add dialog support");
4792                 /*
4793                 Roo.MessageBox.alert("Error",
4794                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4795                         action.result.errorMsg :
4796                         "Saving Failed, please check your entries or try again"
4797                 );
4798                 */
4799             }
4800             
4801             this.fireEvent('actionfailed', this, action);
4802         }
4803         
4804     },
4805     /**
4806      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4807      * @param {String} id The value to search for
4808      * @return Field
4809      */
4810     findField : function(id){
4811         var items = this.getItems();
4812         var field = items.get(id);
4813         if(!field){
4814              items.each(function(f){
4815                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4816                     field = f;
4817                     return false;
4818                 }
4819                 return true;
4820             });
4821         }
4822         return field || null;
4823     },
4824      /**
4825      * Mark fields in this form invalid in bulk.
4826      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4827      * @return {BasicForm} this
4828      */
4829     markInvalid : function(errors){
4830         if(errors instanceof Array){
4831             for(var i = 0, len = errors.length; i < len; i++){
4832                 var fieldError = errors[i];
4833                 var f = this.findField(fieldError.id);
4834                 if(f){
4835                     f.markInvalid(fieldError.msg);
4836                 }
4837             }
4838         }else{
4839             var field, id;
4840             for(id in errors){
4841                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4842                     field.markInvalid(errors[id]);
4843                 }
4844             }
4845         }
4846         //Roo.each(this.childForms || [], function (f) {
4847         //    f.markInvalid(errors);
4848         //});
4849         
4850         return this;
4851     },
4852
4853     /**
4854      * Set values for fields in this form in bulk.
4855      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4856      * @return {BasicForm} this
4857      */
4858     setValues : function(values){
4859         if(values instanceof Array){ // array of objects
4860             for(var i = 0, len = values.length; i < len; i++){
4861                 var v = values[i];
4862                 var f = this.findField(v.id);
4863                 if(f){
4864                     f.setValue(v.value);
4865                     if(this.trackResetOnLoad){
4866                         f.originalValue = f.getValue();
4867                     }
4868                 }
4869             }
4870         }else{ // object hash
4871             var field, id;
4872             for(id in values){
4873                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4874                     
4875                     if (field.setFromData && 
4876                         field.valueField && 
4877                         field.displayField &&
4878                         // combos' with local stores can 
4879                         // be queried via setValue()
4880                         // to set their value..
4881                         (field.store && !field.store.isLocal)
4882                         ) {
4883                         // it's a combo
4884                         var sd = { };
4885                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4886                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4887                         field.setFromData(sd);
4888                         
4889                     } else {
4890                         field.setValue(values[id]);
4891                     }
4892                     
4893                     
4894                     if(this.trackResetOnLoad){
4895                         field.originalValue = field.getValue();
4896                     }
4897                 }
4898             }
4899         }
4900          
4901         //Roo.each(this.childForms || [], function (f) {
4902         //    f.setValues(values);
4903         //});
4904                 
4905         return this;
4906     },
4907
4908     /**
4909      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4910      * they are returned as an array.
4911      * @param {Boolean} asString
4912      * @return {Object}
4913      */
4914     getValues : function(asString){
4915         //if (this.childForms) {
4916             // copy values from the child forms
4917         //    Roo.each(this.childForms, function (f) {
4918         //        this.setValues(f.getValues());
4919         //    }, this);
4920         //}
4921         
4922         
4923         
4924         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4925         if(asString === true){
4926             return fs;
4927         }
4928         return Roo.urlDecode(fs);
4929     },
4930     
4931     /**
4932      * Returns the fields in this form as an object with key/value pairs. 
4933      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4934      * @return {Object}
4935      */
4936     getFieldValues : function(with_hidden)
4937     {
4938         var items = this.getItems();
4939         var ret = {};
4940         items.each(function(f){
4941             if (!f.getName()) {
4942                 return;
4943             }
4944             var v = f.getValue();
4945             if (f.inputType =='radio') {
4946                 if (typeof(ret[f.getName()]) == 'undefined') {
4947                     ret[f.getName()] = ''; // empty..
4948                 }
4949                 
4950                 if (!f.el.dom.checked) {
4951                     return;
4952                     
4953                 }
4954                 v = f.el.dom.value;
4955                 
4956             }
4957             
4958             // not sure if this supported any more..
4959             if ((typeof(v) == 'object') && f.getRawValue) {
4960                 v = f.getRawValue() ; // dates..
4961             }
4962             // combo boxes where name != hiddenName...
4963             if (f.name != f.getName()) {
4964                 ret[f.name] = f.getRawValue();
4965             }
4966             ret[f.getName()] = v;
4967         });
4968         
4969         return ret;
4970     },
4971
4972     /**
4973      * Clears all invalid messages in this form.
4974      * @return {BasicForm} this
4975      */
4976     clearInvalid : function(){
4977         var items = this.getItems();
4978         
4979         items.each(function(f){
4980            f.clearInvalid();
4981         });
4982         
4983         
4984         
4985         return this;
4986     },
4987
4988     /**
4989      * Resets this form.
4990      * @return {BasicForm} this
4991      */
4992     reset : function(){
4993         var items = this.getItems();
4994         items.each(function(f){
4995             f.reset();
4996         });
4997         
4998         Roo.each(this.childForms || [], function (f) {
4999             f.reset();
5000         });
5001        
5002         
5003         return this;
5004     },
5005     getItems : function()
5006     {
5007         var r=new Roo.util.MixedCollection(false, function(o){
5008             return o.id || (o.id = Roo.id());
5009         });
5010         var iter = function(el) {
5011             if (el.inputEl) {
5012                 r.add(el);
5013             }
5014             if (!el.items) {
5015                 return;
5016             }
5017             Roo.each(el.items,function(e) {
5018                 iter(e);
5019             });
5020             
5021             
5022         };
5023         iter(this);
5024         return r;
5025         
5026         
5027         
5028         
5029     }
5030     
5031 });
5032
5033  
5034 /*
5035  * Based on:
5036  * Ext JS Library 1.1.1
5037  * Copyright(c) 2006-2007, Ext JS, LLC.
5038  *
5039  * Originally Released Under LGPL - original licence link has changed is not relivant.
5040  *
5041  * Fork - LGPL
5042  * <script type="text/javascript">
5043  */
5044 /**
5045  * @class Roo.form.VTypes
5046  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5047  * @singleton
5048  */
5049 Roo.form.VTypes = function(){
5050     // closure these in so they are only created once.
5051     var alpha = /^[a-zA-Z_]+$/;
5052     var alphanum = /^[a-zA-Z0-9_]+$/;
5053     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5054     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5055
5056     // All these messages and functions are configurable
5057     return {
5058         /**
5059          * The function used to validate email addresses
5060          * @param {String} value The email address
5061          */
5062         'email' : function(v){
5063             return email.test(v);
5064         },
5065         /**
5066          * The error text to display when the email validation function returns false
5067          * @type String
5068          */
5069         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5070         /**
5071          * The keystroke filter mask to be applied on email input
5072          * @type RegExp
5073          */
5074         'emailMask' : /[a-z0-9_\.\-@]/i,
5075
5076         /**
5077          * The function used to validate URLs
5078          * @param {String} value The URL
5079          */
5080         'url' : function(v){
5081             return url.test(v);
5082         },
5083         /**
5084          * The error text to display when the url validation function returns false
5085          * @type String
5086          */
5087         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5088         
5089         /**
5090          * The function used to validate alpha values
5091          * @param {String} value The value
5092          */
5093         'alpha' : function(v){
5094             return alpha.test(v);
5095         },
5096         /**
5097          * The error text to display when the alpha validation function returns false
5098          * @type String
5099          */
5100         'alphaText' : 'This field should only contain letters and _',
5101         /**
5102          * The keystroke filter mask to be applied on alpha input
5103          * @type RegExp
5104          */
5105         'alphaMask' : /[a-z_]/i,
5106
5107         /**
5108          * The function used to validate alphanumeric values
5109          * @param {String} value The value
5110          */
5111         'alphanum' : function(v){
5112             return alphanum.test(v);
5113         },
5114         /**
5115          * The error text to display when the alphanumeric validation function returns false
5116          * @type String
5117          */
5118         'alphanumText' : 'This field should only contain letters, numbers and _',
5119         /**
5120          * The keystroke filter mask to be applied on alphanumeric input
5121          * @type RegExp
5122          */
5123         'alphanumMask' : /[a-z0-9_]/i
5124     };
5125 }();/*
5126  * - LGPL
5127  *
5128  * Input
5129  * 
5130  */
5131
5132 /**
5133  * @class Roo.bootstrap.Input
5134  * @extends Roo.bootstrap.Component
5135  * Bootstrap Input class
5136  * @cfg {Boolean} disabled is it disabled
5137  * @cfg {String} fieldLabel - the label associated
5138  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5139  * @cfg {String} name name of the input
5140  * @cfg {string} fieldLabel - the label associated
5141  * @cfg {string}  inputType - input / file submit ...
5142  * @cfg {string} placeholder - placeholder to put in text.
5143  * @cfg {string}  before - input group add on before
5144  * @cfg {string} after - input group add on after
5145  * @cfg {string} size - (lg|sm) or leave empty..
5146  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5147  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5148  * @cfg {Number} md colspan out of 12 for computer-sized screens
5149  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5150  * @cfg {string} value default value of the input
5151  * @cfg {Number} labelWidth set the width of label (0-12)
5152  * @cfg {String} labelAlign (top|left)
5153  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5154  * 
5155  * 
5156  * @constructor
5157  * Create a new Input
5158  * @param {Object} config The config object
5159  */
5160
5161 Roo.bootstrap.Input = function(config){
5162     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5163    
5164         this.addEvents({
5165             /**
5166              * @event focus
5167              * Fires when this field receives input focus.
5168              * @param {Roo.form.Field} this
5169              */
5170             focus : true,
5171             /**
5172              * @event blur
5173              * Fires when this field loses input focus.
5174              * @param {Roo.form.Field} this
5175              */
5176             blur : true,
5177             /**
5178              * @event specialkey
5179              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5180              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5181              * @param {Roo.form.Field} this
5182              * @param {Roo.EventObject} e The event object
5183              */
5184             specialkey : true,
5185             /**
5186              * @event change
5187              * Fires just before the field blurs if the field value has changed.
5188              * @param {Roo.form.Field} this
5189              * @param {Mixed} newValue The new value
5190              * @param {Mixed} oldValue The original value
5191              */
5192             change : true,
5193             /**
5194              * @event invalid
5195              * Fires after the field has been marked as invalid.
5196              * @param {Roo.form.Field} this
5197              * @param {String} msg The validation message
5198              */
5199             invalid : true,
5200             /**
5201              * @event valid
5202              * Fires after the field has been validated with no errors.
5203              * @param {Roo.form.Field} this
5204              */
5205             valid : true,
5206              /**
5207              * @event keyup
5208              * Fires after the key up
5209              * @param {Roo.form.Field} this
5210              * @param {Roo.EventObject}  e The event Object
5211              */
5212             keyup : true
5213         });
5214 };
5215
5216 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5217      /**
5218      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5219       automatic validation (defaults to "keyup").
5220      */
5221     validationEvent : "keyup",
5222      /**
5223      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5224      */
5225     validateOnBlur : true,
5226     /**
5227      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5228      */
5229     validationDelay : 250,
5230      /**
5231      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5232      */
5233     focusClass : "x-form-focus",  // not needed???
5234     
5235        
5236     /**
5237      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5238      */
5239     invalidClass : "has-error",
5240     
5241     /**
5242      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5243      */
5244     selectOnFocus : false,
5245     
5246      /**
5247      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5248      */
5249     maskRe : null,
5250        /**
5251      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5252      */
5253     vtype : null,
5254     
5255       /**
5256      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5257      */
5258     disableKeyFilter : false,
5259     
5260        /**
5261      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5262      */
5263     disabled : false,
5264      /**
5265      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5266      */
5267     allowBlank : true,
5268     /**
5269      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5270      */
5271     blankText : "This field is required",
5272     
5273      /**
5274      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5275      */
5276     minLength : 0,
5277     /**
5278      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5279      */
5280     maxLength : Number.MAX_VALUE,
5281     /**
5282      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5283      */
5284     minLengthText : "The minimum length for this field is {0}",
5285     /**
5286      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5287      */
5288     maxLengthText : "The maximum length for this field is {0}",
5289   
5290     
5291     /**
5292      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5293      * If available, this function will be called only after the basic validators all return true, and will be passed the
5294      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5295      */
5296     validator : null,
5297     /**
5298      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5299      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5300      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5301      */
5302     regex : null,
5303     /**
5304      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5305      */
5306     regexText : "",
5307     
5308     
5309     
5310     fieldLabel : '',
5311     inputType : 'text',
5312     
5313     name : false,
5314     placeholder: false,
5315     before : false,
5316     after : false,
5317     size : false,
5318     // private
5319     hasFocus : false,
5320     preventMark: false,
5321     isFormField : true,
5322     value : '',
5323     labelWidth : 2,
5324     labelAlign : false,
5325     readOnly : false,
5326     
5327     parentLabelAlign : function()
5328     {
5329         var parent = this;
5330         while (parent.parent()) {
5331             parent = parent.parent();
5332             if (typeof(parent.labelAlign) !='undefined') {
5333                 return parent.labelAlign;
5334             }
5335         }
5336         return 'left';
5337         
5338     },
5339     
5340     getAutoCreate : function(){
5341         
5342         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5343         
5344         var id = Roo.id();
5345         
5346         var cfg = {};
5347         
5348         if(this.inputType != 'hidden'){
5349             cfg.cls = 'form-group' //input-group
5350         }
5351         
5352         var input =  {
5353             tag: 'input',
5354             id : id,
5355             type : this.inputType,
5356             value : this.value,
5357             cls : 'form-control',
5358             placeholder : this.placeholder || ''
5359             
5360         };
5361         
5362         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5363             input.maxLength = this.maxLength;
5364         }
5365         
5366         if (this.disabled) {
5367             input.disabled=true;
5368         }
5369         
5370         if (this.readOnly) {
5371             input.readonly=true;
5372         }
5373         
5374         if (this.name) {
5375             input.name = this.name;
5376         }
5377         if (this.size) {
5378             input.cls += ' input-' + this.size;
5379         }
5380         var settings=this;
5381         ['xs','sm','md','lg'].map(function(size){
5382             if (settings[size]) {
5383                 cfg.cls += ' col-' + size + '-' + settings[size];
5384             }
5385         });
5386         
5387         var inputblock = input;
5388         
5389         if (this.before || this.after) {
5390             
5391             inputblock = {
5392                 cls : 'input-group',
5393                 cn :  [] 
5394             };
5395             if (this.before) {
5396                 inputblock.cn.push({
5397                     tag :'span',
5398                     cls : 'input-group-addon',
5399                     html : this.before
5400                 });
5401             }
5402             inputblock.cn.push(input);
5403             if (this.after) {
5404                 inputblock.cn.push({
5405                     tag :'span',
5406                     cls : 'input-group-addon',
5407                     html : this.after
5408                 });
5409             }
5410             
5411         };
5412         
5413         if (align ==='left' && this.fieldLabel.length) {
5414                 Roo.log("left and has label");
5415                 cfg.cn = [
5416                     
5417                     {
5418                         tag: 'label',
5419                         'for' :  id,
5420                         cls : 'control-label col-sm-' + this.labelWidth,
5421                         html : this.fieldLabel
5422                         
5423                     },
5424                     {
5425                         cls : "col-sm-" + (12 - this.labelWidth), 
5426                         cn: [
5427                             inputblock
5428                         ]
5429                     }
5430                     
5431                 ];
5432         } else if ( this.fieldLabel.length) {
5433                 Roo.log(" label");
5434                  cfg.cn = [
5435                    
5436                     {
5437                         tag: 'label',
5438                         //cls : 'input-group-addon',
5439                         html : this.fieldLabel
5440                         
5441                     },
5442                     
5443                     inputblock
5444                     
5445                 ];
5446
5447         } else {
5448             
5449                 Roo.log(" no label && no align");
5450                 cfg.cn = [
5451                     
5452                         inputblock
5453                     
5454                 ];
5455                 
5456                 
5457         };
5458         Roo.log('input-parentType: ' + this.parentType);
5459         
5460         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5461            cfg.cls += ' navbar-form';
5462            Roo.log(cfg);
5463         }
5464         
5465         return cfg;
5466         
5467     },
5468     /**
5469      * return the real input element.
5470      */
5471     inputEl: function ()
5472     {
5473         return this.el.select('input.form-control',true).first();
5474     },
5475     setDisabled : function(v)
5476     {
5477         var i  = this.inputEl().dom;
5478         if (!v) {
5479             i.removeAttribute('disabled');
5480             return;
5481             
5482         }
5483         i.setAttribute('disabled','true');
5484     },
5485     initEvents : function()
5486     {
5487         
5488         this.inputEl().on("keydown" , this.fireKey,  this);
5489         this.inputEl().on("focus", this.onFocus,  this);
5490         this.inputEl().on("blur", this.onBlur,  this);
5491         
5492         this.inputEl().relayEvent('keyup', this);
5493
5494         // reference to original value for reset
5495         this.originalValue = this.getValue();
5496         //Roo.form.TextField.superclass.initEvents.call(this);
5497         if(this.validationEvent == 'keyup'){
5498             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5499             this.inputEl().on('keyup', this.filterValidation, this);
5500         }
5501         else if(this.validationEvent !== false){
5502             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5503         }
5504         
5505         if(this.selectOnFocus){
5506             this.on("focus", this.preFocus, this);
5507             
5508         }
5509         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5510             this.inputEl().on("keypress", this.filterKeys, this);
5511         }
5512        /* if(this.grow){
5513             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5514             this.el.on("click", this.autoSize,  this);
5515         }
5516         */
5517         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5518             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5519         }
5520         
5521     },
5522     filterValidation : function(e){
5523         if(!e.isNavKeyPress()){
5524             this.validationTask.delay(this.validationDelay);
5525         }
5526     },
5527      /**
5528      * Validates the field value
5529      * @return {Boolean} True if the value is valid, else false
5530      */
5531     validate : function(){
5532         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5533         if(this.disabled || this.validateValue(this.getRawValue())){
5534             this.clearInvalid();
5535             return true;
5536         }
5537         return false;
5538     },
5539     
5540     
5541     /**
5542      * Validates a value according to the field's validation rules and marks the field as invalid
5543      * if the validation fails
5544      * @param {Mixed} value The value to validate
5545      * @return {Boolean} True if the value is valid, else false
5546      */
5547     validateValue : function(value){
5548         if(value.length < 1)  { // if it's blank
5549              if(this.allowBlank){
5550                 this.clearInvalid();
5551                 return true;
5552              }else{
5553                 this.markInvalid(this.blankText);
5554                 return false;
5555              }
5556         }
5557         if(value.length < this.minLength){
5558             this.markInvalid(String.format(this.minLengthText, this.minLength));
5559             return false;
5560         }
5561         if(value.length > this.maxLength){
5562             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5563             return false;
5564         }
5565         if(this.vtype){
5566             var vt = Roo.form.VTypes;
5567             if(!vt[this.vtype](value, this)){
5568                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5569                 return false;
5570             }
5571         }
5572         if(typeof this.validator == "function"){
5573             var msg = this.validator(value);
5574             if(msg !== true){
5575                 this.markInvalid(msg);
5576                 return false;
5577             }
5578         }
5579         if(this.regex && !this.regex.test(value)){
5580             this.markInvalid(this.regexText);
5581             return false;
5582         }
5583         return true;
5584     },
5585
5586     
5587     
5588      // private
5589     fireKey : function(e){
5590         //Roo.log('field ' + e.getKey());
5591         if(e.isNavKeyPress()){
5592             this.fireEvent("specialkey", this, e);
5593         }
5594     },
5595     focus : function (selectText){
5596         if(this.rendered){
5597             this.inputEl().focus();
5598             if(selectText === true){
5599                 this.inputEl().dom.select();
5600             }
5601         }
5602         return this;
5603     } ,
5604     
5605     onFocus : function(){
5606         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5607            // this.el.addClass(this.focusClass);
5608         }
5609         if(!this.hasFocus){
5610             this.hasFocus = true;
5611             this.startValue = this.getValue();
5612             this.fireEvent("focus", this);
5613         }
5614     },
5615     
5616     beforeBlur : Roo.emptyFn,
5617
5618     
5619     // private
5620     onBlur : function(){
5621         this.beforeBlur();
5622         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5623             //this.el.removeClass(this.focusClass);
5624         }
5625         this.hasFocus = false;
5626         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5627             this.validate();
5628         }
5629         var v = this.getValue();
5630         if(String(v) !== String(this.startValue)){
5631             this.fireEvent('change', this, v, this.startValue);
5632         }
5633         this.fireEvent("blur", this);
5634     },
5635     
5636     /**
5637      * Resets the current field value to the originally loaded value and clears any validation messages
5638      */
5639     reset : function(){
5640         this.setValue(this.originalValue);
5641         this.clearInvalid();
5642     },
5643      /**
5644      * Returns the name of the field
5645      * @return {Mixed} name The name field
5646      */
5647     getName: function(){
5648         return this.name;
5649     },
5650      /**
5651      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
5652      * @return {Mixed} value The field value
5653      */
5654     getValue : function(){
5655         return this.inputEl().getValue();
5656     },
5657     /**
5658      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
5659      * @return {Mixed} value The field value
5660      */
5661     getRawValue : function(){
5662         var v = this.inputEl().getValue();
5663         
5664         return v;
5665     },
5666     
5667     /**
5668      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
5669      * @param {Mixed} value The value to set
5670      */
5671     setRawValue : function(v){
5672         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5673     },
5674     
5675     selectText : function(start, end){
5676         var v = this.getRawValue();
5677         if(v.length > 0){
5678             start = start === undefined ? 0 : start;
5679             end = end === undefined ? v.length : end;
5680             var d = this.inputEl().dom;
5681             if(d.setSelectionRange){
5682                 d.setSelectionRange(start, end);
5683             }else if(d.createTextRange){
5684                 var range = d.createTextRange();
5685                 range.moveStart("character", start);
5686                 range.moveEnd("character", v.length-end);
5687                 range.select();
5688             }
5689         }
5690     },
5691     
5692     /**
5693      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
5694      * @param {Mixed} value The value to set
5695      */
5696     setValue : function(v){
5697         this.value = v;
5698         if(this.rendered){
5699             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5700             this.validate();
5701         }
5702     },
5703     
5704     /*
5705     processValue : function(value){
5706         if(this.stripCharsRe){
5707             var newValue = value.replace(this.stripCharsRe, '');
5708             if(newValue !== value){
5709                 this.setRawValue(newValue);
5710                 return newValue;
5711             }
5712         }
5713         return value;
5714     },
5715   */
5716     preFocus : function(){
5717         
5718         if(this.selectOnFocus){
5719             this.inputEl().dom.select();
5720         }
5721     },
5722     filterKeys : function(e){
5723         var k = e.getKey();
5724         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5725             return;
5726         }
5727         var c = e.getCharCode(), cc = String.fromCharCode(c);
5728         if(Roo.isIE && (e.isSpecialKey() || !cc)){
5729             return;
5730         }
5731         if(!this.maskRe.test(cc)){
5732             e.stopEvent();
5733         }
5734     },
5735      /**
5736      * Clear any invalid styles/messages for this field
5737      */
5738     clearInvalid : function(){
5739         
5740         if(!this.el || this.preventMark){ // not rendered
5741             return;
5742         }
5743         this.el.removeClass(this.invalidClass);
5744         /*
5745         switch(this.msgTarget){
5746             case 'qtip':
5747                 this.el.dom.qtip = '';
5748                 break;
5749             case 'title':
5750                 this.el.dom.title = '';
5751                 break;
5752             case 'under':
5753                 if(this.errorEl){
5754                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5755                 }
5756                 break;
5757             case 'side':
5758                 if(this.errorIcon){
5759                     this.errorIcon.dom.qtip = '';
5760                     this.errorIcon.hide();
5761                     this.un('resize', this.alignErrorIcon, this);
5762                 }
5763                 break;
5764             default:
5765                 var t = Roo.getDom(this.msgTarget);
5766                 t.innerHTML = '';
5767                 t.style.display = 'none';
5768                 break;
5769         }
5770         */
5771         this.fireEvent('valid', this);
5772     },
5773      /**
5774      * Mark this field as invalid
5775      * @param {String} msg The validation message
5776      */
5777     markInvalid : function(msg){
5778         if(!this.el  || this.preventMark){ // not rendered
5779             return;
5780         }
5781         this.el.addClass(this.invalidClass);
5782         /*
5783         msg = msg || this.invalidText;
5784         switch(this.msgTarget){
5785             case 'qtip':
5786                 this.el.dom.qtip = msg;
5787                 this.el.dom.qclass = 'x-form-invalid-tip';
5788                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5789                     Roo.QuickTips.enable();
5790                 }
5791                 break;
5792             case 'title':
5793                 this.el.dom.title = msg;
5794                 break;
5795             case 'under':
5796                 if(!this.errorEl){
5797                     var elp = this.el.findParent('.x-form-element', 5, true);
5798                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5799                     this.errorEl.setWidth(elp.getWidth(true)-20);
5800                 }
5801                 this.errorEl.update(msg);
5802                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5803                 break;
5804             case 'side':
5805                 if(!this.errorIcon){
5806                     var elp = this.el.findParent('.x-form-element', 5, true);
5807                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5808                 }
5809                 this.alignErrorIcon();
5810                 this.errorIcon.dom.qtip = msg;
5811                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5812                 this.errorIcon.show();
5813                 this.on('resize', this.alignErrorIcon, this);
5814                 break;
5815             default:
5816                 var t = Roo.getDom(this.msgTarget);
5817                 t.innerHTML = msg;
5818                 t.style.display = this.msgDisplay;
5819                 break;
5820         }
5821         */
5822         this.fireEvent('invalid', this, msg);
5823     },
5824     // private
5825     SafariOnKeyDown : function(event)
5826     {
5827         // this is a workaround for a password hang bug on chrome/ webkit.
5828         
5829         var isSelectAll = false;
5830         
5831         if(this.inputEl().dom.selectionEnd > 0){
5832             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5833         }
5834         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5835             event.preventDefault();
5836             this.setValue('');
5837             return;
5838         }
5839         
5840         if(isSelectAll){ // backspace and delete key
5841             
5842             event.preventDefault();
5843             // this is very hacky as keydown always get's upper case.
5844             //
5845             var cc = String.fromCharCode(event.getCharCode());
5846             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5847             
5848         }
5849     },
5850     adjustWidth : function(tag, w){
5851         tag = tag.toLowerCase();
5852         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5853             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5854                 if(tag == 'input'){
5855                     return w + 2;
5856                 }
5857                 if(tag == 'textarea'){
5858                     return w-2;
5859                 }
5860             }else if(Roo.isOpera){
5861                 if(tag == 'input'){
5862                     return w + 2;
5863                 }
5864                 if(tag == 'textarea'){
5865                     return w-2;
5866                 }
5867             }
5868         }
5869         return w;
5870     }
5871     
5872 });
5873
5874  
5875 /*
5876  * - LGPL
5877  *
5878  * Input
5879  * 
5880  */
5881
5882 /**
5883  * @class Roo.bootstrap.TextArea
5884  * @extends Roo.bootstrap.Input
5885  * Bootstrap TextArea class
5886  * @cfg {Number} cols Specifies the visible width of a text area
5887  * @cfg {Number} rows Specifies the visible number of lines in a text area
5888  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5889  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5890  * @cfg {string} html text
5891  * 
5892  * @constructor
5893  * Create a new TextArea
5894  * @param {Object} config The config object
5895  */
5896
5897 Roo.bootstrap.TextArea = function(config){
5898     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5899    
5900 };
5901
5902 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5903      
5904     cols : false,
5905     rows : 5,
5906     readOnly : false,
5907     warp : 'soft',
5908     resize : false,
5909     value: false,
5910     html: false,
5911     
5912     getAutoCreate : function(){
5913         
5914         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5915         
5916         var id = Roo.id();
5917         
5918         var cfg = {};
5919         
5920         var input =  {
5921             tag: 'textarea',
5922             id : id,
5923             warp : this.warp,
5924             rows : this.rows,
5925             value : this.value || '',
5926             html: this.html || '',
5927             cls : 'form-control',
5928             placeholder : this.placeholder || '' 
5929             
5930         };
5931         
5932         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5933             input.maxLength = this.maxLength;
5934         }
5935         
5936         if(this.resize){
5937             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5938         }
5939         
5940         if(this.cols){
5941             input.cols = this.cols;
5942         }
5943         
5944         if (this.readOnly) {
5945             input.readonly = true;
5946         }
5947         
5948         if (this.name) {
5949             input.name = this.name;
5950         }
5951         
5952         if (this.size) {
5953             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5954         }
5955         
5956         var settings=this;
5957         ['xs','sm','md','lg'].map(function(size){
5958             if (settings[size]) {
5959                 cfg.cls += ' col-' + size + '-' + settings[size];
5960             }
5961         });
5962         
5963         var inputblock = input;
5964         
5965         if (this.before || this.after) {
5966             
5967             inputblock = {
5968                 cls : 'input-group',
5969                 cn :  [] 
5970             };
5971             if (this.before) {
5972                 inputblock.cn.push({
5973                     tag :'span',
5974                     cls : 'input-group-addon',
5975                     html : this.before
5976                 });
5977             }
5978             inputblock.cn.push(input);
5979             if (this.after) {
5980                 inputblock.cn.push({
5981                     tag :'span',
5982                     cls : 'input-group-addon',
5983                     html : this.after
5984                 });
5985             }
5986             
5987         }
5988         
5989         if (align ==='left' && this.fieldLabel.length) {
5990                 Roo.log("left and has label");
5991                 cfg.cn = [
5992                     
5993                     {
5994                         tag: 'label',
5995                         'for' :  id,
5996                         cls : 'control-label col-sm-' + this.labelWidth,
5997                         html : this.fieldLabel
5998                         
5999                     },
6000                     {
6001                         cls : "col-sm-" + (12 - this.labelWidth), 
6002                         cn: [
6003                             inputblock
6004                         ]
6005                     }
6006                     
6007                 ];
6008         } else if ( this.fieldLabel.length) {
6009                 Roo.log(" label");
6010                  cfg.cn = [
6011                    
6012                     {
6013                         tag: 'label',
6014                         //cls : 'input-group-addon',
6015                         html : this.fieldLabel
6016                         
6017                     },
6018                     
6019                     inputblock
6020                     
6021                 ];
6022
6023         } else {
6024             
6025                    Roo.log(" no label && no align");
6026                 cfg.cn = [
6027                     
6028                         inputblock
6029                     
6030                 ];
6031                 
6032                 
6033         }
6034         
6035         if (this.disabled) {
6036             input.disabled=true;
6037         }
6038         
6039         return cfg;
6040         
6041     },
6042     /**
6043      * return the real textarea element.
6044      */
6045     inputEl: function ()
6046     {
6047         return this.el.select('textarea.form-control',true).first();
6048     }
6049 });
6050
6051  
6052 /*
6053  * - LGPL
6054  *
6055  * trigger field - base class for combo..
6056  * 
6057  */
6058  
6059 /**
6060  * @class Roo.bootstrap.TriggerField
6061  * @extends Roo.bootstrap.Input
6062  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6063  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6064  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6065  * for which you can provide a custom implementation.  For example:
6066  * <pre><code>
6067 var trigger = new Roo.bootstrap.TriggerField();
6068 trigger.onTriggerClick = myTriggerFn;
6069 trigger.applyTo('my-field');
6070 </code></pre>
6071  *
6072  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6073  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6074  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6075  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6076  * @constructor
6077  * Create a new TriggerField.
6078  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6079  * to the base TextField)
6080  */
6081 Roo.bootstrap.TriggerField = function(config){
6082     this.mimicing = false;
6083     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6084 };
6085
6086 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6087     /**
6088      * @cfg {String} triggerClass A CSS class to apply to the trigger
6089      */
6090      /**
6091      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6092      */
6093     hideTrigger:false,
6094
6095     /** @cfg {Boolean} grow @hide */
6096     /** @cfg {Number} growMin @hide */
6097     /** @cfg {Number} growMax @hide */
6098
6099     /**
6100      * @hide 
6101      * @method
6102      */
6103     autoSize: Roo.emptyFn,
6104     // private
6105     monitorTab : true,
6106     // private
6107     deferHeight : true,
6108
6109     
6110     actionMode : 'wrap',
6111     
6112     
6113     
6114     getAutoCreate : function(){
6115        
6116         var parent = this.parent();
6117         
6118         var align = this.parentLabelAlign();
6119         
6120         var id = Roo.id();
6121         
6122         var cfg = {
6123             cls: 'form-group' //input-group
6124         };
6125         
6126         
6127         var input =  {
6128             tag: 'input',
6129             id : id,
6130             type : this.inputType,
6131             cls : 'form-control',
6132             autocomplete: 'off',
6133             placeholder : this.placeholder || '' 
6134             
6135         };
6136         if (this.name) {
6137             input.name = this.name;
6138         }
6139         if (this.size) {
6140             input.cls += ' input-' + this.size;
6141         }
6142         
6143         if (this.disabled) {
6144             input.disabled=true;
6145         }
6146         
6147         var inputblock = input;
6148         
6149         if (this.before || this.after) {
6150             
6151             inputblock = {
6152                 cls : 'input-group',
6153                 cn :  [] 
6154             };
6155             if (this.before) {
6156                 inputblock.cn.push({
6157                     tag :'span',
6158                     cls : 'input-group-addon',
6159                     html : this.before
6160                 });
6161             }
6162             inputblock.cn.push(input);
6163             if (this.after) {
6164                 inputblock.cn.push({
6165                     tag :'span',
6166                     cls : 'input-group-addon',
6167                     html : this.after
6168                 });
6169             }
6170             
6171         };
6172         
6173         var box = {
6174             tag: 'div',
6175             cn: [
6176                 {
6177                     tag: 'input',
6178                     type : 'hidden',
6179                     cls: 'form-hidden-field'
6180                 },
6181                 inputblock
6182             ]
6183             
6184         };
6185         
6186         if(this.multiple){
6187             Roo.log('multiple');
6188             
6189             box = {
6190                 tag: 'div',
6191                 cn: [
6192                     {
6193                         tag: 'input',
6194                         type : 'hidden',
6195                         cls: 'form-hidden-field'
6196                     },
6197                     {
6198                         tag: 'ul',
6199                         cls: 'select2-choices',
6200                         cn:[
6201                             {
6202                                 tag: 'li',
6203                                 cls: 'select2-search-field',
6204                                 cn: [
6205
6206                                     inputblock
6207                                 ]
6208                             }
6209                         ]
6210                     }
6211                 ]
6212             }
6213         };
6214         
6215         var combobox = {
6216             cls: 'select2-container input-group',
6217             cn: [
6218                 box,
6219                 {
6220                     tag: 'ul',
6221                     cls: 'typeahead typeahead-long dropdown-menu',
6222                     style: 'display:none'
6223                 }
6224             ]
6225         };
6226         
6227         if(!this.multiple){
6228             combobox.cn.push({
6229                 tag :'span',
6230                 cls : 'input-group-addon btn dropdown-toggle',
6231                 cn : [
6232                     {
6233                         tag: 'span',
6234                         cls: 'caret'
6235                     },
6236                     {
6237                         tag: 'span',
6238                         cls: 'combobox-clear',
6239                         cn  : [
6240                             {
6241                                 tag : 'i',
6242                                 cls: 'icon-remove'
6243                             }
6244                         ]
6245                     }
6246                 ]
6247
6248             })
6249         }
6250         
6251         if(this.multiple){
6252             combobox.cls += ' select2-container-multi';
6253         }
6254         
6255         if (align ==='left' && this.fieldLabel.length) {
6256             
6257                 Roo.log("left and has label");
6258                 cfg.cn = [
6259                     
6260                     {
6261                         tag: 'label',
6262                         'for' :  id,
6263                         cls : 'control-label col-sm-' + this.labelWidth,
6264                         html : this.fieldLabel
6265                         
6266                     },
6267                     {
6268                         cls : "col-sm-" + (12 - this.labelWidth), 
6269                         cn: [
6270                             combobox
6271                         ]
6272                     }
6273                     
6274                 ];
6275         } else if ( this.fieldLabel.length) {
6276                 Roo.log(" label");
6277                  cfg.cn = [
6278                    
6279                     {
6280                         tag: 'label',
6281                         //cls : 'input-group-addon',
6282                         html : this.fieldLabel
6283                         
6284                     },
6285                     
6286                     combobox
6287                     
6288                 ];
6289
6290         } else {
6291             
6292                 Roo.log(" no label && no align");
6293                 cfg = combobox
6294                      
6295                 
6296         }
6297          
6298         var settings=this;
6299         ['xs','sm','md','lg'].map(function(size){
6300             if (settings[size]) {
6301                 cfg.cls += ' col-' + size + '-' + settings[size];
6302             }
6303         });
6304         
6305         return cfg;
6306         
6307     },
6308     
6309     
6310     
6311     // private
6312     onResize : function(w, h){
6313 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6314 //        if(typeof w == 'number'){
6315 //            var x = w - this.trigger.getWidth();
6316 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6317 //            this.trigger.setStyle('left', x+'px');
6318 //        }
6319     },
6320
6321     // private
6322     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6323
6324     // private
6325     getResizeEl : function(){
6326         return this.inputEl();
6327     },
6328
6329     // private
6330     getPositionEl : function(){
6331         return this.inputEl();
6332     },
6333
6334     // private
6335     alignErrorIcon : function(){
6336         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6337     },
6338
6339     // private
6340     initEvents : function(){
6341         
6342         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6343         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6344         if(!this.multiple){
6345             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6346             if(this.hideTrigger){
6347                 this.trigger.setDisplayed(false);
6348             }
6349             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6350         }
6351         
6352         if(this.multiple){
6353             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6354         }
6355         
6356         //this.trigger.addClassOnOver('x-form-trigger-over');
6357         //this.trigger.addClassOnClick('x-form-trigger-click');
6358         
6359         //if(!this.width){
6360         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6361         //}
6362     },
6363
6364     // private
6365     initTrigger : function(){
6366        
6367     },
6368
6369     // private
6370     onDestroy : function(){
6371         if(this.trigger){
6372             this.trigger.removeAllListeners();
6373           //  this.trigger.remove();
6374         }
6375         //if(this.wrap){
6376         //    this.wrap.remove();
6377         //}
6378         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6379     },
6380
6381     // private
6382     onFocus : function(){
6383         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6384         /*
6385         if(!this.mimicing){
6386             this.wrap.addClass('x-trigger-wrap-focus');
6387             this.mimicing = true;
6388             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6389             if(this.monitorTab){
6390                 this.el.on("keydown", this.checkTab, this);
6391             }
6392         }
6393         */
6394     },
6395
6396     // private
6397     checkTab : function(e){
6398         if(e.getKey() == e.TAB){
6399             this.triggerBlur();
6400         }
6401     },
6402
6403     // private
6404     onBlur : function(){
6405         // do nothing
6406     },
6407
6408     // private
6409     mimicBlur : function(e, t){
6410         /*
6411         if(!this.wrap.contains(t) && this.validateBlur()){
6412             this.triggerBlur();
6413         }
6414         */
6415     },
6416
6417     // private
6418     triggerBlur : function(){
6419         this.mimicing = false;
6420         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6421         if(this.monitorTab){
6422             this.el.un("keydown", this.checkTab, this);
6423         }
6424         //this.wrap.removeClass('x-trigger-wrap-focus');
6425         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6426     },
6427
6428     // private
6429     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6430     validateBlur : function(e, t){
6431         return true;
6432     },
6433
6434     // private
6435     onDisable : function(){
6436         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6437         //if(this.wrap){
6438         //    this.wrap.addClass('x-item-disabled');
6439         //}
6440     },
6441
6442     // private
6443     onEnable : function(){
6444         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6445         //if(this.wrap){
6446         //    this.el.removeClass('x-item-disabled');
6447         //}
6448     },
6449
6450     // private
6451     onShow : function(){
6452         var ae = this.getActionEl();
6453         
6454         if(ae){
6455             ae.dom.style.display = '';
6456             ae.dom.style.visibility = 'visible';
6457         }
6458     },
6459
6460     // private
6461     
6462     onHide : function(){
6463         var ae = this.getActionEl();
6464         ae.dom.style.display = 'none';
6465     },
6466
6467     /**
6468      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6469      * by an implementing function.
6470      * @method
6471      * @param {EventObject} e
6472      */
6473     onTriggerClick : Roo.emptyFn
6474 });
6475  /*
6476  * Based on:
6477  * Ext JS Library 1.1.1
6478  * Copyright(c) 2006-2007, Ext JS, LLC.
6479  *
6480  * Originally Released Under LGPL - original licence link has changed is not relivant.
6481  *
6482  * Fork - LGPL
6483  * <script type="text/javascript">
6484  */
6485
6486
6487 /**
6488  * @class Roo.data.SortTypes
6489  * @singleton
6490  * Defines the default sorting (casting?) comparison functions used when sorting data.
6491  */
6492 Roo.data.SortTypes = {
6493     /**
6494      * Default sort that does nothing
6495      * @param {Mixed} s The value being converted
6496      * @return {Mixed} The comparison value
6497      */
6498     none : function(s){
6499         return s;
6500     },
6501     
6502     /**
6503      * The regular expression used to strip tags
6504      * @type {RegExp}
6505      * @property
6506      */
6507     stripTagsRE : /<\/?[^>]+>/gi,
6508     
6509     /**
6510      * Strips all HTML tags to sort on text only
6511      * @param {Mixed} s The value being converted
6512      * @return {String} The comparison value
6513      */
6514     asText : function(s){
6515         return String(s).replace(this.stripTagsRE, "");
6516     },
6517     
6518     /**
6519      * Strips all HTML tags to sort on text only - Case insensitive
6520      * @param {Mixed} s The value being converted
6521      * @return {String} The comparison value
6522      */
6523     asUCText : function(s){
6524         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6525     },
6526     
6527     /**
6528      * Case insensitive string
6529      * @param {Mixed} s The value being converted
6530      * @return {String} The comparison value
6531      */
6532     asUCString : function(s) {
6533         return String(s).toUpperCase();
6534     },
6535     
6536     /**
6537      * Date sorting
6538      * @param {Mixed} s The value being converted
6539      * @return {Number} The comparison value
6540      */
6541     asDate : function(s) {
6542         if(!s){
6543             return 0;
6544         }
6545         if(s instanceof Date){
6546             return s.getTime();
6547         }
6548         return Date.parse(String(s));
6549     },
6550     
6551     /**
6552      * Float sorting
6553      * @param {Mixed} s The value being converted
6554      * @return {Float} The comparison value
6555      */
6556     asFloat : function(s) {
6557         var val = parseFloat(String(s).replace(/,/g, ""));
6558         if(isNaN(val)) val = 0;
6559         return val;
6560     },
6561     
6562     /**
6563      * Integer sorting
6564      * @param {Mixed} s The value being converted
6565      * @return {Number} The comparison value
6566      */
6567     asInt : function(s) {
6568         var val = parseInt(String(s).replace(/,/g, ""));
6569         if(isNaN(val)) val = 0;
6570         return val;
6571     }
6572 };/*
6573  * Based on:
6574  * Ext JS Library 1.1.1
6575  * Copyright(c) 2006-2007, Ext JS, LLC.
6576  *
6577  * Originally Released Under LGPL - original licence link has changed is not relivant.
6578  *
6579  * Fork - LGPL
6580  * <script type="text/javascript">
6581  */
6582
6583 /**
6584 * @class Roo.data.Record
6585  * Instances of this class encapsulate both record <em>definition</em> information, and record
6586  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6587  * to access Records cached in an {@link Roo.data.Store} object.<br>
6588  * <p>
6589  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6590  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6591  * objects.<br>
6592  * <p>
6593  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6594  * @constructor
6595  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6596  * {@link #create}. The parameters are the same.
6597  * @param {Array} data An associative Array of data values keyed by the field name.
6598  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6599  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6600  * not specified an integer id is generated.
6601  */
6602 Roo.data.Record = function(data, id){
6603     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6604     this.data = data;
6605 };
6606
6607 /**
6608  * Generate a constructor for a specific record layout.
6609  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6610  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6611  * Each field definition object may contain the following properties: <ul>
6612  * <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,
6613  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6614  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6615  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6616  * is being used, then this is a string containing the javascript expression to reference the data relative to 
6617  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6618  * to the data item relative to the record element. If the mapping expression is the same as the field name,
6619  * this may be omitted.</p></li>
6620  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6621  * <ul><li>auto (Default, implies no conversion)</li>
6622  * <li>string</li>
6623  * <li>int</li>
6624  * <li>float</li>
6625  * <li>boolean</li>
6626  * <li>date</li></ul></p></li>
6627  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6628  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6629  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6630  * by the Reader into an object that will be stored in the Record. It is passed the
6631  * following parameters:<ul>
6632  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6633  * </ul></p></li>
6634  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6635  * </ul>
6636  * <br>usage:<br><pre><code>
6637 var TopicRecord = Roo.data.Record.create(
6638     {name: 'title', mapping: 'topic_title'},
6639     {name: 'author', mapping: 'username'},
6640     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6641     {name: 'lastPost', mapping: 'post_time', type: 'date'},
6642     {name: 'lastPoster', mapping: 'user2'},
6643     {name: 'excerpt', mapping: 'post_text'}
6644 );
6645
6646 var myNewRecord = new TopicRecord({
6647     title: 'Do my job please',
6648     author: 'noobie',
6649     totalPosts: 1,
6650     lastPost: new Date(),
6651     lastPoster: 'Animal',
6652     excerpt: 'No way dude!'
6653 });
6654 myStore.add(myNewRecord);
6655 </code></pre>
6656  * @method create
6657  * @static
6658  */
6659 Roo.data.Record.create = function(o){
6660     var f = function(){
6661         f.superclass.constructor.apply(this, arguments);
6662     };
6663     Roo.extend(f, Roo.data.Record);
6664     var p = f.prototype;
6665     p.fields = new Roo.util.MixedCollection(false, function(field){
6666         return field.name;
6667     });
6668     for(var i = 0, len = o.length; i < len; i++){
6669         p.fields.add(new Roo.data.Field(o[i]));
6670     }
6671     f.getField = function(name){
6672         return p.fields.get(name);  
6673     };
6674     return f;
6675 };
6676
6677 Roo.data.Record.AUTO_ID = 1000;
6678 Roo.data.Record.EDIT = 'edit';
6679 Roo.data.Record.REJECT = 'reject';
6680 Roo.data.Record.COMMIT = 'commit';
6681
6682 Roo.data.Record.prototype = {
6683     /**
6684      * Readonly flag - true if this record has been modified.
6685      * @type Boolean
6686      */
6687     dirty : false,
6688     editing : false,
6689     error: null,
6690     modified: null,
6691
6692     // private
6693     join : function(store){
6694         this.store = store;
6695     },
6696
6697     /**
6698      * Set the named field to the specified value.
6699      * @param {String} name The name of the field to set.
6700      * @param {Object} value The value to set the field to.
6701      */
6702     set : function(name, value){
6703         if(this.data[name] == value){
6704             return;
6705         }
6706         this.dirty = true;
6707         if(!this.modified){
6708             this.modified = {};
6709         }
6710         if(typeof this.modified[name] == 'undefined'){
6711             this.modified[name] = this.data[name];
6712         }
6713         this.data[name] = value;
6714         if(!this.editing && this.store){
6715             this.store.afterEdit(this);
6716         }       
6717     },
6718
6719     /**
6720      * Get the value of the named field.
6721      * @param {String} name The name of the field to get the value of.
6722      * @return {Object} The value of the field.
6723      */
6724     get : function(name){
6725         return this.data[name]; 
6726     },
6727
6728     // private
6729     beginEdit : function(){
6730         this.editing = true;
6731         this.modified = {}; 
6732     },
6733
6734     // private
6735     cancelEdit : function(){
6736         this.editing = false;
6737         delete this.modified;
6738     },
6739
6740     // private
6741     endEdit : function(){
6742         this.editing = false;
6743         if(this.dirty && this.store){
6744             this.store.afterEdit(this);
6745         }
6746     },
6747
6748     /**
6749      * Usually called by the {@link Roo.data.Store} which owns the Record.
6750      * Rejects all changes made to the Record since either creation, or the last commit operation.
6751      * Modified fields are reverted to their original values.
6752      * <p>
6753      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6754      * of reject operations.
6755      */
6756     reject : function(){
6757         var m = this.modified;
6758         for(var n in m){
6759             if(typeof m[n] != "function"){
6760                 this.data[n] = m[n];
6761             }
6762         }
6763         this.dirty = false;
6764         delete this.modified;
6765         this.editing = false;
6766         if(this.store){
6767             this.store.afterReject(this);
6768         }
6769     },
6770
6771     /**
6772      * Usually called by the {@link Roo.data.Store} which owns the Record.
6773      * Commits all changes made to the Record since either creation, or the last commit operation.
6774      * <p>
6775      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6776      * of commit operations.
6777      */
6778     commit : function(){
6779         this.dirty = false;
6780         delete this.modified;
6781         this.editing = false;
6782         if(this.store){
6783             this.store.afterCommit(this);
6784         }
6785     },
6786
6787     // private
6788     hasError : function(){
6789         return this.error != null;
6790     },
6791
6792     // private
6793     clearError : function(){
6794         this.error = null;
6795     },
6796
6797     /**
6798      * Creates a copy of this record.
6799      * @param {String} id (optional) A new record id if you don't want to use this record's id
6800      * @return {Record}
6801      */
6802     copy : function(newId) {
6803         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6804     }
6805 };/*
6806  * Based on:
6807  * Ext JS Library 1.1.1
6808  * Copyright(c) 2006-2007, Ext JS, LLC.
6809  *
6810  * Originally Released Under LGPL - original licence link has changed is not relivant.
6811  *
6812  * Fork - LGPL
6813  * <script type="text/javascript">
6814  */
6815
6816
6817
6818 /**
6819  * @class Roo.data.Store
6820  * @extends Roo.util.Observable
6821  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6822  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6823  * <p>
6824  * 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
6825  * has no knowledge of the format of the data returned by the Proxy.<br>
6826  * <p>
6827  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6828  * instances from the data object. These records are cached and made available through accessor functions.
6829  * @constructor
6830  * Creates a new Store.
6831  * @param {Object} config A config object containing the objects needed for the Store to access data,
6832  * and read the data into Records.
6833  */
6834 Roo.data.Store = function(config){
6835     this.data = new Roo.util.MixedCollection(false);
6836     this.data.getKey = function(o){
6837         return o.id;
6838     };
6839     this.baseParams = {};
6840     // private
6841     this.paramNames = {
6842         "start" : "start",
6843         "limit" : "limit",
6844         "sort" : "sort",
6845         "dir" : "dir",
6846         "multisort" : "_multisort"
6847     };
6848
6849     if(config && config.data){
6850         this.inlineData = config.data;
6851         delete config.data;
6852     }
6853
6854     Roo.apply(this, config);
6855     
6856     if(this.reader){ // reader passed
6857         this.reader = Roo.factory(this.reader, Roo.data);
6858         this.reader.xmodule = this.xmodule || false;
6859         if(!this.recordType){
6860             this.recordType = this.reader.recordType;
6861         }
6862         if(this.reader.onMetaChange){
6863             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6864         }
6865     }
6866
6867     if(this.recordType){
6868         this.fields = this.recordType.prototype.fields;
6869     }
6870     this.modified = [];
6871
6872     this.addEvents({
6873         /**
6874          * @event datachanged
6875          * Fires when the data cache has changed, and a widget which is using this Store
6876          * as a Record cache should refresh its view.
6877          * @param {Store} this
6878          */
6879         datachanged : true,
6880         /**
6881          * @event metachange
6882          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6883          * @param {Store} this
6884          * @param {Object} meta The JSON metadata
6885          */
6886         metachange : true,
6887         /**
6888          * @event add
6889          * Fires when Records have been added to the Store
6890          * @param {Store} this
6891          * @param {Roo.data.Record[]} records The array of Records added
6892          * @param {Number} index The index at which the record(s) were added
6893          */
6894         add : true,
6895         /**
6896          * @event remove
6897          * Fires when a Record has been removed from the Store
6898          * @param {Store} this
6899          * @param {Roo.data.Record} record The Record that was removed
6900          * @param {Number} index The index at which the record was removed
6901          */
6902         remove : true,
6903         /**
6904          * @event update
6905          * Fires when a Record has been updated
6906          * @param {Store} this
6907          * @param {Roo.data.Record} record The Record that was updated
6908          * @param {String} operation The update operation being performed.  Value may be one of:
6909          * <pre><code>
6910  Roo.data.Record.EDIT
6911  Roo.data.Record.REJECT
6912  Roo.data.Record.COMMIT
6913          * </code></pre>
6914          */
6915         update : true,
6916         /**
6917          * @event clear
6918          * Fires when the data cache has been cleared.
6919          * @param {Store} this
6920          */
6921         clear : true,
6922         /**
6923          * @event beforeload
6924          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6925          * the load action will be canceled.
6926          * @param {Store} this
6927          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6928          */
6929         beforeload : true,
6930         /**
6931          * @event beforeloadadd
6932          * Fires after a new set of Records has been loaded.
6933          * @param {Store} this
6934          * @param {Roo.data.Record[]} records The Records that were loaded
6935          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6936          */
6937         beforeloadadd : true,
6938         /**
6939          * @event load
6940          * Fires after a new set of Records has been loaded, before they are added to the store.
6941          * @param {Store} this
6942          * @param {Roo.data.Record[]} records The Records that were loaded
6943          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6944          * @params {Object} return from reader
6945          */
6946         load : true,
6947         /**
6948          * @event loadexception
6949          * Fires if an exception occurs in the Proxy during loading.
6950          * Called with the signature of the Proxy's "loadexception" event.
6951          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6952          * 
6953          * @param {Proxy} 
6954          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6955          * @param {Object} load options 
6956          * @param {Object} jsonData from your request (normally this contains the Exception)
6957          */
6958         loadexception : true
6959     });
6960     
6961     if(this.proxy){
6962         this.proxy = Roo.factory(this.proxy, Roo.data);
6963         this.proxy.xmodule = this.xmodule || false;
6964         this.relayEvents(this.proxy,  ["loadexception"]);
6965     }
6966     this.sortToggle = {};
6967     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6968
6969     Roo.data.Store.superclass.constructor.call(this);
6970
6971     if(this.inlineData){
6972         this.loadData(this.inlineData);
6973         delete this.inlineData;
6974     }
6975 };
6976
6977 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6978      /**
6979     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6980     * without a remote query - used by combo/forms at present.
6981     */
6982     
6983     /**
6984     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6985     */
6986     /**
6987     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6988     */
6989     /**
6990     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6991     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6992     */
6993     /**
6994     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6995     * on any HTTP request
6996     */
6997     /**
6998     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6999     */
7000     /**
7001     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7002     */
7003     multiSort: false,
7004     /**
7005     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7006     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7007     */
7008     remoteSort : false,
7009
7010     /**
7011     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7012      * loaded or when a record is removed. (defaults to false).
7013     */
7014     pruneModifiedRecords : false,
7015
7016     // private
7017     lastOptions : null,
7018
7019     /**
7020      * Add Records to the Store and fires the add event.
7021      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7022      */
7023     add : function(records){
7024         records = [].concat(records);
7025         for(var i = 0, len = records.length; i < len; i++){
7026             records[i].join(this);
7027         }
7028         var index = this.data.length;
7029         this.data.addAll(records);
7030         this.fireEvent("add", this, records, index);
7031     },
7032
7033     /**
7034      * Remove a Record from the Store and fires the remove event.
7035      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7036      */
7037     remove : function(record){
7038         var index = this.data.indexOf(record);
7039         this.data.removeAt(index);
7040         if(this.pruneModifiedRecords){
7041             this.modified.remove(record);
7042         }
7043         this.fireEvent("remove", this, record, index);
7044     },
7045
7046     /**
7047      * Remove all Records from the Store and fires the clear event.
7048      */
7049     removeAll : function(){
7050         this.data.clear();
7051         if(this.pruneModifiedRecords){
7052             this.modified = [];
7053         }
7054         this.fireEvent("clear", this);
7055     },
7056
7057     /**
7058      * Inserts Records to the Store at the given index and fires the add event.
7059      * @param {Number} index The start index at which to insert the passed Records.
7060      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7061      */
7062     insert : function(index, records){
7063         records = [].concat(records);
7064         for(var i = 0, len = records.length; i < len; i++){
7065             this.data.insert(index, records[i]);
7066             records[i].join(this);
7067         }
7068         this.fireEvent("add", this, records, index);
7069     },
7070
7071     /**
7072      * Get the index within the cache of the passed Record.
7073      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7074      * @return {Number} The index of the passed Record. Returns -1 if not found.
7075      */
7076     indexOf : function(record){
7077         return this.data.indexOf(record);
7078     },
7079
7080     /**
7081      * Get the index within the cache of the Record with the passed id.
7082      * @param {String} id The id of the Record to find.
7083      * @return {Number} The index of the Record. Returns -1 if not found.
7084      */
7085     indexOfId : function(id){
7086         return this.data.indexOfKey(id);
7087     },
7088
7089     /**
7090      * Get the Record with the specified id.
7091      * @param {String} id The id of the Record to find.
7092      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7093      */
7094     getById : function(id){
7095         return this.data.key(id);
7096     },
7097
7098     /**
7099      * Get the Record at the specified index.
7100      * @param {Number} index The index of the Record to find.
7101      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7102      */
7103     getAt : function(index){
7104         return this.data.itemAt(index);
7105     },
7106
7107     /**
7108      * Returns a range of Records between specified indices.
7109      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7110      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7111      * @return {Roo.data.Record[]} An array of Records
7112      */
7113     getRange : function(start, end){
7114         return this.data.getRange(start, end);
7115     },
7116
7117     // private
7118     storeOptions : function(o){
7119         o = Roo.apply({}, o);
7120         delete o.callback;
7121         delete o.scope;
7122         this.lastOptions = o;
7123     },
7124
7125     /**
7126      * Loads the Record cache from the configured Proxy using the configured Reader.
7127      * <p>
7128      * If using remote paging, then the first load call must specify the <em>start</em>
7129      * and <em>limit</em> properties in the options.params property to establish the initial
7130      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7131      * <p>
7132      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7133      * and this call will return before the new data has been loaded. Perform any post-processing
7134      * in a callback function, or in a "load" event handler.</strong>
7135      * <p>
7136      * @param {Object} options An object containing properties which control loading options:<ul>
7137      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7138      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7139      * passed the following arguments:<ul>
7140      * <li>r : Roo.data.Record[]</li>
7141      * <li>options: Options object from the load call</li>
7142      * <li>success: Boolean success indicator</li></ul></li>
7143      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7144      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7145      * </ul>
7146      */
7147     load : function(options){
7148         options = options || {};
7149         if(this.fireEvent("beforeload", this, options) !== false){
7150             this.storeOptions(options);
7151             var p = Roo.apply(options.params || {}, this.baseParams);
7152             // if meta was not loaded from remote source.. try requesting it.
7153             if (!this.reader.metaFromRemote) {
7154                 p._requestMeta = 1;
7155             }
7156             if(this.sortInfo && this.remoteSort){
7157                 var pn = this.paramNames;
7158                 p[pn["sort"]] = this.sortInfo.field;
7159                 p[pn["dir"]] = this.sortInfo.direction;
7160             }
7161             if (this.multiSort) {
7162                 var pn = this.paramNames;
7163                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7164             }
7165             
7166             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7167         }
7168     },
7169
7170     /**
7171      * Reloads the Record cache from the configured Proxy using the configured Reader and
7172      * the options from the last load operation performed.
7173      * @param {Object} options (optional) An object containing properties which may override the options
7174      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7175      * the most recently used options are reused).
7176      */
7177     reload : function(options){
7178         this.load(Roo.applyIf(options||{}, this.lastOptions));
7179     },
7180
7181     // private
7182     // Called as a callback by the Reader during a load operation.
7183     loadRecords : function(o, options, success){
7184         if(!o || success === false){
7185             if(success !== false){
7186                 this.fireEvent("load", this, [], options, o);
7187             }
7188             if(options.callback){
7189                 options.callback.call(options.scope || this, [], options, false);
7190             }
7191             return;
7192         }
7193         // if data returned failure - throw an exception.
7194         if (o.success === false) {
7195             // show a message if no listener is registered.
7196             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7197                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7198             }
7199             // loadmask wil be hooked into this..
7200             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7201             return;
7202         }
7203         var r = o.records, t = o.totalRecords || r.length;
7204         
7205         this.fireEvent("beforeloadadd", this, r, options, o);
7206         
7207         if(!options || options.add !== true){
7208             if(this.pruneModifiedRecords){
7209                 this.modified = [];
7210             }
7211             for(var i = 0, len = r.length; i < len; i++){
7212                 r[i].join(this);
7213             }
7214             if(this.snapshot){
7215                 this.data = this.snapshot;
7216                 delete this.snapshot;
7217             }
7218             this.data.clear();
7219             this.data.addAll(r);
7220             this.totalLength = t;
7221             this.applySort();
7222             this.fireEvent("datachanged", this);
7223         }else{
7224             this.totalLength = Math.max(t, this.data.length+r.length);
7225             this.add(r);
7226         }
7227         this.fireEvent("load", this, r, options, o);
7228         if(options.callback){
7229             options.callback.call(options.scope || this, r, options, true);
7230         }
7231     },
7232
7233
7234     /**
7235      * Loads data from a passed data block. A Reader which understands the format of the data
7236      * must have been configured in the constructor.
7237      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7238      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7239      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7240      */
7241     loadData : function(o, append){
7242         var r = this.reader.readRecords(o);
7243         this.loadRecords(r, {add: append}, true);
7244     },
7245
7246     /**
7247      * Gets the number of cached records.
7248      * <p>
7249      * <em>If using paging, this may not be the total size of the dataset. If the data object
7250      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7251      * the data set size</em>
7252      */
7253     getCount : function(){
7254         return this.data.length || 0;
7255     },
7256
7257     /**
7258      * Gets the total number of records in the dataset as returned by the server.
7259      * <p>
7260      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7261      * the dataset size</em>
7262      */
7263     getTotalCount : function(){
7264         return this.totalLength || 0;
7265     },
7266
7267     /**
7268      * Returns the sort state of the Store as an object with two properties:
7269      * <pre><code>
7270  field {String} The name of the field by which the Records are sorted
7271  direction {String} The sort order, "ASC" or "DESC"
7272      * </code></pre>
7273      */
7274     getSortState : function(){
7275         return this.sortInfo;
7276     },
7277
7278     // private
7279     applySort : function(){
7280         if(this.sortInfo && !this.remoteSort){
7281             var s = this.sortInfo, f = s.field;
7282             var st = this.fields.get(f).sortType;
7283             var fn = function(r1, r2){
7284                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7285                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7286             };
7287             this.data.sort(s.direction, fn);
7288             if(this.snapshot && this.snapshot != this.data){
7289                 this.snapshot.sort(s.direction, fn);
7290             }
7291         }
7292     },
7293
7294     /**
7295      * Sets the default sort column and order to be used by the next load operation.
7296      * @param {String} fieldName The name of the field to sort by.
7297      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7298      */
7299     setDefaultSort : function(field, dir){
7300         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7301     },
7302
7303     /**
7304      * Sort the Records.
7305      * If remote sorting is used, the sort is performed on the server, and the cache is
7306      * reloaded. If local sorting is used, the cache is sorted internally.
7307      * @param {String} fieldName The name of the field to sort by.
7308      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7309      */
7310     sort : function(fieldName, dir){
7311         var f = this.fields.get(fieldName);
7312         if(!dir){
7313             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7314             
7315             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7316                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7317             }else{
7318                 dir = f.sortDir;
7319             }
7320         }
7321         this.sortToggle[f.name] = dir;
7322         this.sortInfo = {field: f.name, direction: dir};
7323         if(!this.remoteSort){
7324             this.applySort();
7325             this.fireEvent("datachanged", this);
7326         }else{
7327             this.load(this.lastOptions);
7328         }
7329     },
7330
7331     /**
7332      * Calls the specified function for each of the Records in the cache.
7333      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7334      * Returning <em>false</em> aborts and exits the iteration.
7335      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7336      */
7337     each : function(fn, scope){
7338         this.data.each(fn, scope);
7339     },
7340
7341     /**
7342      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7343      * (e.g., during paging).
7344      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7345      */
7346     getModifiedRecords : function(){
7347         return this.modified;
7348     },
7349
7350     // private
7351     createFilterFn : function(property, value, anyMatch){
7352         if(!value.exec){ // not a regex
7353             value = String(value);
7354             if(value.length == 0){
7355                 return false;
7356             }
7357             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7358         }
7359         return function(r){
7360             return value.test(r.data[property]);
7361         };
7362     },
7363
7364     /**
7365      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7366      * @param {String} property A field on your records
7367      * @param {Number} start The record index to start at (defaults to 0)
7368      * @param {Number} end The last record index to include (defaults to length - 1)
7369      * @return {Number} The sum
7370      */
7371     sum : function(property, start, end){
7372         var rs = this.data.items, v = 0;
7373         start = start || 0;
7374         end = (end || end === 0) ? end : rs.length-1;
7375
7376         for(var i = start; i <= end; i++){
7377             v += (rs[i].data[property] || 0);
7378         }
7379         return v;
7380     },
7381
7382     /**
7383      * Filter the records by a specified property.
7384      * @param {String} field A field on your records
7385      * @param {String/RegExp} value Either a string that the field
7386      * should start with or a RegExp to test against the field
7387      * @param {Boolean} anyMatch True to match any part not just the beginning
7388      */
7389     filter : function(property, value, anyMatch){
7390         var fn = this.createFilterFn(property, value, anyMatch);
7391         return fn ? this.filterBy(fn) : this.clearFilter();
7392     },
7393
7394     /**
7395      * Filter by a function. The specified function will be called with each
7396      * record in this data source. If the function returns true the record is included,
7397      * otherwise it is filtered.
7398      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7399      * @param {Object} scope (optional) The scope of the function (defaults to this)
7400      */
7401     filterBy : function(fn, scope){
7402         this.snapshot = this.snapshot || this.data;
7403         this.data = this.queryBy(fn, scope||this);
7404         this.fireEvent("datachanged", this);
7405     },
7406
7407     /**
7408      * Query the records by a specified property.
7409      * @param {String} field A field on your records
7410      * @param {String/RegExp} value Either a string that the field
7411      * should start with or a RegExp to test against the field
7412      * @param {Boolean} anyMatch True to match any part not just the beginning
7413      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7414      */
7415     query : function(property, value, anyMatch){
7416         var fn = this.createFilterFn(property, value, anyMatch);
7417         return fn ? this.queryBy(fn) : this.data.clone();
7418     },
7419
7420     /**
7421      * Query by a function. The specified function will be called with each
7422      * record in this data source. If the function returns true the record is included
7423      * in the results.
7424      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7425      * @param {Object} scope (optional) The scope of the function (defaults to this)
7426       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7427      **/
7428     queryBy : function(fn, scope){
7429         var data = this.snapshot || this.data;
7430         return data.filterBy(fn, scope||this);
7431     },
7432
7433     /**
7434      * Collects unique values for a particular dataIndex from this store.
7435      * @param {String} dataIndex The property to collect
7436      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7437      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7438      * @return {Array} An array of the unique values
7439      **/
7440     collect : function(dataIndex, allowNull, bypassFilter){
7441         var d = (bypassFilter === true && this.snapshot) ?
7442                 this.snapshot.items : this.data.items;
7443         var v, sv, r = [], l = {};
7444         for(var i = 0, len = d.length; i < len; i++){
7445             v = d[i].data[dataIndex];
7446             sv = String(v);
7447             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7448                 l[sv] = true;
7449                 r[r.length] = v;
7450             }
7451         }
7452         return r;
7453     },
7454
7455     /**
7456      * Revert to a view of the Record cache with no filtering applied.
7457      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7458      */
7459     clearFilter : function(suppressEvent){
7460         if(this.snapshot && this.snapshot != this.data){
7461             this.data = this.snapshot;
7462             delete this.snapshot;
7463             if(suppressEvent !== true){
7464                 this.fireEvent("datachanged", this);
7465             }
7466         }
7467     },
7468
7469     // private
7470     afterEdit : function(record){
7471         if(this.modified.indexOf(record) == -1){
7472             this.modified.push(record);
7473         }
7474         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7475     },
7476     
7477     // private
7478     afterReject : function(record){
7479         this.modified.remove(record);
7480         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7481     },
7482
7483     // private
7484     afterCommit : function(record){
7485         this.modified.remove(record);
7486         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7487     },
7488
7489     /**
7490      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7491      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7492      */
7493     commitChanges : function(){
7494         var m = this.modified.slice(0);
7495         this.modified = [];
7496         for(var i = 0, len = m.length; i < len; i++){
7497             m[i].commit();
7498         }
7499     },
7500
7501     /**
7502      * Cancel outstanding changes on all changed records.
7503      */
7504     rejectChanges : function(){
7505         var m = this.modified.slice(0);
7506         this.modified = [];
7507         for(var i = 0, len = m.length; i < len; i++){
7508             m[i].reject();
7509         }
7510     },
7511
7512     onMetaChange : function(meta, rtype, o){
7513         this.recordType = rtype;
7514         this.fields = rtype.prototype.fields;
7515         delete this.snapshot;
7516         this.sortInfo = meta.sortInfo || this.sortInfo;
7517         this.modified = [];
7518         this.fireEvent('metachange', this, this.reader.meta);
7519     },
7520     
7521     moveIndex : function(data, type)
7522     {
7523         var index = this.indexOf(data);
7524         
7525         var newIndex = index + type;
7526         
7527         this.remove(data);
7528         
7529         this.insert(newIndex, data);
7530         
7531     }
7532 });/*
7533  * Based on:
7534  * Ext JS Library 1.1.1
7535  * Copyright(c) 2006-2007, Ext JS, LLC.
7536  *
7537  * Originally Released Under LGPL - original licence link has changed is not relivant.
7538  *
7539  * Fork - LGPL
7540  * <script type="text/javascript">
7541  */
7542
7543 /**
7544  * @class Roo.data.SimpleStore
7545  * @extends Roo.data.Store
7546  * Small helper class to make creating Stores from Array data easier.
7547  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7548  * @cfg {Array} fields An array of field definition objects, or field name strings.
7549  * @cfg {Array} data The multi-dimensional array of data
7550  * @constructor
7551  * @param {Object} config
7552  */
7553 Roo.data.SimpleStore = function(config){
7554     Roo.data.SimpleStore.superclass.constructor.call(this, {
7555         isLocal : true,
7556         reader: new Roo.data.ArrayReader({
7557                 id: config.id
7558             },
7559             Roo.data.Record.create(config.fields)
7560         ),
7561         proxy : new Roo.data.MemoryProxy(config.data)
7562     });
7563     this.load();
7564 };
7565 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7566  * Based on:
7567  * Ext JS Library 1.1.1
7568  * Copyright(c) 2006-2007, Ext JS, LLC.
7569  *
7570  * Originally Released Under LGPL - original licence link has changed is not relivant.
7571  *
7572  * Fork - LGPL
7573  * <script type="text/javascript">
7574  */
7575
7576 /**
7577 /**
7578  * @extends Roo.data.Store
7579  * @class Roo.data.JsonStore
7580  * Small helper class to make creating Stores for JSON data easier. <br/>
7581 <pre><code>
7582 var store = new Roo.data.JsonStore({
7583     url: 'get-images.php',
7584     root: 'images',
7585     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7586 });
7587 </code></pre>
7588  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7589  * JsonReader and HttpProxy (unless inline data is provided).</b>
7590  * @cfg {Array} fields An array of field definition objects, or field name strings.
7591  * @constructor
7592  * @param {Object} config
7593  */
7594 Roo.data.JsonStore = function(c){
7595     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7596         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7597         reader: new Roo.data.JsonReader(c, c.fields)
7598     }));
7599 };
7600 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7601  * Based on:
7602  * Ext JS Library 1.1.1
7603  * Copyright(c) 2006-2007, Ext JS, LLC.
7604  *
7605  * Originally Released Under LGPL - original licence link has changed is not relivant.
7606  *
7607  * Fork - LGPL
7608  * <script type="text/javascript">
7609  */
7610
7611  
7612 Roo.data.Field = function(config){
7613     if(typeof config == "string"){
7614         config = {name: config};
7615     }
7616     Roo.apply(this, config);
7617     
7618     if(!this.type){
7619         this.type = "auto";
7620     }
7621     
7622     var st = Roo.data.SortTypes;
7623     // named sortTypes are supported, here we look them up
7624     if(typeof this.sortType == "string"){
7625         this.sortType = st[this.sortType];
7626     }
7627     
7628     // set default sortType for strings and dates
7629     if(!this.sortType){
7630         switch(this.type){
7631             case "string":
7632                 this.sortType = st.asUCString;
7633                 break;
7634             case "date":
7635                 this.sortType = st.asDate;
7636                 break;
7637             default:
7638                 this.sortType = st.none;
7639         }
7640     }
7641
7642     // define once
7643     var stripRe = /[\$,%]/g;
7644
7645     // prebuilt conversion function for this field, instead of
7646     // switching every time we're reading a value
7647     if(!this.convert){
7648         var cv, dateFormat = this.dateFormat;
7649         switch(this.type){
7650             case "":
7651             case "auto":
7652             case undefined:
7653                 cv = function(v){ return v; };
7654                 break;
7655             case "string":
7656                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7657                 break;
7658             case "int":
7659                 cv = function(v){
7660                     return v !== undefined && v !== null && v !== '' ?
7661                            parseInt(String(v).replace(stripRe, ""), 10) : '';
7662                     };
7663                 break;
7664             case "float":
7665                 cv = function(v){
7666                     return v !== undefined && v !== null && v !== '' ?
7667                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
7668                     };
7669                 break;
7670             case "bool":
7671             case "boolean":
7672                 cv = function(v){ return v === true || v === "true" || v == 1; };
7673                 break;
7674             case "date":
7675                 cv = function(v){
7676                     if(!v){
7677                         return '';
7678                     }
7679                     if(v instanceof Date){
7680                         return v;
7681                     }
7682                     if(dateFormat){
7683                         if(dateFormat == "timestamp"){
7684                             return new Date(v*1000);
7685                         }
7686                         return Date.parseDate(v, dateFormat);
7687                     }
7688                     var parsed = Date.parse(v);
7689                     return parsed ? new Date(parsed) : null;
7690                 };
7691              break;
7692             
7693         }
7694         this.convert = cv;
7695     }
7696 };
7697
7698 Roo.data.Field.prototype = {
7699     dateFormat: null,
7700     defaultValue: "",
7701     mapping: null,
7702     sortType : null,
7703     sortDir : "ASC"
7704 };/*
7705  * Based on:
7706  * Ext JS Library 1.1.1
7707  * Copyright(c) 2006-2007, Ext JS, LLC.
7708  *
7709  * Originally Released Under LGPL - original licence link has changed is not relivant.
7710  *
7711  * Fork - LGPL
7712  * <script type="text/javascript">
7713  */
7714  
7715 // Base class for reading structured data from a data source.  This class is intended to be
7716 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7717
7718 /**
7719  * @class Roo.data.DataReader
7720  * Base class for reading structured data from a data source.  This class is intended to be
7721  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7722  */
7723
7724 Roo.data.DataReader = function(meta, recordType){
7725     
7726     this.meta = meta;
7727     
7728     this.recordType = recordType instanceof Array ? 
7729         Roo.data.Record.create(recordType) : recordType;
7730 };
7731
7732 Roo.data.DataReader.prototype = {
7733      /**
7734      * Create an empty record
7735      * @param {Object} data (optional) - overlay some values
7736      * @return {Roo.data.Record} record created.
7737      */
7738     newRow :  function(d) {
7739         var da =  {};
7740         this.recordType.prototype.fields.each(function(c) {
7741             switch( c.type) {
7742                 case 'int' : da[c.name] = 0; break;
7743                 case 'date' : da[c.name] = new Date(); break;
7744                 case 'float' : da[c.name] = 0.0; break;
7745                 case 'boolean' : da[c.name] = false; break;
7746                 default : da[c.name] = ""; break;
7747             }
7748             
7749         });
7750         return new this.recordType(Roo.apply(da, d));
7751     }
7752     
7753 };/*
7754  * Based on:
7755  * Ext JS Library 1.1.1
7756  * Copyright(c) 2006-2007, Ext JS, LLC.
7757  *
7758  * Originally Released Under LGPL - original licence link has changed is not relivant.
7759  *
7760  * Fork - LGPL
7761  * <script type="text/javascript">
7762  */
7763
7764 /**
7765  * @class Roo.data.DataProxy
7766  * @extends Roo.data.Observable
7767  * This class is an abstract base class for implementations which provide retrieval of
7768  * unformatted data objects.<br>
7769  * <p>
7770  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7771  * (of the appropriate type which knows how to parse the data object) to provide a block of
7772  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7773  * <p>
7774  * Custom implementations must implement the load method as described in
7775  * {@link Roo.data.HttpProxy#load}.
7776  */
7777 Roo.data.DataProxy = function(){
7778     this.addEvents({
7779         /**
7780          * @event beforeload
7781          * Fires before a network request is made to retrieve a data object.
7782          * @param {Object} This DataProxy object.
7783          * @param {Object} params The params parameter to the load function.
7784          */
7785         beforeload : true,
7786         /**
7787          * @event load
7788          * Fires before the load method's callback is called.
7789          * @param {Object} This DataProxy object.
7790          * @param {Object} o The data object.
7791          * @param {Object} arg The callback argument object passed to the load function.
7792          */
7793         load : true,
7794         /**
7795          * @event loadexception
7796          * Fires if an Exception occurs during data retrieval.
7797          * @param {Object} This DataProxy object.
7798          * @param {Object} o The data object.
7799          * @param {Object} arg The callback argument object passed to the load function.
7800          * @param {Object} e The Exception.
7801          */
7802         loadexception : true
7803     });
7804     Roo.data.DataProxy.superclass.constructor.call(this);
7805 };
7806
7807 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7808
7809     /**
7810      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7811      */
7812 /*
7813  * Based on:
7814  * Ext JS Library 1.1.1
7815  * Copyright(c) 2006-2007, Ext JS, LLC.
7816  *
7817  * Originally Released Under LGPL - original licence link has changed is not relivant.
7818  *
7819  * Fork - LGPL
7820  * <script type="text/javascript">
7821  */
7822 /**
7823  * @class Roo.data.MemoryProxy
7824  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7825  * to the Reader when its load method is called.
7826  * @constructor
7827  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7828  */
7829 Roo.data.MemoryProxy = function(data){
7830     if (data.data) {
7831         data = data.data;
7832     }
7833     Roo.data.MemoryProxy.superclass.constructor.call(this);
7834     this.data = data;
7835 };
7836
7837 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7838     /**
7839      * Load data from the requested source (in this case an in-memory
7840      * data object passed to the constructor), read the data object into
7841      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7842      * process that block using the passed callback.
7843      * @param {Object} params This parameter is not used by the MemoryProxy class.
7844      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7845      * object into a block of Roo.data.Records.
7846      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7847      * The function must be passed <ul>
7848      * <li>The Record block object</li>
7849      * <li>The "arg" argument from the load function</li>
7850      * <li>A boolean success indicator</li>
7851      * </ul>
7852      * @param {Object} scope The scope in which to call the callback
7853      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7854      */
7855     load : function(params, reader, callback, scope, arg){
7856         params = params || {};
7857         var result;
7858         try {
7859             result = reader.readRecords(this.data);
7860         }catch(e){
7861             this.fireEvent("loadexception", this, arg, null, e);
7862             callback.call(scope, null, arg, false);
7863             return;
7864         }
7865         callback.call(scope, result, arg, true);
7866     },
7867     
7868     // private
7869     update : function(params, records){
7870         
7871     }
7872 });/*
7873  * Based on:
7874  * Ext JS Library 1.1.1
7875  * Copyright(c) 2006-2007, Ext JS, LLC.
7876  *
7877  * Originally Released Under LGPL - original licence link has changed is not relivant.
7878  *
7879  * Fork - LGPL
7880  * <script type="text/javascript">
7881  */
7882 /**
7883  * @class Roo.data.HttpProxy
7884  * @extends Roo.data.DataProxy
7885  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7886  * configured to reference a certain URL.<br><br>
7887  * <p>
7888  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7889  * from which the running page was served.<br><br>
7890  * <p>
7891  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7892  * <p>
7893  * Be aware that to enable the browser to parse an XML document, the server must set
7894  * the Content-Type header in the HTTP response to "text/xml".
7895  * @constructor
7896  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7897  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7898  * will be used to make the request.
7899  */
7900 Roo.data.HttpProxy = function(conn){
7901     Roo.data.HttpProxy.superclass.constructor.call(this);
7902     // is conn a conn config or a real conn?
7903     this.conn = conn;
7904     this.useAjax = !conn || !conn.events;
7905   
7906 };
7907
7908 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7909     // thse are take from connection...
7910     
7911     /**
7912      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7913      */
7914     /**
7915      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7916      * extra parameters to each request made by this object. (defaults to undefined)
7917      */
7918     /**
7919      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7920      *  to each request made by this object. (defaults to undefined)
7921      */
7922     /**
7923      * @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)
7924      */
7925     /**
7926      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7927      */
7928      /**
7929      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7930      * @type Boolean
7931      */
7932   
7933
7934     /**
7935      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7936      * @type Boolean
7937      */
7938     /**
7939      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7940      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7941      * a finer-grained basis than the DataProxy events.
7942      */
7943     getConnection : function(){
7944         return this.useAjax ? Roo.Ajax : this.conn;
7945     },
7946
7947     /**
7948      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7949      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7950      * process that block using the passed callback.
7951      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7952      * for the request to the remote server.
7953      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7954      * object into a block of Roo.data.Records.
7955      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7956      * The function must be passed <ul>
7957      * <li>The Record block object</li>
7958      * <li>The "arg" argument from the load function</li>
7959      * <li>A boolean success indicator</li>
7960      * </ul>
7961      * @param {Object} scope The scope in which to call the callback
7962      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7963      */
7964     load : function(params, reader, callback, scope, arg){
7965         if(this.fireEvent("beforeload", this, params) !== false){
7966             var  o = {
7967                 params : params || {},
7968                 request: {
7969                     callback : callback,
7970                     scope : scope,
7971                     arg : arg
7972                 },
7973                 reader: reader,
7974                 callback : this.loadResponse,
7975                 scope: this
7976             };
7977             if(this.useAjax){
7978                 Roo.applyIf(o, this.conn);
7979                 if(this.activeRequest){
7980                     Roo.Ajax.abort(this.activeRequest);
7981                 }
7982                 this.activeRequest = Roo.Ajax.request(o);
7983             }else{
7984                 this.conn.request(o);
7985             }
7986         }else{
7987             callback.call(scope||this, null, arg, false);
7988         }
7989     },
7990
7991     // private
7992     loadResponse : function(o, success, response){
7993         delete this.activeRequest;
7994         if(!success){
7995             this.fireEvent("loadexception", this, o, response);
7996             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7997             return;
7998         }
7999         var result;
8000         try {
8001             result = o.reader.read(response);
8002         }catch(e){
8003             this.fireEvent("loadexception", this, o, response, e);
8004             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8005             return;
8006         }
8007         
8008         this.fireEvent("load", this, o, o.request.arg);
8009         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8010     },
8011
8012     // private
8013     update : function(dataSet){
8014
8015     },
8016
8017     // private
8018     updateResponse : function(dataSet){
8019
8020     }
8021 });/*
8022  * Based on:
8023  * Ext JS Library 1.1.1
8024  * Copyright(c) 2006-2007, Ext JS, LLC.
8025  *
8026  * Originally Released Under LGPL - original licence link has changed is not relivant.
8027  *
8028  * Fork - LGPL
8029  * <script type="text/javascript">
8030  */
8031
8032 /**
8033  * @class Roo.data.ScriptTagProxy
8034  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8035  * other than the originating domain of the running page.<br><br>
8036  * <p>
8037  * <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
8038  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8039  * <p>
8040  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8041  * source code that is used as the source inside a &lt;script> tag.<br><br>
8042  * <p>
8043  * In order for the browser to process the returned data, the server must wrap the data object
8044  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8045  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8046  * depending on whether the callback name was passed:
8047  * <p>
8048  * <pre><code>
8049 boolean scriptTag = false;
8050 String cb = request.getParameter("callback");
8051 if (cb != null) {
8052     scriptTag = true;
8053     response.setContentType("text/javascript");
8054 } else {
8055     response.setContentType("application/x-json");
8056 }
8057 Writer out = response.getWriter();
8058 if (scriptTag) {
8059     out.write(cb + "(");
8060 }
8061 out.print(dataBlock.toJsonString());
8062 if (scriptTag) {
8063     out.write(");");
8064 }
8065 </pre></code>
8066  *
8067  * @constructor
8068  * @param {Object} config A configuration object.
8069  */
8070 Roo.data.ScriptTagProxy = function(config){
8071     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8072     Roo.apply(this, config);
8073     this.head = document.getElementsByTagName("head")[0];
8074 };
8075
8076 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8077
8078 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8079     /**
8080      * @cfg {String} url The URL from which to request the data object.
8081      */
8082     /**
8083      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8084      */
8085     timeout : 30000,
8086     /**
8087      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8088      * the server the name of the callback function set up by the load call to process the returned data object.
8089      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8090      * javascript output which calls this named function passing the data object as its only parameter.
8091      */
8092     callbackParam : "callback",
8093     /**
8094      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8095      * name to the request.
8096      */
8097     nocache : true,
8098
8099     /**
8100      * Load data from the configured URL, read the data object into
8101      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8102      * process that block using the passed callback.
8103      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8104      * for the request to the remote server.
8105      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8106      * object into a block of Roo.data.Records.
8107      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8108      * The function must be passed <ul>
8109      * <li>The Record block object</li>
8110      * <li>The "arg" argument from the load function</li>
8111      * <li>A boolean success indicator</li>
8112      * </ul>
8113      * @param {Object} scope The scope in which to call the callback
8114      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8115      */
8116     load : function(params, reader, callback, scope, arg){
8117         if(this.fireEvent("beforeload", this, params) !== false){
8118
8119             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8120
8121             var url = this.url;
8122             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8123             if(this.nocache){
8124                 url += "&_dc=" + (new Date().getTime());
8125             }
8126             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8127             var trans = {
8128                 id : transId,
8129                 cb : "stcCallback"+transId,
8130                 scriptId : "stcScript"+transId,
8131                 params : params,
8132                 arg : arg,
8133                 url : url,
8134                 callback : callback,
8135                 scope : scope,
8136                 reader : reader
8137             };
8138             var conn = this;
8139
8140             window[trans.cb] = function(o){
8141                 conn.handleResponse(o, trans);
8142             };
8143
8144             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8145
8146             if(this.autoAbort !== false){
8147                 this.abort();
8148             }
8149
8150             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8151
8152             var script = document.createElement("script");
8153             script.setAttribute("src", url);
8154             script.setAttribute("type", "text/javascript");
8155             script.setAttribute("id", trans.scriptId);
8156             this.head.appendChild(script);
8157
8158             this.trans = trans;
8159         }else{
8160             callback.call(scope||this, null, arg, false);
8161         }
8162     },
8163
8164     // private
8165     isLoading : function(){
8166         return this.trans ? true : false;
8167     },
8168
8169     /**
8170      * Abort the current server request.
8171      */
8172     abort : function(){
8173         if(this.isLoading()){
8174             this.destroyTrans(this.trans);
8175         }
8176     },
8177
8178     // private
8179     destroyTrans : function(trans, isLoaded){
8180         this.head.removeChild(document.getElementById(trans.scriptId));
8181         clearTimeout(trans.timeoutId);
8182         if(isLoaded){
8183             window[trans.cb] = undefined;
8184             try{
8185                 delete window[trans.cb];
8186             }catch(e){}
8187         }else{
8188             // if hasn't been loaded, wait for load to remove it to prevent script error
8189             window[trans.cb] = function(){
8190                 window[trans.cb] = undefined;
8191                 try{
8192                     delete window[trans.cb];
8193                 }catch(e){}
8194             };
8195         }
8196     },
8197
8198     // private
8199     handleResponse : function(o, trans){
8200         this.trans = false;
8201         this.destroyTrans(trans, true);
8202         var result;
8203         try {
8204             result = trans.reader.readRecords(o);
8205         }catch(e){
8206             this.fireEvent("loadexception", this, o, trans.arg, e);
8207             trans.callback.call(trans.scope||window, null, trans.arg, false);
8208             return;
8209         }
8210         this.fireEvent("load", this, o, trans.arg);
8211         trans.callback.call(trans.scope||window, result, trans.arg, true);
8212     },
8213
8214     // private
8215     handleFailure : function(trans){
8216         this.trans = false;
8217         this.destroyTrans(trans, false);
8218         this.fireEvent("loadexception", this, null, trans.arg);
8219         trans.callback.call(trans.scope||window, null, trans.arg, false);
8220     }
8221 });/*
8222  * Based on:
8223  * Ext JS Library 1.1.1
8224  * Copyright(c) 2006-2007, Ext JS, LLC.
8225  *
8226  * Originally Released Under LGPL - original licence link has changed is not relivant.
8227  *
8228  * Fork - LGPL
8229  * <script type="text/javascript">
8230  */
8231
8232 /**
8233  * @class Roo.data.JsonReader
8234  * @extends Roo.data.DataReader
8235  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8236  * based on mappings in a provided Roo.data.Record constructor.
8237  * 
8238  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8239  * in the reply previously. 
8240  * 
8241  * <p>
8242  * Example code:
8243  * <pre><code>
8244 var RecordDef = Roo.data.Record.create([
8245     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8246     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8247 ]);
8248 var myReader = new Roo.data.JsonReader({
8249     totalProperty: "results",    // The property which contains the total dataset size (optional)
8250     root: "rows",                // The property which contains an Array of row objects
8251     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8252 }, RecordDef);
8253 </code></pre>
8254  * <p>
8255  * This would consume a JSON file like this:
8256  * <pre><code>
8257 { 'results': 2, 'rows': [
8258     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8259     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8260 }
8261 </code></pre>
8262  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8263  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8264  * paged from the remote server.
8265  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8266  * @cfg {String} root name of the property which contains the Array of row objects.
8267  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8268  * @constructor
8269  * Create a new JsonReader
8270  * @param {Object} meta Metadata configuration options
8271  * @param {Object} recordType Either an Array of field definition objects,
8272  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8273  */
8274 Roo.data.JsonReader = function(meta, recordType){
8275     
8276     meta = meta || {};
8277     // set some defaults:
8278     Roo.applyIf(meta, {
8279         totalProperty: 'total',
8280         successProperty : 'success',
8281         root : 'data',
8282         id : 'id'
8283     });
8284     
8285     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8286 };
8287 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8288     
8289     /**
8290      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8291      * Used by Store query builder to append _requestMeta to params.
8292      * 
8293      */
8294     metaFromRemote : false,
8295     /**
8296      * This method is only used by a DataProxy which has retrieved data from a remote server.
8297      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8298      * @return {Object} data A data block which is used by an Roo.data.Store object as
8299      * a cache of Roo.data.Records.
8300      */
8301     read : function(response){
8302         var json = response.responseText;
8303        
8304         var o = /* eval:var:o */ eval("("+json+")");
8305         if(!o) {
8306             throw {message: "JsonReader.read: Json object not found"};
8307         }
8308         
8309         if(o.metaData){
8310             
8311             delete this.ef;
8312             this.metaFromRemote = true;
8313             this.meta = o.metaData;
8314             this.recordType = Roo.data.Record.create(o.metaData.fields);
8315             this.onMetaChange(this.meta, this.recordType, o);
8316         }
8317         return this.readRecords(o);
8318     },
8319
8320     // private function a store will implement
8321     onMetaChange : function(meta, recordType, o){
8322
8323     },
8324
8325     /**
8326          * @ignore
8327          */
8328     simpleAccess: function(obj, subsc) {
8329         return obj[subsc];
8330     },
8331
8332         /**
8333          * @ignore
8334          */
8335     getJsonAccessor: function(){
8336         var re = /[\[\.]/;
8337         return function(expr) {
8338             try {
8339                 return(re.test(expr))
8340                     ? new Function("obj", "return obj." + expr)
8341                     : function(obj){
8342                         return obj[expr];
8343                     };
8344             } catch(e){}
8345             return Roo.emptyFn;
8346         };
8347     }(),
8348
8349     /**
8350      * Create a data block containing Roo.data.Records from an XML document.
8351      * @param {Object} o An object which contains an Array of row objects in the property specified
8352      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8353      * which contains the total size of the dataset.
8354      * @return {Object} data A data block which is used by an Roo.data.Store object as
8355      * a cache of Roo.data.Records.
8356      */
8357     readRecords : function(o){
8358         /**
8359          * After any data loads, the raw JSON data is available for further custom processing.
8360          * @type Object
8361          */
8362         this.o = o;
8363         var s = this.meta, Record = this.recordType,
8364             f = Record.prototype.fields, fi = f.items, fl = f.length;
8365
8366 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8367         if (!this.ef) {
8368             if(s.totalProperty) {
8369                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8370                 }
8371                 if(s.successProperty) {
8372                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8373                 }
8374                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8375                 if (s.id) {
8376                         var g = this.getJsonAccessor(s.id);
8377                         this.getId = function(rec) {
8378                                 var r = g(rec);
8379                                 return (r === undefined || r === "") ? null : r;
8380                         };
8381                 } else {
8382                         this.getId = function(){return null;};
8383                 }
8384             this.ef = [];
8385             for(var jj = 0; jj < fl; jj++){
8386                 f = fi[jj];
8387                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8388                 this.ef[jj] = this.getJsonAccessor(map);
8389             }
8390         }
8391
8392         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8393         if(s.totalProperty){
8394             var vt = parseInt(this.getTotal(o), 10);
8395             if(!isNaN(vt)){
8396                 totalRecords = vt;
8397             }
8398         }
8399         if(s.successProperty){
8400             var vs = this.getSuccess(o);
8401             if(vs === false || vs === 'false'){
8402                 success = false;
8403             }
8404         }
8405         var records = [];
8406             for(var i = 0; i < c; i++){
8407                     var n = root[i];
8408                 var values = {};
8409                 var id = this.getId(n);
8410                 for(var j = 0; j < fl; j++){
8411                     f = fi[j];
8412                 var v = this.ef[j](n);
8413                 if (!f.convert) {
8414                     Roo.log('missing convert for ' + f.name);
8415                     Roo.log(f);
8416                     continue;
8417                 }
8418                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8419                 }
8420                 var record = new Record(values, id);
8421                 record.json = n;
8422                 records[i] = record;
8423             }
8424             return {
8425             raw : o,
8426                 success : success,
8427                 records : records,
8428                 totalRecords : totalRecords
8429             };
8430     }
8431 });/*
8432  * Based on:
8433  * Ext JS Library 1.1.1
8434  * Copyright(c) 2006-2007, Ext JS, LLC.
8435  *
8436  * Originally Released Under LGPL - original licence link has changed is not relivant.
8437  *
8438  * Fork - LGPL
8439  * <script type="text/javascript">
8440  */
8441
8442 /**
8443  * @class Roo.data.ArrayReader
8444  * @extends Roo.data.DataReader
8445  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8446  * Each element of that Array represents a row of data fields. The
8447  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8448  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8449  * <p>
8450  * Example code:.
8451  * <pre><code>
8452 var RecordDef = Roo.data.Record.create([
8453     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8454     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8455 ]);
8456 var myReader = new Roo.data.ArrayReader({
8457     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8458 }, RecordDef);
8459 </code></pre>
8460  * <p>
8461  * This would consume an Array like this:
8462  * <pre><code>
8463 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8464   </code></pre>
8465  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8466  * @constructor
8467  * Create a new JsonReader
8468  * @param {Object} meta Metadata configuration options.
8469  * @param {Object} recordType Either an Array of field definition objects
8470  * as specified to {@link Roo.data.Record#create},
8471  * or an {@link Roo.data.Record} object
8472  * created using {@link Roo.data.Record#create}.
8473  */
8474 Roo.data.ArrayReader = function(meta, recordType){
8475     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8476 };
8477
8478 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8479     /**
8480      * Create a data block containing Roo.data.Records from an XML document.
8481      * @param {Object} o An Array of row objects which represents the dataset.
8482      * @return {Object} data A data block which is used by an Roo.data.Store object as
8483      * a cache of Roo.data.Records.
8484      */
8485     readRecords : function(o){
8486         var sid = this.meta ? this.meta.id : null;
8487         var recordType = this.recordType, fields = recordType.prototype.fields;
8488         var records = [];
8489         var root = o;
8490             for(var i = 0; i < root.length; i++){
8491                     var n = root[i];
8492                 var values = {};
8493                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8494                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8495                 var f = fields.items[j];
8496                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8497                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8498                 v = f.convert(v);
8499                 values[f.name] = v;
8500             }
8501                 var record = new recordType(values, id);
8502                 record.json = n;
8503                 records[records.length] = record;
8504             }
8505             return {
8506                 records : records,
8507                 totalRecords : records.length
8508             };
8509     }
8510 });/*
8511  * - LGPL
8512  * * 
8513  */
8514
8515 /**
8516  * @class Roo.bootstrap.ComboBox
8517  * @extends Roo.bootstrap.TriggerField
8518  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8519  * @cfg {Boolean} append (true|false) default false
8520  * @constructor
8521  * Create a new ComboBox.
8522  * @param {Object} config Configuration options
8523  */
8524 Roo.bootstrap.ComboBox = function(config){
8525     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8526     this.addEvents({
8527         /**
8528          * @event expand
8529          * Fires when the dropdown list is expanded
8530              * @param {Roo.bootstrap.ComboBox} combo This combo box
8531              */
8532         'expand' : true,
8533         /**
8534          * @event collapse
8535          * Fires when the dropdown list is collapsed
8536              * @param {Roo.bootstrap.ComboBox} combo This combo box
8537              */
8538         'collapse' : true,
8539         /**
8540          * @event beforeselect
8541          * Fires before a list item is selected. Return false to cancel the selection.
8542              * @param {Roo.bootstrap.ComboBox} combo This combo box
8543              * @param {Roo.data.Record} record The data record returned from the underlying store
8544              * @param {Number} index The index of the selected item in the dropdown list
8545              */
8546         'beforeselect' : true,
8547         /**
8548          * @event select
8549          * Fires when a list item is selected
8550              * @param {Roo.bootstrap.ComboBox} combo This combo box
8551              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8552              * @param {Number} index The index of the selected item in the dropdown list
8553              */
8554         'select' : true,
8555         /**
8556          * @event beforequery
8557          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8558          * The event object passed has these properties:
8559              * @param {Roo.bootstrap.ComboBox} combo This combo box
8560              * @param {String} query The query
8561              * @param {Boolean} forceAll true to force "all" query
8562              * @param {Boolean} cancel true to cancel the query
8563              * @param {Object} e The query event object
8564              */
8565         'beforequery': true,
8566          /**
8567          * @event add
8568          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8569              * @param {Roo.bootstrap.ComboBox} combo This combo box
8570              */
8571         'add' : true,
8572         /**
8573          * @event edit
8574          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8575              * @param {Roo.bootstrap.ComboBox} combo This combo box
8576              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8577              */
8578         'edit' : true,
8579         /**
8580          * @event remove
8581          * Fires when the remove value from the combobox array
8582              * @param {Roo.bootstrap.ComboBox} combo This combo box
8583              */
8584         'remove' : true
8585         
8586     });
8587     
8588     
8589     this.selectedIndex = -1;
8590     if(this.mode == 'local'){
8591         if(config.queryDelay === undefined){
8592             this.queryDelay = 10;
8593         }
8594         if(config.minChars === undefined){
8595             this.minChars = 0;
8596         }
8597     }
8598 };
8599
8600 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8601      
8602     /**
8603      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8604      * rendering into an Roo.Editor, defaults to false)
8605      */
8606     /**
8607      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8608      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8609      */
8610     /**
8611      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8612      */
8613     /**
8614      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8615      * the dropdown list (defaults to undefined, with no header element)
8616      */
8617
8618      /**
8619      * @cfg {String/Roo.Template} tpl The template to use to render the output
8620      */
8621      
8622      /**
8623      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8624      */
8625     listWidth: undefined,
8626     /**
8627      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8628      * mode = 'remote' or 'text' if mode = 'local')
8629      */
8630     displayField: undefined,
8631     /**
8632      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8633      * mode = 'remote' or 'value' if mode = 'local'). 
8634      * Note: use of a valueField requires the user make a selection
8635      * in order for a value to be mapped.
8636      */
8637     valueField: undefined,
8638     
8639     
8640     /**
8641      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8642      * field's data value (defaults to the underlying DOM element's name)
8643      */
8644     hiddenName: undefined,
8645     /**
8646      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8647      */
8648     listClass: '',
8649     /**
8650      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8651      */
8652     selectedClass: 'active',
8653     
8654     /**
8655      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8656      */
8657     shadow:'sides',
8658     /**
8659      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8660      * anchor positions (defaults to 'tl-bl')
8661      */
8662     listAlign: 'tl-bl?',
8663     /**
8664      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8665      */
8666     maxHeight: 300,
8667     /**
8668      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
8669      * query specified by the allQuery config option (defaults to 'query')
8670      */
8671     triggerAction: 'query',
8672     /**
8673      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8674      * (defaults to 4, does not apply if editable = false)
8675      */
8676     minChars : 4,
8677     /**
8678      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8679      * delay (typeAheadDelay) if it matches a known value (defaults to false)
8680      */
8681     typeAhead: false,
8682     /**
8683      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8684      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8685      */
8686     queryDelay: 500,
8687     /**
8688      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8689      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
8690      */
8691     pageSize: 0,
8692     /**
8693      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
8694      * when editable = true (defaults to false)
8695      */
8696     selectOnFocus:false,
8697     /**
8698      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8699      */
8700     queryParam: 'query',
8701     /**
8702      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
8703      * when mode = 'remote' (defaults to 'Loading...')
8704      */
8705     loadingText: 'Loading...',
8706     /**
8707      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8708      */
8709     resizable: false,
8710     /**
8711      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8712      */
8713     handleHeight : 8,
8714     /**
8715      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8716      * traditional select (defaults to true)
8717      */
8718     editable: true,
8719     /**
8720      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8721      */
8722     allQuery: '',
8723     /**
8724      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8725      */
8726     mode: 'remote',
8727     /**
8728      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8729      * listWidth has a higher value)
8730      */
8731     minListWidth : 70,
8732     /**
8733      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8734      * allow the user to set arbitrary text into the field (defaults to false)
8735      */
8736     forceSelection:false,
8737     /**
8738      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8739      * if typeAhead = true (defaults to 250)
8740      */
8741     typeAheadDelay : 250,
8742     /**
8743      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8744      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8745      */
8746     valueNotFoundText : undefined,
8747     /**
8748      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8749      */
8750     blockFocus : false,
8751     
8752     /**
8753      * @cfg {Boolean} disableClear Disable showing of clear button.
8754      */
8755     disableClear : false,
8756     /**
8757      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8758      */
8759     alwaysQuery : false,
8760     
8761     /**
8762      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8763      */
8764     multiple : false,
8765     
8766     //private
8767     addicon : false,
8768     editicon: false,
8769     
8770     page: 0,
8771     hasQuery: false,
8772     append: false,
8773     loadNext: false,
8774     item: [],
8775     
8776     // element that contains real text value.. (when hidden is used..)
8777      
8778     // private
8779     initEvents: function(){
8780         
8781         if (!this.store) {
8782             throw "can not find store for combo";
8783         }
8784         this.store = Roo.factory(this.store, Roo.data);
8785         
8786         
8787         
8788         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8789         
8790         
8791         if(this.hiddenName){
8792             
8793             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8794             
8795             this.hiddenField.dom.value =
8796                 this.hiddenValue !== undefined ? this.hiddenValue :
8797                 this.value !== undefined ? this.value : '';
8798
8799             // prevent input submission
8800             this.el.dom.removeAttribute('name');
8801             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8802              
8803              
8804         }
8805         //if(Roo.isGecko){
8806         //    this.el.dom.setAttribute('autocomplete', 'off');
8807         //}
8808
8809         var cls = 'x-combo-list';
8810         this.list = this.el.select('ul.dropdown-menu',true).first();
8811
8812         //this.list = new Roo.Layer({
8813         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8814         //});
8815         
8816         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8817         this.list.setWidth(lw);
8818         
8819         this.list.on('mouseover', this.onViewOver, this);
8820         this.list.on('mousemove', this.onViewMove, this);
8821         
8822         this.list.on('scroll', this.onViewScroll, this);
8823         
8824         /*
8825         this.list.swallowEvent('mousewheel');
8826         this.assetHeight = 0;
8827
8828         if(this.title){
8829             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8830             this.assetHeight += this.header.getHeight();
8831         }
8832
8833         this.innerList = this.list.createChild({cls:cls+'-inner'});
8834         this.innerList.on('mouseover', this.onViewOver, this);
8835         this.innerList.on('mousemove', this.onViewMove, this);
8836         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8837         
8838         if(this.allowBlank && !this.pageSize && !this.disableClear){
8839             this.footer = this.list.createChild({cls:cls+'-ft'});
8840             this.pageTb = new Roo.Toolbar(this.footer);
8841            
8842         }
8843         if(this.pageSize){
8844             this.footer = this.list.createChild({cls:cls+'-ft'});
8845             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8846                     {pageSize: this.pageSize});
8847             
8848         }
8849         
8850         if (this.pageTb && this.allowBlank && !this.disableClear) {
8851             var _this = this;
8852             this.pageTb.add(new Roo.Toolbar.Fill(), {
8853                 cls: 'x-btn-icon x-btn-clear',
8854                 text: '&#160;',
8855                 handler: function()
8856                 {
8857                     _this.collapse();
8858                     _this.clearValue();
8859                     _this.onSelect(false, -1);
8860                 }
8861             });
8862         }
8863         if (this.footer) {
8864             this.assetHeight += this.footer.getHeight();
8865         }
8866         */
8867             
8868         if(!this.tpl){
8869             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8870         }
8871
8872         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8873             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8874         });
8875         //this.view.wrapEl.setDisplayed(false);
8876         this.view.on('click', this.onViewClick, this);
8877         
8878         
8879         
8880         this.store.on('beforeload', this.onBeforeLoad, this);
8881         this.store.on('load', this.onLoad, this);
8882         this.store.on('loadexception', this.onLoadException, this);
8883         /*
8884         if(this.resizable){
8885             this.resizer = new Roo.Resizable(this.list,  {
8886                pinned:true, handles:'se'
8887             });
8888             this.resizer.on('resize', function(r, w, h){
8889                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8890                 this.listWidth = w;
8891                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8892                 this.restrictHeight();
8893             }, this);
8894             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8895         }
8896         */
8897         if(!this.editable){
8898             this.editable = true;
8899             this.setEditable(false);
8900         }
8901         
8902         /*
8903         
8904         if (typeof(this.events.add.listeners) != 'undefined') {
8905             
8906             this.addicon = this.wrap.createChild(
8907                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8908        
8909             this.addicon.on('click', function(e) {
8910                 this.fireEvent('add', this);
8911             }, this);
8912         }
8913         if (typeof(this.events.edit.listeners) != 'undefined') {
8914             
8915             this.editicon = this.wrap.createChild(
8916                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8917             if (this.addicon) {
8918                 this.editicon.setStyle('margin-left', '40px');
8919             }
8920             this.editicon.on('click', function(e) {
8921                 
8922                 // we fire even  if inothing is selected..
8923                 this.fireEvent('edit', this, this.lastData );
8924                 
8925             }, this);
8926         }
8927         */
8928         
8929         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8930             "up" : function(e){
8931                 this.inKeyMode = true;
8932                 this.selectPrev();
8933             },
8934
8935             "down" : function(e){
8936                 if(!this.isExpanded()){
8937                     this.onTriggerClick();
8938                 }else{
8939                     this.inKeyMode = true;
8940                     this.selectNext();
8941                 }
8942             },
8943
8944             "enter" : function(e){
8945                 this.onViewClick();
8946                 //return true;
8947             },
8948
8949             "esc" : function(e){
8950                 this.collapse();
8951             },
8952
8953             "tab" : function(e){
8954                 this.collapse();
8955                 
8956                 if(this.fireEvent("specialkey", this, e)){
8957                     this.onViewClick(false);
8958                 }
8959                 
8960                 return true;
8961             },
8962
8963             scope : this,
8964
8965             doRelay : function(foo, bar, hname){
8966                 if(hname == 'down' || this.scope.isExpanded()){
8967                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8968                 }
8969                 return true;
8970             },
8971
8972             forceKeyDown: true
8973         });
8974         
8975         
8976         this.queryDelay = Math.max(this.queryDelay || 10,
8977                 this.mode == 'local' ? 10 : 250);
8978         
8979         
8980         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8981         
8982         if(this.typeAhead){
8983             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8984         }
8985         if(this.editable !== false){
8986             this.inputEl().on("keyup", this.onKeyUp, this);
8987         }
8988         if(this.forceSelection){
8989             this.on('blur', this.doForce, this);
8990         }
8991         
8992         if(this.multiple){
8993             this.choices = this.el.select('ul.select2-choices', true).first();
8994             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8995         }
8996     },
8997
8998     onDestroy : function(){
8999         if(this.view){
9000             this.view.setStore(null);
9001             this.view.el.removeAllListeners();
9002             this.view.el.remove();
9003             this.view.purgeListeners();
9004         }
9005         if(this.list){
9006             this.list.dom.innerHTML  = '';
9007         }
9008         if(this.store){
9009             this.store.un('beforeload', this.onBeforeLoad, this);
9010             this.store.un('load', this.onLoad, this);
9011             this.store.un('loadexception', this.onLoadException, this);
9012         }
9013         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9014     },
9015
9016     // private
9017     fireKey : function(e){
9018         if(e.isNavKeyPress() && !this.list.isVisible()){
9019             this.fireEvent("specialkey", this, e);
9020         }
9021     },
9022
9023     // private
9024     onResize: function(w, h){
9025 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9026 //        
9027 //        if(typeof w != 'number'){
9028 //            // we do not handle it!?!?
9029 //            return;
9030 //        }
9031 //        var tw = this.trigger.getWidth();
9032 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9033 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9034 //        var x = w - tw;
9035 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9036 //            
9037 //        //this.trigger.setStyle('left', x+'px');
9038 //        
9039 //        if(this.list && this.listWidth === undefined){
9040 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9041 //            this.list.setWidth(lw);
9042 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9043 //        }
9044         
9045     
9046         
9047     },
9048
9049     /**
9050      * Allow or prevent the user from directly editing the field text.  If false is passed,
9051      * the user will only be able to select from the items defined in the dropdown list.  This method
9052      * is the runtime equivalent of setting the 'editable' config option at config time.
9053      * @param {Boolean} value True to allow the user to directly edit the field text
9054      */
9055     setEditable : function(value){
9056         if(value == this.editable){
9057             return;
9058         }
9059         this.editable = value;
9060         if(!value){
9061             this.inputEl().dom.setAttribute('readOnly', true);
9062             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9063             this.inputEl().addClass('x-combo-noedit');
9064         }else{
9065             this.inputEl().dom.setAttribute('readOnly', false);
9066             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9067             this.inputEl().removeClass('x-combo-noedit');
9068         }
9069     },
9070
9071     // private
9072     
9073     onBeforeLoad : function(combo,opts){
9074         if(!this.hasFocus){
9075             return;
9076         }
9077          if (!opts.add) {
9078             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9079          }
9080         this.restrictHeight();
9081         this.selectedIndex = -1;
9082     },
9083
9084     // private
9085     onLoad : function(){
9086         
9087         this.hasQuery = false;
9088         
9089         if(!this.hasFocus){
9090             return;
9091         }
9092         
9093         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9094             this.loading.hide();
9095         }
9096         
9097         if(this.store.getCount() > 0){
9098             this.expand();
9099             this.restrictHeight();
9100             if(this.lastQuery == this.allQuery){
9101                 if(this.editable){
9102                     this.inputEl().dom.select();
9103                 }
9104                 if(!this.selectByValue(this.value, true)){
9105                     this.select(0, true);
9106                 }
9107             }else{
9108                 this.selectNext();
9109                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9110                     this.taTask.delay(this.typeAheadDelay);
9111                 }
9112             }
9113         }else{
9114             this.onEmptyResults();
9115         }
9116         
9117         //this.el.focus();
9118     },
9119     // private
9120     onLoadException : function()
9121     {
9122         this.hasQuery = false;
9123         
9124         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9125             this.loading.hide();
9126         }
9127         
9128         this.collapse();
9129         Roo.log(this.store.reader.jsonData);
9130         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9131             // fixme
9132             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9133         }
9134         
9135         
9136     },
9137     // private
9138     onTypeAhead : function(){
9139         if(this.store.getCount() > 0){
9140             var r = this.store.getAt(0);
9141             var newValue = r.data[this.displayField];
9142             var len = newValue.length;
9143             var selStart = this.getRawValue().length;
9144             
9145             if(selStart != len){
9146                 this.setRawValue(newValue);
9147                 this.selectText(selStart, newValue.length);
9148             }
9149         }
9150     },
9151
9152     // private
9153     onSelect : function(record, index){
9154         
9155         if(this.fireEvent('beforeselect', this, record, index) !== false){
9156         
9157             this.setFromData(index > -1 ? record.data : false);
9158             
9159             this.collapse();
9160             this.fireEvent('select', this, record, index);
9161         }
9162     },
9163
9164     /**
9165      * Returns the currently selected field value or empty string if no value is set.
9166      * @return {String} value The selected value
9167      */
9168     getValue : function(){
9169         
9170         if(this.multiple){
9171             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9172         }
9173         
9174         if(this.valueField){
9175             return typeof this.value != 'undefined' ? this.value : '';
9176         }else{
9177             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9178         }
9179     },
9180
9181     /**
9182      * Clears any text/value currently set in the field
9183      */
9184     clearValue : function(){
9185         if(this.hiddenField){
9186             this.hiddenField.dom.value = '';
9187         }
9188         this.value = '';
9189         this.setRawValue('');
9190         this.lastSelectionText = '';
9191         
9192     },
9193
9194     /**
9195      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9196      * will be displayed in the field.  If the value does not match the data value of an existing item,
9197      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9198      * Otherwise the field will be blank (although the value will still be set).
9199      * @param {String} value The value to match
9200      */
9201     setValue : function(v){
9202         if(this.multiple){
9203             this.syncValue();
9204             return;
9205         }
9206         
9207         var text = v;
9208         if(this.valueField){
9209             var r = this.findRecord(this.valueField, v);
9210             if(r){
9211                 text = r.data[this.displayField];
9212             }else if(this.valueNotFoundText !== undefined){
9213                 text = this.valueNotFoundText;
9214             }
9215         }
9216         this.lastSelectionText = text;
9217         if(this.hiddenField){
9218             this.hiddenField.dom.value = v;
9219         }
9220         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9221         this.value = v;
9222     },
9223     /**
9224      * @property {Object} the last set data for the element
9225      */
9226     
9227     lastData : false,
9228     /**
9229      * Sets the value of the field based on a object which is related to the record format for the store.
9230      * @param {Object} value the value to set as. or false on reset?
9231      */
9232     setFromData : function(o){
9233         
9234         if(this.multiple){
9235             this.addItem(o);
9236             return;
9237         }
9238             
9239         var dv = ''; // display value
9240         var vv = ''; // value value..
9241         this.lastData = o;
9242         if (this.displayField) {
9243             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9244         } else {
9245             // this is an error condition!!!
9246             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9247         }
9248         
9249         if(this.valueField){
9250             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9251         }
9252         
9253         if(this.hiddenField){
9254             this.hiddenField.dom.value = vv;
9255             
9256             this.lastSelectionText = dv;
9257             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9258             this.value = vv;
9259             return;
9260         }
9261         // no hidden field.. - we store the value in 'value', but still display
9262         // display field!!!!
9263         this.lastSelectionText = dv;
9264         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9265         this.value = vv;
9266         
9267         
9268     },
9269     // private
9270     reset : function(){
9271         // overridden so that last data is reset..
9272         this.setValue(this.originalValue);
9273         this.clearInvalid();
9274         this.lastData = false;
9275         if (this.view) {
9276             this.view.clearSelections();
9277         }
9278     },
9279     // private
9280     findRecord : function(prop, value){
9281         var record;
9282         if(this.store.getCount() > 0){
9283             this.store.each(function(r){
9284                 if(r.data[prop] == value){
9285                     record = r;
9286                     return false;
9287                 }
9288                 return true;
9289             });
9290         }
9291         return record;
9292     },
9293     
9294     getName: function()
9295     {
9296         // returns hidden if it's set..
9297         if (!this.rendered) {return ''};
9298         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9299         
9300     },
9301     // private
9302     onViewMove : function(e, t){
9303         this.inKeyMode = false;
9304     },
9305
9306     // private
9307     onViewOver : function(e, t){
9308         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9309             return;
9310         }
9311         var item = this.view.findItemFromChild(t);
9312         if(item){
9313             var index = this.view.indexOf(item);
9314             this.select(index, false);
9315         }
9316     },
9317
9318     // private
9319     onViewClick : function(doFocus)
9320     {
9321         var index = this.view.getSelectedIndexes()[0];
9322         var r = this.store.getAt(index);
9323         if(r){
9324             this.onSelect(r, index);
9325         }
9326         if(doFocus !== false && !this.blockFocus){
9327             this.inputEl().focus();
9328         }
9329     },
9330
9331     // private
9332     restrictHeight : function(){
9333         //this.innerList.dom.style.height = '';
9334         //var inner = this.innerList.dom;
9335         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9336         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9337         //this.list.beginUpdate();
9338         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9339         this.list.alignTo(this.inputEl(), this.listAlign);
9340         //this.list.endUpdate();
9341     },
9342
9343     // private
9344     onEmptyResults : function(){
9345         this.collapse();
9346     },
9347
9348     /**
9349      * Returns true if the dropdown list is expanded, else false.
9350      */
9351     isExpanded : function(){
9352         return this.list.isVisible();
9353     },
9354
9355     /**
9356      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9357      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9358      * @param {String} value The data value of the item to select
9359      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9360      * selected item if it is not currently in view (defaults to true)
9361      * @return {Boolean} True if the value matched an item in the list, else false
9362      */
9363     selectByValue : function(v, scrollIntoView){
9364         if(v !== undefined && v !== null){
9365             var r = this.findRecord(this.valueField || this.displayField, v);
9366             if(r){
9367                 this.select(this.store.indexOf(r), scrollIntoView);
9368                 return true;
9369             }
9370         }
9371         return false;
9372     },
9373
9374     /**
9375      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9376      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9377      * @param {Number} index The zero-based index of the list item to select
9378      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9379      * selected item if it is not currently in view (defaults to true)
9380      */
9381     select : function(index, scrollIntoView){
9382         this.selectedIndex = index;
9383         this.view.select(index);
9384         if(scrollIntoView !== false){
9385             var el = this.view.getNode(index);
9386             if(el){
9387                 //this.innerList.scrollChildIntoView(el, false);
9388                 
9389             }
9390         }
9391     },
9392
9393     // private
9394     selectNext : function(){
9395         var ct = this.store.getCount();
9396         if(ct > 0){
9397             if(this.selectedIndex == -1){
9398                 this.select(0);
9399             }else if(this.selectedIndex < ct-1){
9400                 this.select(this.selectedIndex+1);
9401             }
9402         }
9403     },
9404
9405     // private
9406     selectPrev : function(){
9407         var ct = this.store.getCount();
9408         if(ct > 0){
9409             if(this.selectedIndex == -1){
9410                 this.select(0);
9411             }else if(this.selectedIndex != 0){
9412                 this.select(this.selectedIndex-1);
9413             }
9414         }
9415     },
9416
9417     // private
9418     onKeyUp : function(e){
9419         if(this.editable !== false && !e.isSpecialKey()){
9420             this.lastKey = e.getKey();
9421             this.dqTask.delay(this.queryDelay);
9422         }
9423     },
9424
9425     // private
9426     validateBlur : function(){
9427         return !this.list || !this.list.isVisible();   
9428     },
9429
9430     // private
9431     initQuery : function(){
9432         this.doQuery(this.getRawValue());
9433     },
9434
9435     // private
9436     doForce : function(){
9437         if(this.el.dom.value.length > 0){
9438             this.el.dom.value =
9439                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9440              
9441         }
9442     },
9443
9444     /**
9445      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9446      * query allowing the query action to be canceled if needed.
9447      * @param {String} query The SQL query to execute
9448      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9449      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9450      * saved in the current store (defaults to false)
9451      */
9452     doQuery : function(q, forceAll){
9453         
9454         if(q === undefined || q === null){
9455             q = '';
9456         }
9457         var qe = {
9458             query: q,
9459             forceAll: forceAll,
9460             combo: this,
9461             cancel:false
9462         };
9463         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9464             return false;
9465         }
9466         q = qe.query;
9467         
9468         forceAll = qe.forceAll;
9469         if(forceAll === true || (q.length >= this.minChars)){
9470             
9471             this.hasQuery = true;
9472             
9473             if(this.lastQuery != q || this.alwaysQuery){
9474                 this.lastQuery = q;
9475                 if(this.mode == 'local'){
9476                     this.selectedIndex = -1;
9477                     if(forceAll){
9478                         this.store.clearFilter();
9479                     }else{
9480                         this.store.filter(this.displayField, q);
9481                     }
9482                     this.onLoad();
9483                 }else{
9484                     this.store.baseParams[this.queryParam] = q;
9485                     
9486                     var options = {params : this.getParams(q)};
9487                     
9488                     if(this.loadNext){
9489                         options.add = true;
9490                         options.params.start = this.page * this.pageSize;
9491                     }
9492                     
9493                     this.store.load(options);
9494                     this.expand();
9495                 }
9496             }else{
9497                 this.selectedIndex = -1;
9498                 this.onLoad();   
9499             }
9500         }
9501         
9502         this.loadNext = false;
9503     },
9504
9505     // private
9506     getParams : function(q){
9507         var p = {};
9508         //p[this.queryParam] = q;
9509         
9510         if(this.pageSize){
9511             p.start = 0;
9512             p.limit = this.pageSize;
9513         }
9514         return p;
9515     },
9516
9517     /**
9518      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9519      */
9520     collapse : function(){
9521         if(!this.isExpanded()){
9522             return;
9523         }
9524         
9525         this.list.hide();
9526         Roo.get(document).un('mousedown', this.collapseIf, this);
9527         Roo.get(document).un('mousewheel', this.collapseIf, this);
9528         if (!this.editable) {
9529             Roo.get(document).un('keydown', this.listKeyPress, this);
9530         }
9531         this.fireEvent('collapse', this);
9532     },
9533
9534     // private
9535     collapseIf : function(e){
9536         var in_combo  = e.within(this.el);
9537         var in_list =  e.within(this.list);
9538         
9539         if (in_combo || in_list) {
9540             //e.stopPropagation();
9541             return;
9542         }
9543
9544         this.collapse();
9545         
9546     },
9547
9548     /**
9549      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9550      */
9551     expand : function(){
9552        
9553         if(this.isExpanded() || !this.hasFocus){
9554             return;
9555         }
9556          Roo.log('expand');
9557         this.list.alignTo(this.inputEl(), this.listAlign);
9558         this.list.show();
9559         Roo.get(document).on('mousedown', this.collapseIf, this);
9560         Roo.get(document).on('mousewheel', this.collapseIf, this);
9561         if (!this.editable) {
9562             Roo.get(document).on('keydown', this.listKeyPress, this);
9563         }
9564         
9565         this.fireEvent('expand', this);
9566     },
9567
9568     // private
9569     // Implements the default empty TriggerField.onTriggerClick function
9570     onTriggerClick : function()
9571     {
9572         Roo.log('trigger click');
9573         
9574         if(this.disabled){
9575             return;
9576         }
9577         
9578         this.page = 0;
9579         this.loadNext = false;
9580         
9581         if(this.isExpanded()){
9582             this.collapse();
9583             if (!this.blockFocus) {
9584                 this.inputEl().focus();
9585             }
9586             
9587         }else {
9588             this.hasFocus = true;
9589             if(this.triggerAction == 'all') {
9590                 this.doQuery(this.allQuery, true);
9591             } else {
9592                 this.doQuery(this.getRawValue());
9593             }
9594             if (!this.blockFocus) {
9595                 this.inputEl().focus();
9596             }
9597         }
9598     },
9599     listKeyPress : function(e)
9600     {
9601         //Roo.log('listkeypress');
9602         // scroll to first matching element based on key pres..
9603         if (e.isSpecialKey()) {
9604             return false;
9605         }
9606         var k = String.fromCharCode(e.getKey()).toUpperCase();
9607         //Roo.log(k);
9608         var match  = false;
9609         var csel = this.view.getSelectedNodes();
9610         var cselitem = false;
9611         if (csel.length) {
9612             var ix = this.view.indexOf(csel[0]);
9613             cselitem  = this.store.getAt(ix);
9614             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9615                 cselitem = false;
9616             }
9617             
9618         }
9619         
9620         this.store.each(function(v) { 
9621             if (cselitem) {
9622                 // start at existing selection.
9623                 if (cselitem.id == v.id) {
9624                     cselitem = false;
9625                 }
9626                 return true;
9627             }
9628                 
9629             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9630                 match = this.store.indexOf(v);
9631                 return false;
9632             }
9633             return true;
9634         }, this);
9635         
9636         if (match === false) {
9637             return true; // no more action?
9638         }
9639         // scroll to?
9640         this.view.select(match);
9641         var sn = Roo.get(this.view.getSelectedNodes()[0])
9642         //sn.scrollIntoView(sn.dom.parentNode, false);
9643     },
9644     
9645     onViewScroll : function(e, t){
9646         
9647         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9648             return;
9649         }
9650         
9651         this.hasQuery = true;
9652         
9653         this.loading = this.list.select('.loading', true).first();
9654         
9655         if(this.loading === null){
9656             this.list.createChild({
9657                 tag: 'div',
9658                 cls: 'loading select2-more-results select2-active',
9659                 html: 'Loading more results...'
9660             })
9661             
9662             this.loading = this.list.select('.loading', true).first();
9663             
9664             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9665             
9666             this.loading.hide();
9667         }
9668         
9669         this.loading.show();
9670         
9671         var _combo = this;
9672         
9673         this.page++;
9674         this.loadNext = true;
9675         
9676         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9677         
9678         return;
9679     },
9680     
9681     addItem : function(o)
9682     {   
9683         var dv = ''; // display value
9684         
9685         if (this.displayField) {
9686             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9687         } else {
9688             // this is an error condition!!!
9689             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9690         }
9691         
9692         if(!dv.length){
9693             return;
9694         }
9695         
9696         var choice = this.choices.createChild({
9697             tag: 'li',
9698             cls: 'select2-search-choice',
9699             cn: [
9700                 {
9701                     tag: 'div',
9702                     html: dv
9703                 },
9704                 {
9705                     tag: 'a',
9706                     href: '#',
9707                     cls: 'select2-search-choice-close',
9708                     tabindex: '-1'
9709                 }
9710             ]
9711             
9712         }, this.searchField);
9713         
9714         var close = choice.select('a.select2-search-choice-close', true).first()
9715         
9716         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9717         
9718         this.item.push(o);
9719         this.lastData = o;
9720         
9721         this.syncValue();
9722         
9723         this.inputEl().dom.value = '';
9724         
9725     },
9726     
9727     onRemoveItem : function(e, _self, o)
9728     {
9729         Roo.log('remove item');
9730         var index = this.item.indexOf(o.data) * 1;
9731         
9732         if( index < 0){
9733             Roo.log('not this item?!');
9734             return;
9735         }
9736         
9737         this.item.splice(index, 1);
9738         o.item.remove();
9739         
9740         this.syncValue();
9741         
9742         this.fireEvent('remove', this);
9743         
9744     },
9745     
9746     syncValue : function()
9747     {
9748         if(!this.item.length){
9749             this.clearValue();
9750             return;
9751         }
9752             
9753         var value = [];
9754         var _this = this;
9755         Roo.each(this.item, function(i){
9756             if(_this.valueField){
9757                 value.push(i[_this.valueField]);
9758                 return;
9759             }
9760
9761             value.push(i);
9762         });
9763
9764         this.value = value.join(',');
9765
9766         if(this.hiddenField){
9767             this.hiddenField.dom.value = this.value;
9768         }
9769     },
9770     
9771     clearItem : function()
9772     {
9773         if(!this.multiple){
9774             return;
9775         }
9776         
9777         this.item = [];
9778         
9779         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9780            c.remove();
9781         });
9782         
9783         this.syncValue();
9784     }
9785     
9786     
9787
9788     /** 
9789     * @cfg {Boolean} grow 
9790     * @hide 
9791     */
9792     /** 
9793     * @cfg {Number} growMin 
9794     * @hide 
9795     */
9796     /** 
9797     * @cfg {Number} growMax 
9798     * @hide 
9799     */
9800     /**
9801      * @hide
9802      * @method autoSize
9803      */
9804 });
9805 /*
9806  * Based on:
9807  * Ext JS Library 1.1.1
9808  * Copyright(c) 2006-2007, Ext JS, LLC.
9809  *
9810  * Originally Released Under LGPL - original licence link has changed is not relivant.
9811  *
9812  * Fork - LGPL
9813  * <script type="text/javascript">
9814  */
9815
9816 /**
9817  * @class Roo.View
9818  * @extends Roo.util.Observable
9819  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9820  * This class also supports single and multi selection modes. <br>
9821  * Create a data model bound view:
9822  <pre><code>
9823  var store = new Roo.data.Store(...);
9824
9825  var view = new Roo.View({
9826     el : "my-element",
9827     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9828  
9829     singleSelect: true,
9830     selectedClass: "ydataview-selected",
9831     store: store
9832  });
9833
9834  // listen for node click?
9835  view.on("click", function(vw, index, node, e){
9836  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9837  });
9838
9839  // load XML data
9840  dataModel.load("foobar.xml");
9841  </code></pre>
9842  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9843  * <br><br>
9844  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9845  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9846  * 
9847  * Note: old style constructor is still suported (container, template, config)
9848  * 
9849  * @constructor
9850  * Create a new View
9851  * @param {Object} config The config object
9852  * 
9853  */
9854 Roo.View = function(config, depreciated_tpl, depreciated_config){
9855     
9856     if (typeof(depreciated_tpl) == 'undefined') {
9857         // new way.. - universal constructor.
9858         Roo.apply(this, config);
9859         this.el  = Roo.get(this.el);
9860     } else {
9861         // old format..
9862         this.el  = Roo.get(config);
9863         this.tpl = depreciated_tpl;
9864         Roo.apply(this, depreciated_config);
9865     }
9866     this.wrapEl  = this.el.wrap().wrap();
9867     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9868     
9869     
9870     if(typeof(this.tpl) == "string"){
9871         this.tpl = new Roo.Template(this.tpl);
9872     } else {
9873         // support xtype ctors..
9874         this.tpl = new Roo.factory(this.tpl, Roo);
9875     }
9876     
9877     
9878     this.tpl.compile();
9879    
9880   
9881     
9882      
9883     /** @private */
9884     this.addEvents({
9885         /**
9886          * @event beforeclick
9887          * Fires before a click is processed. Returns false to cancel the default action.
9888          * @param {Roo.View} this
9889          * @param {Number} index The index of the target node
9890          * @param {HTMLElement} node The target node
9891          * @param {Roo.EventObject} e The raw event object
9892          */
9893             "beforeclick" : true,
9894         /**
9895          * @event click
9896          * Fires when a template node is clicked.
9897          * @param {Roo.View} this
9898          * @param {Number} index The index of the target node
9899          * @param {HTMLElement} node The target node
9900          * @param {Roo.EventObject} e The raw event object
9901          */
9902             "click" : true,
9903         /**
9904          * @event dblclick
9905          * Fires when a template node is double clicked.
9906          * @param {Roo.View} this
9907          * @param {Number} index The index of the target node
9908          * @param {HTMLElement} node The target node
9909          * @param {Roo.EventObject} e The raw event object
9910          */
9911             "dblclick" : true,
9912         /**
9913          * @event contextmenu
9914          * Fires when a template node is right clicked.
9915          * @param {Roo.View} this
9916          * @param {Number} index The index of the target node
9917          * @param {HTMLElement} node The target node
9918          * @param {Roo.EventObject} e The raw event object
9919          */
9920             "contextmenu" : true,
9921         /**
9922          * @event selectionchange
9923          * Fires when the selected nodes change.
9924          * @param {Roo.View} this
9925          * @param {Array} selections Array of the selected nodes
9926          */
9927             "selectionchange" : true,
9928     
9929         /**
9930          * @event beforeselect
9931          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9932          * @param {Roo.View} this
9933          * @param {HTMLElement} node The node to be selected
9934          * @param {Array} selections Array of currently selected nodes
9935          */
9936             "beforeselect" : true,
9937         /**
9938          * @event preparedata
9939          * Fires on every row to render, to allow you to change the data.
9940          * @param {Roo.View} this
9941          * @param {Object} data to be rendered (change this)
9942          */
9943           "preparedata" : true
9944           
9945           
9946         });
9947
9948
9949
9950     this.el.on({
9951         "click": this.onClick,
9952         "dblclick": this.onDblClick,
9953         "contextmenu": this.onContextMenu,
9954         scope:this
9955     });
9956
9957     this.selections = [];
9958     this.nodes = [];
9959     this.cmp = new Roo.CompositeElementLite([]);
9960     if(this.store){
9961         this.store = Roo.factory(this.store, Roo.data);
9962         this.setStore(this.store, true);
9963     }
9964     
9965     if ( this.footer && this.footer.xtype) {
9966            
9967          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9968         
9969         this.footer.dataSource = this.store
9970         this.footer.container = fctr;
9971         this.footer = Roo.factory(this.footer, Roo);
9972         fctr.insertFirst(this.el);
9973         
9974         // this is a bit insane - as the paging toolbar seems to detach the el..
9975 //        dom.parentNode.parentNode.parentNode
9976          // they get detached?
9977     }
9978     
9979     
9980     Roo.View.superclass.constructor.call(this);
9981     
9982     
9983 };
9984
9985 Roo.extend(Roo.View, Roo.util.Observable, {
9986     
9987      /**
9988      * @cfg {Roo.data.Store} store Data store to load data from.
9989      */
9990     store : false,
9991     
9992     /**
9993      * @cfg {String|Roo.Element} el The container element.
9994      */
9995     el : '',
9996     
9997     /**
9998      * @cfg {String|Roo.Template} tpl The template used by this View 
9999      */
10000     tpl : false,
10001     /**
10002      * @cfg {String} dataName the named area of the template to use as the data area
10003      *                          Works with domtemplates roo-name="name"
10004      */
10005     dataName: false,
10006     /**
10007      * @cfg {String} selectedClass The css class to add to selected nodes
10008      */
10009     selectedClass : "x-view-selected",
10010      /**
10011      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10012      */
10013     emptyText : "",
10014     
10015     /**
10016      * @cfg {String} text to display on mask (default Loading)
10017      */
10018     mask : false,
10019     /**
10020      * @cfg {Boolean} multiSelect Allow multiple selection
10021      */
10022     multiSelect : false,
10023     /**
10024      * @cfg {Boolean} singleSelect Allow single selection
10025      */
10026     singleSelect:  false,
10027     
10028     /**
10029      * @cfg {Boolean} toggleSelect - selecting 
10030      */
10031     toggleSelect : false,
10032     
10033     /**
10034      * Returns the element this view is bound to.
10035      * @return {Roo.Element}
10036      */
10037     getEl : function(){
10038         return this.wrapEl;
10039     },
10040     
10041     
10042
10043     /**
10044      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10045      */
10046     refresh : function(){
10047         Roo.log('refresh');
10048         var t = this.tpl;
10049         
10050         // if we are using something like 'domtemplate', then
10051         // the what gets used is:
10052         // t.applySubtemplate(NAME, data, wrapping data..)
10053         // the outer template then get' applied with
10054         //     the store 'extra data'
10055         // and the body get's added to the
10056         //      roo-name="data" node?
10057         //      <span class='roo-tpl-{name}'></span> ?????
10058         
10059         
10060         
10061         this.clearSelections();
10062         this.el.update("");
10063         var html = [];
10064         var records = this.store.getRange();
10065         if(records.length < 1) {
10066             
10067             // is this valid??  = should it render a template??
10068             
10069             this.el.update(this.emptyText);
10070             return;
10071         }
10072         var el = this.el;
10073         if (this.dataName) {
10074             this.el.update(t.apply(this.store.meta)); //????
10075             el = this.el.child('.roo-tpl-' + this.dataName);
10076         }
10077         
10078         for(var i = 0, len = records.length; i < len; i++){
10079             var data = this.prepareData(records[i].data, i, records[i]);
10080             this.fireEvent("preparedata", this, data, i, records[i]);
10081             html[html.length] = Roo.util.Format.trim(
10082                 this.dataName ?
10083                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10084                     t.apply(data)
10085             );
10086         }
10087         
10088         
10089         
10090         el.update(html.join(""));
10091         this.nodes = el.dom.childNodes;
10092         this.updateIndexes(0);
10093     },
10094     
10095
10096     /**
10097      * Function to override to reformat the data that is sent to
10098      * the template for each node.
10099      * DEPRICATED - use the preparedata event handler.
10100      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10101      * a JSON object for an UpdateManager bound view).
10102      */
10103     prepareData : function(data, index, record)
10104     {
10105         this.fireEvent("preparedata", this, data, index, record);
10106         return data;
10107     },
10108
10109     onUpdate : function(ds, record){
10110          Roo.log('on update');   
10111         this.clearSelections();
10112         var index = this.store.indexOf(record);
10113         var n = this.nodes[index];
10114         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10115         n.parentNode.removeChild(n);
10116         this.updateIndexes(index, index);
10117     },
10118
10119     
10120     
10121 // --------- FIXME     
10122     onAdd : function(ds, records, index)
10123     {
10124         Roo.log(['on Add', ds, records, index] );        
10125         this.clearSelections();
10126         if(this.nodes.length == 0){
10127             this.refresh();
10128             return;
10129         }
10130         var n = this.nodes[index];
10131         for(var i = 0, len = records.length; i < len; i++){
10132             var d = this.prepareData(records[i].data, i, records[i]);
10133             if(n){
10134                 this.tpl.insertBefore(n, d);
10135             }else{
10136                 
10137                 this.tpl.append(this.el, d);
10138             }
10139         }
10140         this.updateIndexes(index);
10141     },
10142
10143     onRemove : function(ds, record, index){
10144         Roo.log('onRemove');
10145         this.clearSelections();
10146         var el = this.dataName  ?
10147             this.el.child('.roo-tpl-' + this.dataName) :
10148             this.el; 
10149         
10150         el.dom.removeChild(this.nodes[index]);
10151         this.updateIndexes(index);
10152     },
10153
10154     /**
10155      * Refresh an individual node.
10156      * @param {Number} index
10157      */
10158     refreshNode : function(index){
10159         this.onUpdate(this.store, this.store.getAt(index));
10160     },
10161
10162     updateIndexes : function(startIndex, endIndex){
10163         var ns = this.nodes;
10164         startIndex = startIndex || 0;
10165         endIndex = endIndex || ns.length - 1;
10166         for(var i = startIndex; i <= endIndex; i++){
10167             ns[i].nodeIndex = i;
10168         }
10169     },
10170
10171     /**
10172      * Changes the data store this view uses and refresh the view.
10173      * @param {Store} store
10174      */
10175     setStore : function(store, initial){
10176         if(!initial && this.store){
10177             this.store.un("datachanged", this.refresh);
10178             this.store.un("add", this.onAdd);
10179             this.store.un("remove", this.onRemove);
10180             this.store.un("update", this.onUpdate);
10181             this.store.un("clear", this.refresh);
10182             this.store.un("beforeload", this.onBeforeLoad);
10183             this.store.un("load", this.onLoad);
10184             this.store.un("loadexception", this.onLoad);
10185         }
10186         if(store){
10187           
10188             store.on("datachanged", this.refresh, this);
10189             store.on("add", this.onAdd, this);
10190             store.on("remove", this.onRemove, this);
10191             store.on("update", this.onUpdate, this);
10192             store.on("clear", this.refresh, this);
10193             store.on("beforeload", this.onBeforeLoad, this);
10194             store.on("load", this.onLoad, this);
10195             store.on("loadexception", this.onLoad, this);
10196         }
10197         
10198         if(store){
10199             this.refresh();
10200         }
10201     },
10202     /**
10203      * onbeforeLoad - masks the loading area.
10204      *
10205      */
10206     onBeforeLoad : function(store,opts)
10207     {
10208          Roo.log('onBeforeLoad');   
10209         if (!opts.add) {
10210             this.el.update("");
10211         }
10212         this.el.mask(this.mask ? this.mask : "Loading" ); 
10213     },
10214     onLoad : function ()
10215     {
10216         this.el.unmask();
10217     },
10218     
10219
10220     /**
10221      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10222      * @param {HTMLElement} node
10223      * @return {HTMLElement} The template node
10224      */
10225     findItemFromChild : function(node){
10226         var el = this.dataName  ?
10227             this.el.child('.roo-tpl-' + this.dataName,true) :
10228             this.el.dom; 
10229         
10230         if(!node || node.parentNode == el){
10231                     return node;
10232             }
10233             var p = node.parentNode;
10234             while(p && p != el){
10235             if(p.parentNode == el){
10236                 return p;
10237             }
10238             p = p.parentNode;
10239         }
10240             return null;
10241     },
10242
10243     /** @ignore */
10244     onClick : function(e){
10245         var item = this.findItemFromChild(e.getTarget());
10246         if(item){
10247             var index = this.indexOf(item);
10248             if(this.onItemClick(item, index, e) !== false){
10249                 this.fireEvent("click", this, index, item, e);
10250             }
10251         }else{
10252             this.clearSelections();
10253         }
10254     },
10255
10256     /** @ignore */
10257     onContextMenu : function(e){
10258         var item = this.findItemFromChild(e.getTarget());
10259         if(item){
10260             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10261         }
10262     },
10263
10264     /** @ignore */
10265     onDblClick : function(e){
10266         var item = this.findItemFromChild(e.getTarget());
10267         if(item){
10268             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10269         }
10270     },
10271
10272     onItemClick : function(item, index, e)
10273     {
10274         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10275             return false;
10276         }
10277         if (this.toggleSelect) {
10278             var m = this.isSelected(item) ? 'unselect' : 'select';
10279             Roo.log(m);
10280             var _t = this;
10281             _t[m](item, true, false);
10282             return true;
10283         }
10284         if(this.multiSelect || this.singleSelect){
10285             if(this.multiSelect && e.shiftKey && this.lastSelection){
10286                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10287             }else{
10288                 this.select(item, this.multiSelect && e.ctrlKey);
10289                 this.lastSelection = item;
10290             }
10291             e.preventDefault();
10292         }
10293         return true;
10294     },
10295
10296     /**
10297      * Get the number of selected nodes.
10298      * @return {Number}
10299      */
10300     getSelectionCount : function(){
10301         return this.selections.length;
10302     },
10303
10304     /**
10305      * Get the currently selected nodes.
10306      * @return {Array} An array of HTMLElements
10307      */
10308     getSelectedNodes : function(){
10309         return this.selections;
10310     },
10311
10312     /**
10313      * Get the indexes of the selected nodes.
10314      * @return {Array}
10315      */
10316     getSelectedIndexes : function(){
10317         var indexes = [], s = this.selections;
10318         for(var i = 0, len = s.length; i < len; i++){
10319             indexes.push(s[i].nodeIndex);
10320         }
10321         return indexes;
10322     },
10323
10324     /**
10325      * Clear all selections
10326      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10327      */
10328     clearSelections : function(suppressEvent){
10329         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10330             this.cmp.elements = this.selections;
10331             this.cmp.removeClass(this.selectedClass);
10332             this.selections = [];
10333             if(!suppressEvent){
10334                 this.fireEvent("selectionchange", this, this.selections);
10335             }
10336         }
10337     },
10338
10339     /**
10340      * Returns true if the passed node is selected
10341      * @param {HTMLElement/Number} node The node or node index
10342      * @return {Boolean}
10343      */
10344     isSelected : function(node){
10345         var s = this.selections;
10346         if(s.length < 1){
10347             return false;
10348         }
10349         node = this.getNode(node);
10350         return s.indexOf(node) !== -1;
10351     },
10352
10353     /**
10354      * Selects nodes.
10355      * @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
10356      * @param {Boolean} keepExisting (optional) true to keep existing selections
10357      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10358      */
10359     select : function(nodeInfo, keepExisting, suppressEvent){
10360         if(nodeInfo instanceof Array){
10361             if(!keepExisting){
10362                 this.clearSelections(true);
10363             }
10364             for(var i = 0, len = nodeInfo.length; i < len; i++){
10365                 this.select(nodeInfo[i], true, true);
10366             }
10367             return;
10368         } 
10369         var node = this.getNode(nodeInfo);
10370         if(!node || this.isSelected(node)){
10371             return; // already selected.
10372         }
10373         if(!keepExisting){
10374             this.clearSelections(true);
10375         }
10376         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10377             Roo.fly(node).addClass(this.selectedClass);
10378             this.selections.push(node);
10379             if(!suppressEvent){
10380                 this.fireEvent("selectionchange", this, this.selections);
10381             }
10382         }
10383         
10384         
10385     },
10386       /**
10387      * Unselects nodes.
10388      * @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
10389      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10390      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10391      */
10392     unselect : function(nodeInfo, keepExisting, suppressEvent)
10393     {
10394         if(nodeInfo instanceof Array){
10395             Roo.each(this.selections, function(s) {
10396                 this.unselect(s, nodeInfo);
10397             }, this);
10398             return;
10399         }
10400         var node = this.getNode(nodeInfo);
10401         if(!node || !this.isSelected(node)){
10402             Roo.log("not selected");
10403             return; // not selected.
10404         }
10405         // fireevent???
10406         var ns = [];
10407         Roo.each(this.selections, function(s) {
10408             if (s == node ) {
10409                 Roo.fly(node).removeClass(this.selectedClass);
10410
10411                 return;
10412             }
10413             ns.push(s);
10414         },this);
10415         
10416         this.selections= ns;
10417         this.fireEvent("selectionchange", this, this.selections);
10418     },
10419
10420     /**
10421      * Gets a template node.
10422      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10423      * @return {HTMLElement} The node or null if it wasn't found
10424      */
10425     getNode : function(nodeInfo){
10426         if(typeof nodeInfo == "string"){
10427             return document.getElementById(nodeInfo);
10428         }else if(typeof nodeInfo == "number"){
10429             return this.nodes[nodeInfo];
10430         }
10431         return nodeInfo;
10432     },
10433
10434     /**
10435      * Gets a range template nodes.
10436      * @param {Number} startIndex
10437      * @param {Number} endIndex
10438      * @return {Array} An array of nodes
10439      */
10440     getNodes : function(start, end){
10441         var ns = this.nodes;
10442         start = start || 0;
10443         end = typeof end == "undefined" ? ns.length - 1 : end;
10444         var nodes = [];
10445         if(start <= end){
10446             for(var i = start; i <= end; i++){
10447                 nodes.push(ns[i]);
10448             }
10449         } else{
10450             for(var i = start; i >= end; i--){
10451                 nodes.push(ns[i]);
10452             }
10453         }
10454         return nodes;
10455     },
10456
10457     /**
10458      * Finds the index of the passed node
10459      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10460      * @return {Number} The index of the node or -1
10461      */
10462     indexOf : function(node){
10463         node = this.getNode(node);
10464         if(typeof node.nodeIndex == "number"){
10465             return node.nodeIndex;
10466         }
10467         var ns = this.nodes;
10468         for(var i = 0, len = ns.length; i < len; i++){
10469             if(ns[i] == node){
10470                 return i;
10471             }
10472         }
10473         return -1;
10474     }
10475 });
10476 /*
10477  * - LGPL
10478  *
10479  * based on jquery fullcalendar
10480  * 
10481  */
10482
10483 Roo.bootstrap = Roo.bootstrap || {};
10484 /**
10485  * @class Roo.bootstrap.Calendar
10486  * @extends Roo.bootstrap.Component
10487  * Bootstrap Calendar class
10488  * @cfg {Boolean} loadMask (true|false) default false
10489  * @cfg {Object} header generate the user specific header of the calendar, default false
10490
10491  * @constructor
10492  * Create a new Container
10493  * @param {Object} config The config object
10494  */
10495
10496
10497
10498 Roo.bootstrap.Calendar = function(config){
10499     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10500      this.addEvents({
10501         /**
10502              * @event select
10503              * Fires when a date is selected
10504              * @param {DatePicker} this
10505              * @param {Date} date The selected date
10506              */
10507         'select': true,
10508         /**
10509              * @event monthchange
10510              * Fires when the displayed month changes 
10511              * @param {DatePicker} this
10512              * @param {Date} date The selected month
10513              */
10514         'monthchange': true,
10515         /**
10516              * @event evententer
10517              * Fires when mouse over an event
10518              * @param {Calendar} this
10519              * @param {event} Event
10520              */
10521         'evententer': true,
10522         /**
10523              * @event eventleave
10524              * Fires when the mouse leaves an
10525              * @param {Calendar} this
10526              * @param {event}
10527              */
10528         'eventleave': true,
10529         /**
10530              * @event eventclick
10531              * Fires when the mouse click an
10532              * @param {Calendar} this
10533              * @param {event}
10534              */
10535         'eventclick': true
10536         
10537     });
10538
10539 };
10540
10541 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10542     
10543      /**
10544      * @cfg {Number} startDay
10545      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10546      */
10547     startDay : 0,
10548     
10549     loadMask : false,
10550     
10551     header : false,
10552       
10553     getAutoCreate : function(){
10554         
10555         
10556         var fc_button = function(name, corner, style, content ) {
10557             return Roo.apply({},{
10558                 tag : 'span',
10559                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10560                          (corner.length ?
10561                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10562                             ''
10563                         ),
10564                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10565                 unselectable: 'on'
10566             });
10567         };
10568         
10569         var header = {};
10570         
10571         if(!this.header){
10572             header = {
10573                 tag : 'table',
10574                 cls : 'fc-header',
10575                 style : 'width:100%',
10576                 cn : [
10577                     {
10578                         tag: 'tr',
10579                         cn : [
10580                             {
10581                                 tag : 'td',
10582                                 cls : 'fc-header-left',
10583                                 cn : [
10584                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
10585                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
10586                                     { tag: 'span', cls: 'fc-header-space' },
10587                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
10588
10589
10590                                 ]
10591                             },
10592
10593                             {
10594                                 tag : 'td',
10595                                 cls : 'fc-header-center',
10596                                 cn : [
10597                                     {
10598                                         tag: 'span',
10599                                         cls: 'fc-header-title',
10600                                         cn : {
10601                                             tag: 'H2',
10602                                             html : 'month / year'
10603                                         }
10604                                     }
10605
10606                                 ]
10607                             },
10608                             {
10609                                 tag : 'td',
10610                                 cls : 'fc-header-right',
10611                                 cn : [
10612                               /*      fc_button('month', 'left', '', 'month' ),
10613                                     fc_button('week', '', '', 'week' ),
10614                                     fc_button('day', 'right', '', 'day' )
10615                                 */    
10616
10617                                 ]
10618                             }
10619
10620                         ]
10621                     }
10622                 ]
10623             };
10624         }
10625         
10626         header = this.header;
10627         
10628        
10629         var cal_heads = function() {
10630             var ret = [];
10631             // fixme - handle this.
10632             
10633             for (var i =0; i < Date.dayNames.length; i++) {
10634                 var d = Date.dayNames[i];
10635                 ret.push({
10636                     tag: 'th',
10637                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10638                     html : d.substring(0,3)
10639                 });
10640                 
10641             }
10642             ret[0].cls += ' fc-first';
10643             ret[6].cls += ' fc-last';
10644             return ret;
10645         };
10646         var cal_cell = function(n) {
10647             return  {
10648                 tag: 'td',
10649                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10650                 cn : [
10651                     {
10652                         cn : [
10653                             {
10654                                 cls: 'fc-day-number',
10655                                 html: 'D'
10656                             },
10657                             {
10658                                 cls: 'fc-day-content',
10659                              
10660                                 cn : [
10661                                      {
10662                                         style: 'position: relative;' // height: 17px;
10663                                     }
10664                                 ]
10665                             }
10666                             
10667                             
10668                         ]
10669                     }
10670                 ]
10671                 
10672             }
10673         };
10674         var cal_rows = function() {
10675             
10676             var ret = []
10677             for (var r = 0; r < 6; r++) {
10678                 var row= {
10679                     tag : 'tr',
10680                     cls : 'fc-week',
10681                     cn : []
10682                 };
10683                 
10684                 for (var i =0; i < Date.dayNames.length; i++) {
10685                     var d = Date.dayNames[i];
10686                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10687
10688                 }
10689                 row.cn[0].cls+=' fc-first';
10690                 row.cn[0].cn[0].style = 'min-height:90px';
10691                 row.cn[6].cls+=' fc-last';
10692                 ret.push(row);
10693                 
10694             }
10695             ret[0].cls += ' fc-first';
10696             ret[4].cls += ' fc-prev-last';
10697             ret[5].cls += ' fc-last';
10698             return ret;
10699             
10700         };
10701         
10702         var cal_table = {
10703             tag: 'table',
10704             cls: 'fc-border-separate',
10705             style : 'width:100%',
10706             cellspacing  : 0,
10707             cn : [
10708                 { 
10709                     tag: 'thead',
10710                     cn : [
10711                         { 
10712                             tag: 'tr',
10713                             cls : 'fc-first fc-last',
10714                             cn : cal_heads()
10715                         }
10716                     ]
10717                 },
10718                 { 
10719                     tag: 'tbody',
10720                     cn : cal_rows()
10721                 }
10722                   
10723             ]
10724         };
10725          
10726          var cfg = {
10727             cls : 'fc fc-ltr',
10728             cn : [
10729                 header,
10730                 {
10731                     cls : 'fc-content',
10732                     style : "position: relative;",
10733                     cn : [
10734                         {
10735                             cls : 'fc-view fc-view-month fc-grid',
10736                             style : 'position: relative',
10737                             unselectable : 'on',
10738                             cn : [
10739                                 {
10740                                     cls : 'fc-event-container',
10741                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10742                                 },
10743                                 cal_table
10744                             ]
10745                         }
10746                     ]
10747     
10748                 }
10749            ] 
10750             
10751         };
10752         
10753          
10754         
10755         return cfg;
10756     },
10757     
10758     
10759     initEvents : function()
10760     {
10761         if(!this.store){
10762             throw "can not find store for calendar";
10763         }
10764         
10765         var mark = {
10766             tag: "div",
10767             cls:"x-dlg-mask",
10768             style: "text-align:center",
10769             cn: [
10770                 {
10771                     tag: "div",
10772                     style: "background-color:white;width:50%;margin:250 auto",
10773                     cn: [
10774                         {
10775                             tag: "img",
10776                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10777                         },
10778                         {
10779                             tag: "span",
10780                             html: "Loading"
10781                         }
10782                         
10783                     ]
10784                 }
10785             ]
10786         }
10787         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10788         
10789         var size = this.el.select('.fc-content', true).first().getSize();
10790         this.maskEl.setSize(size.width, size.height);
10791         this.maskEl.enableDisplayMode("block");
10792         if(!this.loadMask){
10793             this.maskEl.hide();
10794         }
10795         
10796         this.store = Roo.factory(this.store, Roo.data);
10797         this.store.on('load', this.onLoad, this);
10798         this.store.on('beforeload', this.onBeforeLoad, this);
10799         
10800         this.resize();
10801         
10802         this.cells = this.el.select('.fc-day',true);
10803         //Roo.log(this.cells);
10804         this.textNodes = this.el.query('.fc-day-number');
10805         this.cells.addClassOnOver('fc-state-hover');
10806         
10807         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10808         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10809         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10810         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10811         
10812         this.on('monthchange', this.onMonthChange, this);
10813         
10814         this.update(new Date().clearTime());
10815     },
10816     
10817     resize : function() {
10818         var sz  = this.el.getSize();
10819         
10820         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10821         this.el.select('.fc-day-content div',true).setHeight(34);
10822     },
10823     
10824     
10825     // private
10826     showPrevMonth : function(e){
10827         this.update(this.activeDate.add("mo", -1));
10828     },
10829     showToday : function(e){
10830         this.update(new Date().clearTime());
10831     },
10832     // private
10833     showNextMonth : function(e){
10834         this.update(this.activeDate.add("mo", 1));
10835     },
10836
10837     // private
10838     showPrevYear : function(){
10839         this.update(this.activeDate.add("y", -1));
10840     },
10841
10842     // private
10843     showNextYear : function(){
10844         this.update(this.activeDate.add("y", 1));
10845     },
10846
10847     
10848    // private
10849     update : function(date)
10850     {
10851         var vd = this.activeDate;
10852         this.activeDate = date;
10853 //        if(vd && this.el){
10854 //            var t = date.getTime();
10855 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10856 //                Roo.log('using add remove');
10857 //                
10858 //                this.fireEvent('monthchange', this, date);
10859 //                
10860 //                this.cells.removeClass("fc-state-highlight");
10861 //                this.cells.each(function(c){
10862 //                   if(c.dateValue == t){
10863 //                       c.addClass("fc-state-highlight");
10864 //                       setTimeout(function(){
10865 //                            try{c.dom.firstChild.focus();}catch(e){}
10866 //                       }, 50);
10867 //                       return false;
10868 //                   }
10869 //                   return true;
10870 //                });
10871 //                return;
10872 //            }
10873 //        }
10874         
10875         var days = date.getDaysInMonth();
10876         
10877         var firstOfMonth = date.getFirstDateOfMonth();
10878         var startingPos = firstOfMonth.getDay()-this.startDay;
10879         
10880         if(startingPos < this.startDay){
10881             startingPos += 7;
10882         }
10883         
10884         var pm = date.add(Date.MONTH, -1);
10885         var prevStart = pm.getDaysInMonth()-startingPos;
10886 //        
10887         this.cells = this.el.select('.fc-day',true);
10888         this.textNodes = this.el.query('.fc-day-number');
10889         this.cells.addClassOnOver('fc-state-hover');
10890         
10891         var cells = this.cells.elements;
10892         var textEls = this.textNodes;
10893         
10894         Roo.each(cells, function(cell){
10895             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10896         });
10897         
10898         days += startingPos;
10899
10900         // convert everything to numbers so it's fast
10901         var day = 86400000;
10902         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10903         //Roo.log(d);
10904         //Roo.log(pm);
10905         //Roo.log(prevStart);
10906         
10907         var today = new Date().clearTime().getTime();
10908         var sel = date.clearTime().getTime();
10909         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10910         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10911         var ddMatch = this.disabledDatesRE;
10912         var ddText = this.disabledDatesText;
10913         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10914         var ddaysText = this.disabledDaysText;
10915         var format = this.format;
10916         
10917         var setCellClass = function(cal, cell){
10918             
10919             //Roo.log('set Cell Class');
10920             cell.title = "";
10921             var t = d.getTime();
10922             
10923             //Roo.log(d);
10924             
10925             cell.dateValue = t;
10926             if(t == today){
10927                 cell.className += " fc-today";
10928                 cell.className += " fc-state-highlight";
10929                 cell.title = cal.todayText;
10930             }
10931             if(t == sel){
10932                 // disable highlight in other month..
10933                 //cell.className += " fc-state-highlight";
10934                 
10935             }
10936             // disabling
10937             if(t < min) {
10938                 cell.className = " fc-state-disabled";
10939                 cell.title = cal.minText;
10940                 return;
10941             }
10942             if(t > max) {
10943                 cell.className = " fc-state-disabled";
10944                 cell.title = cal.maxText;
10945                 return;
10946             }
10947             if(ddays){
10948                 if(ddays.indexOf(d.getDay()) != -1){
10949                     cell.title = ddaysText;
10950                     cell.className = " fc-state-disabled";
10951                 }
10952             }
10953             if(ddMatch && format){
10954                 var fvalue = d.dateFormat(format);
10955                 if(ddMatch.test(fvalue)){
10956                     cell.title = ddText.replace("%0", fvalue);
10957                     cell.className = " fc-state-disabled";
10958                 }
10959             }
10960             
10961             if (!cell.initialClassName) {
10962                 cell.initialClassName = cell.dom.className;
10963             }
10964             
10965             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10966         };
10967
10968         var i = 0;
10969         
10970         for(; i < startingPos; i++) {
10971             textEls[i].innerHTML = (++prevStart);
10972             d.setDate(d.getDate()+1);
10973             
10974             cells[i].className = "fc-past fc-other-month";
10975             setCellClass(this, cells[i]);
10976         }
10977         
10978         var intDay = 0;
10979         
10980         for(; i < days; i++){
10981             intDay = i - startingPos + 1;
10982             textEls[i].innerHTML = (intDay);
10983             d.setDate(d.getDate()+1);
10984             
10985             cells[i].className = ''; // "x-date-active";
10986             setCellClass(this, cells[i]);
10987         }
10988         var extraDays = 0;
10989         
10990         for(; i < 42; i++) {
10991             textEls[i].innerHTML = (++extraDays);
10992             d.setDate(d.getDate()+1);
10993             
10994             cells[i].className = "fc-future fc-other-month";
10995             setCellClass(this, cells[i]);
10996         }
10997         
10998         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10999         
11000         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11001         
11002         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11003         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11004         
11005         if(totalRows != 6){
11006             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11007             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11008         }
11009         
11010         this.fireEvent('monthchange', this, date);
11011         
11012         
11013         /*
11014         if(!this.internalRender){
11015             var main = this.el.dom.firstChild;
11016             var w = main.offsetWidth;
11017             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11018             Roo.fly(main).setWidth(w);
11019             this.internalRender = true;
11020             // opera does not respect the auto grow header center column
11021             // then, after it gets a width opera refuses to recalculate
11022             // without a second pass
11023             if(Roo.isOpera && !this.secondPass){
11024                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11025                 this.secondPass = true;
11026                 this.update.defer(10, this, [date]);
11027             }
11028         }
11029         */
11030         
11031     },
11032     
11033     findCell : function(dt) {
11034         dt = dt.clearTime().getTime();
11035         var ret = false;
11036         this.cells.each(function(c){
11037             //Roo.log("check " +c.dateValue + '?=' + dt);
11038             if(c.dateValue == dt){
11039                 ret = c;
11040                 return false;
11041             }
11042             return true;
11043         });
11044         
11045         return ret;
11046     },
11047     
11048     findCells : function(ev) {
11049         var s = ev.start.clone().clearTime().getTime();
11050        // Roo.log(s);
11051         var e= ev.end.clone().clearTime().getTime();
11052        // Roo.log(e);
11053         var ret = [];
11054         this.cells.each(function(c){
11055              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11056             
11057             if(c.dateValue > e){
11058                 return ;
11059             }
11060             if(c.dateValue < s){
11061                 return ;
11062             }
11063             ret.push(c);
11064         });
11065         
11066         return ret;    
11067     },
11068     
11069     findBestRow: function(cells)
11070     {
11071         var ret = 0;
11072         
11073         for (var i =0 ; i < cells.length;i++) {
11074             ret  = Math.max(cells[i].rows || 0,ret);
11075         }
11076         return ret;
11077         
11078     },
11079     
11080     
11081     addItem : function(ev)
11082     {
11083         // look for vertical location slot in
11084         var cells = this.findCells(ev);
11085         
11086         ev.row = this.findBestRow(cells);
11087         
11088         // work out the location.
11089         
11090         var crow = false;
11091         var rows = [];
11092         for(var i =0; i < cells.length; i++) {
11093             if (!crow) {
11094                 crow = {
11095                     start : cells[i],
11096                     end :  cells[i]
11097                 };
11098                 continue;
11099             }
11100             if (crow.start.getY() == cells[i].getY()) {
11101                 // on same row.
11102                 crow.end = cells[i];
11103                 continue;
11104             }
11105             // different row.
11106             rows.push(crow);
11107             crow = {
11108                 start: cells[i],
11109                 end : cells[i]
11110             };
11111             
11112         }
11113         
11114         rows.push(crow);
11115         ev.els = [];
11116         ev.rows = rows;
11117         ev.cells = cells;
11118         for (var i = 0; i < cells.length;i++) {
11119             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11120             
11121         }
11122         
11123         this.calevents.push(ev);
11124     },
11125     
11126     clearEvents: function() {
11127         
11128         if(!this.calevents){
11129             return;
11130         }
11131         
11132         Roo.each(this.cells.elements, function(c){
11133             c.rows = 0;
11134         });
11135         
11136         Roo.each(this.calevents, function(e) {
11137             Roo.each(e.els, function(el) {
11138                 el.un('mouseenter' ,this.onEventEnter, this);
11139                 el.un('mouseleave' ,this.onEventLeave, this);
11140                 el.remove();
11141             },this);
11142         },this);
11143         
11144     },
11145     
11146     renderEvents: function()
11147     {   
11148         // first make sure there is enough space..
11149         
11150         this.cells.each(function(c) {
11151         
11152             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11153         });
11154         
11155         for (var e = 0; e < this.calevents.length; e++) {
11156             var ev = this.calevents[e];
11157             var cells = ev.cells;
11158             var rows = ev.rows;
11159             
11160             for(var i =0; i < rows.length; i++) {
11161                 
11162                  
11163                 // how many rows should it span..
11164                 
11165                 var  cfg = {
11166                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11167                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11168                     
11169                     unselectable : "on",
11170                     cn : [
11171                         {
11172                             cls: 'fc-event-inner',
11173                             cn : [
11174 //                                {
11175 //                                  tag:'span',
11176 //                                  cls: 'fc-event-time',
11177 //                                  html : cells.length > 1 ? '' : ev.time
11178 //                                },
11179                                 {
11180                                   tag:'span',
11181                                   cls: 'fc-event-title',
11182                                   html : String.format('{0}', ev.title)
11183                                 }
11184                                 
11185                                 
11186                             ]
11187                         },
11188                         {
11189                             cls: 'ui-resizable-handle ui-resizable-e',
11190                             html : '&nbsp;&nbsp;&nbsp'
11191                         }
11192                         
11193                     ]
11194                 };
11195                 if (i == 0) {
11196                     cfg.cls += ' fc-event-start';
11197                 }
11198                 if ((i+1) == rows.length) {
11199                     cfg.cls += ' fc-event-end';
11200                 }
11201                 
11202                 var ctr = this.el.select('.fc-event-container',true).first();
11203                 var cg = ctr.createChild(cfg);
11204                 
11205                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11206                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11207                 cg.on('click', this.onEventClick, this, ev);
11208                 
11209                 ev.els.push(cg);
11210                 
11211                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11212                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11213                 //Roo.log(cg);
11214                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11215                 cg.setWidth(ebox.right - sbox.x -2);
11216             }
11217             
11218             
11219         }
11220         
11221     },
11222     
11223     onEventEnter: function (e, el,event,d) {
11224         this.fireEvent('evententer', this, el, event);
11225     },
11226     
11227     onEventLeave: function (e, el,event,d) {
11228         this.fireEvent('eventleave', this, el, event);
11229     },
11230     
11231     onEventClick: function (e, el,event,d) {
11232         this.fireEvent('eventclick', this, el, event);
11233     },
11234     
11235     onMonthChange: function () {
11236         this.store.load();
11237     },
11238     
11239     onLoad: function () 
11240     {   
11241         this.calevents = [];
11242         var cal = this;
11243         
11244         if(this.store.getCount() > 0){
11245             this.store.data.each(function(d){
11246                cal.addItem({
11247                     id : d.data.id,
11248                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11249                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11250                     time : d.data.start_time,
11251                     title : d.data.title,
11252                     description : d.data.description,
11253                     venue : d.data.venue
11254                 });
11255             });
11256         }
11257         
11258         this.renderEvents();
11259         
11260         if(this.loadMask){
11261             this.maskEl.hide();
11262         }
11263     },
11264     
11265     onBeforeLoad: function()
11266     {
11267         this.clearEvents();
11268         
11269         if(this.loadMask){
11270             this.maskEl.show();
11271         }
11272     }
11273 });
11274
11275  
11276  /*
11277  * - LGPL
11278  *
11279  * element
11280  * 
11281  */
11282
11283 /**
11284  * @class Roo.bootstrap.Popover
11285  * @extends Roo.bootstrap.Component
11286  * Bootstrap Popover class
11287  * @cfg {String} html contents of the popover   (or false to use children..)
11288  * @cfg {String} title of popover (or false to hide)
11289  * @cfg {String} placement how it is placed
11290  * @cfg {String} trigger click || hover (or false to trigger manually)
11291  * @cfg {String} over what (parent or false to trigger manually.)
11292  * 
11293  * @constructor
11294  * Create a new Popover
11295  * @param {Object} config The config object
11296  */
11297
11298 Roo.bootstrap.Popover = function(config){
11299     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11300 };
11301
11302 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11303     
11304     title: 'Fill in a title',
11305     html: false,
11306     
11307     placement : 'right',
11308     trigger : 'hover', // hover
11309     
11310     over: 'parent',
11311     
11312     can_build_overlaid : false,
11313     
11314     getChildContainer : function()
11315     {
11316         return this.el.select('.popover-content',true).first();
11317     },
11318     
11319     getAutoCreate : function(){
11320          Roo.log('make popover?');
11321         var cfg = {
11322            cls : 'popover roo-dynamic',
11323            style: 'display:block',
11324            cn : [
11325                 {
11326                     cls : 'arrow'
11327                 },
11328                 {
11329                     cls : 'popover-inner',
11330                     cn : [
11331                         {
11332                             tag: 'h3',
11333                             cls: 'popover-title',
11334                             html : this.title
11335                         },
11336                         {
11337                             cls : 'popover-content',
11338                             html : this.html
11339                         }
11340                     ]
11341                     
11342                 }
11343            ]
11344         };
11345         
11346         return cfg;
11347     },
11348     setTitle: function(str)
11349     {
11350         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11351     },
11352     setContent: function(str)
11353     {
11354         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11355     },
11356     // as it get's added to the bottom of the page.
11357     onRender : function(ct, position)
11358     {
11359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11360         if(!this.el){
11361             var cfg = Roo.apply({},  this.getAutoCreate());
11362             cfg.id = Roo.id();
11363             
11364             if (this.cls) {
11365                 cfg.cls += ' ' + this.cls;
11366             }
11367             if (this.style) {
11368                 cfg.style = this.style;
11369             }
11370             Roo.log("adding to ")
11371             this.el = Roo.get(document.body).createChild(cfg, position);
11372             Roo.log(this.el);
11373         }
11374         this.initEvents();
11375     },
11376     
11377     initEvents : function()
11378     {
11379         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11380         this.el.enableDisplayMode('block');
11381         this.el.hide();
11382         if (this.over === false) {
11383             return; 
11384         }
11385         if (this.triggers === false) {
11386             return;
11387         }
11388         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11389         var triggers = this.trigger ? this.trigger.split(' ') : [];
11390         Roo.each(triggers, function(trigger) {
11391         
11392             if (trigger == 'click') {
11393                 on_el.on('click', this.toggle, this);
11394             } else if (trigger != 'manual') {
11395                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11396                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11397       
11398                 on_el.on(eventIn  ,this.enter, this);
11399                 on_el.on(eventOut, this.leave, this);
11400             }
11401         }, this);
11402         
11403     },
11404     
11405     
11406     // private
11407     timeout : null,
11408     hoverState : null,
11409     
11410     toggle : function () {
11411         this.hoverState == 'in' ? this.leave() : this.enter();
11412     },
11413     
11414     enter : function () {
11415        
11416     
11417         clearTimeout(this.timeout);
11418     
11419         this.hoverState = 'in'
11420     
11421         if (!this.delay || !this.delay.show) {
11422             this.show();
11423             return 
11424         }
11425         var _t = this;
11426         this.timeout = setTimeout(function () {
11427             if (_t.hoverState == 'in') {
11428                 _t.show();
11429             }
11430         }, this.delay.show)
11431     },
11432     leave : function() {
11433         clearTimeout(this.timeout);
11434     
11435         this.hoverState = 'out'
11436     
11437         if (!this.delay || !this.delay.hide) {
11438             this.hide();
11439             return 
11440         }
11441         var _t = this;
11442         this.timeout = setTimeout(function () {
11443             if (_t.hoverState == 'out') {
11444                 _t.hide();
11445             }
11446         }, this.delay.hide)
11447     },
11448     
11449     show : function (on_el)
11450     {
11451         if (!on_el) {
11452             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11453         }
11454         // set content.
11455         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11456         if (this.html !== false) {
11457             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11458         }
11459         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11460         if (!this.title.length) {
11461             this.el.select('.popover-title',true).hide();
11462         }
11463         
11464         var placement = typeof this.placement == 'function' ?
11465             this.placement.call(this, this.el, on_el) :
11466             this.placement;
11467             
11468         var autoToken = /\s?auto?\s?/i;
11469         var autoPlace = autoToken.test(placement);
11470         if (autoPlace) {
11471             placement = placement.replace(autoToken, '') || 'top';
11472         }
11473         
11474         //this.el.detach()
11475         //this.el.setXY([0,0]);
11476         this.el.show();
11477         this.el.dom.style.display='block';
11478         this.el.addClass(placement);
11479         
11480         //this.el.appendTo(on_el);
11481         
11482         var p = this.getPosition();
11483         var box = this.el.getBox();
11484         
11485         if (autoPlace) {
11486             // fixme..
11487         }
11488         var align = Roo.bootstrap.Popover.alignment[placement]
11489         this.el.alignTo(on_el, align[0],align[1]);
11490         //var arrow = this.el.select('.arrow',true).first();
11491         //arrow.set(align[2], 
11492         
11493         this.el.addClass('in');
11494         this.hoverState = null;
11495         
11496         if (this.el.hasClass('fade')) {
11497             // fade it?
11498         }
11499         
11500     },
11501     hide : function()
11502     {
11503         this.el.setXY([0,0]);
11504         this.el.removeClass('in');
11505         this.el.hide();
11506         
11507     }
11508     
11509 });
11510
11511 Roo.bootstrap.Popover.alignment = {
11512     'left' : ['r-l', [-10,0], 'right'],
11513     'right' : ['l-r', [10,0], 'left'],
11514     'bottom' : ['t-b', [0,10], 'top'],
11515     'top' : [ 'b-t', [0,-10], 'bottom']
11516 };
11517
11518  /*
11519  * - LGPL
11520  *
11521  * Progress
11522  * 
11523  */
11524
11525 /**
11526  * @class Roo.bootstrap.Progress
11527  * @extends Roo.bootstrap.Component
11528  * Bootstrap Progress class
11529  * @cfg {Boolean} striped striped of the progress bar
11530  * @cfg {Boolean} active animated of the progress bar
11531  * 
11532  * 
11533  * @constructor
11534  * Create a new Progress
11535  * @param {Object} config The config object
11536  */
11537
11538 Roo.bootstrap.Progress = function(config){
11539     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11540 };
11541
11542 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
11543     
11544     striped : false,
11545     active: false,
11546     
11547     getAutoCreate : function(){
11548         var cfg = {
11549             tag: 'div',
11550             cls: 'progress'
11551         };
11552         
11553         
11554         if(this.striped){
11555             cfg.cls += ' progress-striped';
11556         }
11557       
11558         if(this.active){
11559             cfg.cls += ' active';
11560         }
11561         
11562         
11563         return cfg;
11564     }
11565    
11566 });
11567
11568  
11569
11570  /*
11571  * - LGPL
11572  *
11573  * ProgressBar
11574  * 
11575  */
11576
11577 /**
11578  * @class Roo.bootstrap.ProgressBar
11579  * @extends Roo.bootstrap.Component
11580  * Bootstrap ProgressBar class
11581  * @cfg {Number} aria_valuenow aria-value now
11582  * @cfg {Number} aria_valuemin aria-value min
11583  * @cfg {Number} aria_valuemax aria-value max
11584  * @cfg {String} label label for the progress bar
11585  * @cfg {String} panel (success | info | warning | danger )
11586  * @cfg {String} role role of the progress bar
11587  * @cfg {String} sr_only text
11588  * 
11589  * 
11590  * @constructor
11591  * Create a new ProgressBar
11592  * @param {Object} config The config object
11593  */
11594
11595 Roo.bootstrap.ProgressBar = function(config){
11596     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11597 };
11598
11599 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
11600     
11601     aria_valuenow : 0,
11602     aria_valuemin : 0,
11603     aria_valuemax : 100,
11604     label : false,
11605     panel : false,
11606     role : false,
11607     sr_only: false,
11608     
11609     getAutoCreate : function()
11610     {
11611         
11612         var cfg = {
11613             tag: 'div',
11614             cls: 'progress-bar',
11615             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11616         };
11617         
11618         if(this.sr_only){
11619             cfg.cn = {
11620                 tag: 'span',
11621                 cls: 'sr-only',
11622                 html: this.sr_only
11623             }
11624         }
11625         
11626         if(this.role){
11627             cfg.role = this.role;
11628         }
11629         
11630         if(this.aria_valuenow){
11631             cfg['aria-valuenow'] = this.aria_valuenow;
11632         }
11633         
11634         if(this.aria_valuemin){
11635             cfg['aria-valuemin'] = this.aria_valuemin;
11636         }
11637         
11638         if(this.aria_valuemax){
11639             cfg['aria-valuemax'] = this.aria_valuemax;
11640         }
11641         
11642         if(this.label && !this.sr_only){
11643             cfg.html = this.label;
11644         }
11645         
11646         if(this.panel){
11647             cfg.cls += ' progress-bar-' + this.panel;
11648         }
11649         
11650         return cfg;
11651     },
11652     
11653     update : function(aria_valuenow)
11654     {
11655         this.aria_valuenow = aria_valuenow;
11656         
11657         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11658     }
11659    
11660 });
11661
11662  
11663
11664  /*
11665  * - LGPL
11666  *
11667  * TabPanel
11668  * 
11669  */
11670
11671 /**
11672  * @class Roo.bootstrap.TabPanel
11673  * @extends Roo.bootstrap.Component
11674  * Bootstrap TabPanel class
11675  * @cfg {Boolean} active panel active
11676  * @cfg {String} html panel content
11677  * @cfg {String} tabId tab relate id
11678  * @cfg {String} navId The navbar which triggers show hide
11679  * 
11680  * 
11681  * @constructor
11682  * Create a new TabPanel
11683  * @param {Object} config The config object
11684  */
11685
11686 Roo.bootstrap.TabPanel = function(config){
11687     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11688      this.addEvents({
11689         /**
11690              * @event changed
11691              * Fires when the active status changes
11692              * @param {Roo.bootstrap.TabPanel} this
11693              * @param {Boolean} state the new state
11694             
11695          */
11696         'changed': true
11697      });
11698 };
11699
11700 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11701     
11702     active: false,
11703     html: false,
11704     tabId: false,
11705     navId : false,
11706     
11707     getAutoCreate : function(){
11708         var cfg = {
11709             tag: 'div',
11710             cls: 'tab-pane',
11711             html: this.html || ''
11712         };
11713         
11714         if(this.active){
11715             cfg.cls += ' active';
11716         }
11717         
11718         if(this.tabId){
11719             cfg.tabId = this.tabId;
11720         }
11721         
11722         return cfg;
11723     },
11724     onRender : function(ct, position)
11725     {
11726        // Roo.log("Call onRender: " + this.xtype);
11727         
11728         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11729         
11730         if (this.navId && this.tabId) {
11731             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11732             if (!item) {
11733                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11734             } else {
11735                 item.on('changed', function(item, state) {
11736                     this.setActive(state);
11737                 }, this);
11738             }
11739         }
11740         
11741     },
11742     setActive: function(state)
11743     {
11744         Roo.log("panel - set active " + this.tabId + "=" + state);
11745         
11746         this.active = state;
11747         if (!state) {
11748             this.el.removeClass('active');
11749             
11750         } else  if (!this.el.hasClass('active')) {
11751             this.el.addClass('active');
11752         }
11753         this.fireEvent('changed', this, state);
11754     }
11755     
11756     
11757 });
11758  
11759
11760  
11761
11762  /*
11763  * - LGPL
11764  *
11765  * DateField
11766  * 
11767  */
11768
11769 /**
11770  * @class Roo.bootstrap.DateField
11771  * @extends Roo.bootstrap.Input
11772  * Bootstrap DateField class
11773  * @cfg {Number} weekStart default 0
11774  * @cfg {Number} weekStart default 0
11775  * @cfg {Number} viewMode default empty, (months|years)
11776  * @cfg {Number} minViewMode default empty, (months|years)
11777  * @cfg {Number} startDate default -Infinity
11778  * @cfg {Number} endDate default Infinity
11779  * @cfg {Boolean} todayHighlight default false
11780  * @cfg {Boolean} todayBtn default false
11781  * @cfg {Boolean} calendarWeeks default false
11782  * @cfg {Object} daysOfWeekDisabled default empty
11783  * 
11784  * @cfg {Boolean} keyboardNavigation default true
11785  * @cfg {String} language default en
11786  * 
11787  * @constructor
11788  * Create a new DateField
11789  * @param {Object} config The config object
11790  */
11791
11792 Roo.bootstrap.DateField = function(config){
11793     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11794      this.addEvents({
11795             /**
11796              * @event show
11797              * Fires when this field show.
11798              * @param {Roo.bootstrap.DateField} this
11799              * @param {Mixed} date The date value
11800              */
11801             show : true,
11802             /**
11803              * @event show
11804              * Fires when this field hide.
11805              * @param {Roo.bootstrap.DateField} this
11806              * @param {Mixed} date The date value
11807              */
11808             hide : true,
11809             /**
11810              * @event select
11811              * Fires when select a date.
11812              * @param {Roo.bootstrap.DateField} this
11813              * @param {Mixed} date The date value
11814              */
11815             select : true
11816         });
11817 };
11818
11819 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11820     
11821     /**
11822      * @cfg {String} format
11823      * The default date format string which can be overriden for localization support.  The format must be
11824      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11825      */
11826     format : "m/d/y",
11827     /**
11828      * @cfg {String} altFormats
11829      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11830      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11831      */
11832     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11833     
11834     weekStart : 0,
11835     
11836     viewMode : '',
11837     
11838     minViewMode : '',
11839     
11840     todayHighlight : false,
11841     
11842     todayBtn: false,
11843     
11844     language: 'en',
11845     
11846     keyboardNavigation: true,
11847     
11848     calendarWeeks: false,
11849     
11850     startDate: -Infinity,
11851     
11852     endDate: Infinity,
11853     
11854     daysOfWeekDisabled: [],
11855     
11856     _events: [],
11857     
11858     UTCDate: function()
11859     {
11860         return new Date(Date.UTC.apply(Date, arguments));
11861     },
11862     
11863     UTCToday: function()
11864     {
11865         var today = new Date();
11866         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11867     },
11868     
11869     getDate: function() {
11870             var d = this.getUTCDate();
11871             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11872     },
11873     
11874     getUTCDate: function() {
11875             return this.date;
11876     },
11877     
11878     setDate: function(d) {
11879             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11880     },
11881     
11882     setUTCDate: function(d) {
11883             this.date = d;
11884             this.setValue(this.formatDate(this.date));
11885     },
11886         
11887     onRender: function(ct, position)
11888     {
11889         
11890         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11891         
11892         this.language = this.language || 'en';
11893         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11894         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11895         
11896         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11897         this.format = this.format || 'm/d/y';
11898         this.isInline = false;
11899         this.isInput = true;
11900         this.component = this.el.select('.add-on', true).first() || false;
11901         this.component = (this.component && this.component.length === 0) ? false : this.component;
11902         this.hasInput = this.component && this.inputEL().length;
11903         
11904         if (typeof(this.minViewMode === 'string')) {
11905             switch (this.minViewMode) {
11906                 case 'months':
11907                     this.minViewMode = 1;
11908                     break;
11909                 case 'years':
11910                     this.minViewMode = 2;
11911                     break;
11912                 default:
11913                     this.minViewMode = 0;
11914                     break;
11915             }
11916         }
11917         
11918         if (typeof(this.viewMode === 'string')) {
11919             switch (this.viewMode) {
11920                 case 'months':
11921                     this.viewMode = 1;
11922                     break;
11923                 case 'years':
11924                     this.viewMode = 2;
11925                     break;
11926                 default:
11927                     this.viewMode = 0;
11928                     break;
11929             }
11930         }
11931                 
11932         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11933         
11934         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11935         
11936         this.picker().on('mousedown', this.onMousedown, this);
11937         this.picker().on('click', this.onClick, this);
11938         
11939         this.picker().addClass('datepicker-dropdown');
11940         
11941         this.startViewMode = this.viewMode;
11942         
11943         
11944         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11945             if(!this.calendarWeeks){
11946                 v.remove();
11947                 return;
11948             };
11949             
11950             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11951             v.attr('colspan', function(i, val){
11952                 return parseInt(val) + 1;
11953             });
11954         })
11955                         
11956         
11957         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11958         
11959         this.setStartDate(this.startDate);
11960         this.setEndDate(this.endDate);
11961         
11962         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11963         
11964         this.fillDow();
11965         this.fillMonths();
11966         this.update();
11967         this.showMode();
11968         
11969         if(this.isInline) {
11970             this.show();
11971         }
11972     },
11973     
11974     picker : function()
11975     {
11976         return this.el.select('.datepicker', true).first();
11977     },
11978     
11979     fillDow: function()
11980     {
11981         var dowCnt = this.weekStart;
11982         
11983         var dow = {
11984             tag: 'tr',
11985             cn: [
11986                 
11987             ]
11988         };
11989         
11990         if(this.calendarWeeks){
11991             dow.cn.push({
11992                 tag: 'th',
11993                 cls: 'cw',
11994                 html: '&nbsp;'
11995             })
11996         }
11997         
11998         while (dowCnt < this.weekStart + 7) {
11999             dow.cn.push({
12000                 tag: 'th',
12001                 cls: 'dow',
12002                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12003             });
12004         }
12005         
12006         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12007     },
12008     
12009     fillMonths: function()
12010     {    
12011         var i = 0
12012         var months = this.picker().select('>.datepicker-months td', true).first();
12013         
12014         months.dom.innerHTML = '';
12015         
12016         while (i < 12) {
12017             var month = {
12018                 tag: 'span',
12019                 cls: 'month',
12020                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12021             }
12022             
12023             months.createChild(month);
12024         }
12025         
12026     },
12027     
12028     update: function(){
12029         
12030         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12031         
12032         if (this.date < this.startDate) {
12033             this.viewDate = new Date(this.startDate);
12034         } else if (this.date > this.endDate) {
12035             this.viewDate = new Date(this.endDate);
12036         } else {
12037             this.viewDate = new Date(this.date);
12038         }
12039         
12040         this.fill();
12041     },
12042     
12043     fill: function() {
12044         var d = new Date(this.viewDate),
12045                 year = d.getUTCFullYear(),
12046                 month = d.getUTCMonth(),
12047                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12048                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12049                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12050                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12051                 currentDate = this.date && this.date.valueOf(),
12052                 today = this.UTCToday();
12053         
12054         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12055         
12056 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12057         
12058 //        this.picker.select('>tfoot th.today').
12059 //                                              .text(dates[this.language].today)
12060 //                                              .toggle(this.todayBtn !== false);
12061     
12062         this.updateNavArrows();
12063         this.fillMonths();
12064                                                 
12065         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12066         
12067         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12068          
12069         prevMonth.setUTCDate(day);
12070         
12071         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12072         
12073         var nextMonth = new Date(prevMonth);
12074         
12075         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12076         
12077         nextMonth = nextMonth.valueOf();
12078         
12079         var fillMonths = false;
12080         
12081         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12082         
12083         while(prevMonth.valueOf() < nextMonth) {
12084             var clsName = '';
12085             
12086             if (prevMonth.getUTCDay() === this.weekStart) {
12087                 if(fillMonths){
12088                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12089                 }
12090                     
12091                 fillMonths = {
12092                     tag: 'tr',
12093                     cn: []
12094                 };
12095                 
12096                 if(this.calendarWeeks){
12097                     // ISO 8601: First week contains first thursday.
12098                     // ISO also states week starts on Monday, but we can be more abstract here.
12099                     var
12100                     // Start of current week: based on weekstart/current date
12101                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12102                     // Thursday of this week
12103                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12104                     // First Thursday of year, year from thursday
12105                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12106                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12107                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12108                     
12109                     fillMonths.cn.push({
12110                         tag: 'td',
12111                         cls: 'cw',
12112                         html: calWeek
12113                     });
12114                 }
12115             }
12116             
12117             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12118                 clsName += ' old';
12119             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12120                 clsName += ' new';
12121             }
12122             if (this.todayHighlight &&
12123                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12124                 prevMonth.getUTCMonth() == today.getMonth() &&
12125                 prevMonth.getUTCDate() == today.getDate()) {
12126                 clsName += ' today';
12127             }
12128             
12129             if (currentDate && prevMonth.valueOf() === currentDate) {
12130                 clsName += ' active';
12131             }
12132             
12133             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12134                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12135                     clsName += ' disabled';
12136             }
12137             
12138             fillMonths.cn.push({
12139                 tag: 'td',
12140                 cls: 'day ' + clsName,
12141                 html: prevMonth.getDate()
12142             })
12143             
12144             prevMonth.setDate(prevMonth.getDate()+1);
12145         }
12146           
12147         var currentYear = this.date && this.date.getUTCFullYear();
12148         var currentMonth = this.date && this.date.getUTCMonth();
12149         
12150         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12151         
12152         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12153             v.removeClass('active');
12154             
12155             if(currentYear === year && k === currentMonth){
12156                 v.addClass('active');
12157             }
12158             
12159             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12160                 v.addClass('disabled');
12161             }
12162             
12163         });
12164         
12165         
12166         year = parseInt(year/10, 10) * 10;
12167         
12168         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12169         
12170         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12171         
12172         year -= 1;
12173         for (var i = -1; i < 11; i++) {
12174             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12175                 tag: 'span',
12176                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12177                 html: year
12178             })
12179             
12180             year += 1;
12181         }
12182     },
12183     
12184     showMode: function(dir) {
12185         if (dir) {
12186             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12187         }
12188         Roo.each(this.picker().select('>div',true).elements, function(v){
12189             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12190             v.hide();
12191         });
12192         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12193     },
12194     
12195     place: function()
12196     {
12197         if(this.isInline) return;
12198         
12199         this.picker().removeClass(['bottom', 'top']);
12200         
12201         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12202             /*
12203              * place to the top of element!
12204              *
12205              */
12206             
12207             this.picker().addClass('top');
12208             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12209             
12210             return;
12211         }
12212         
12213         this.picker().addClass('bottom');
12214         
12215         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12216     },
12217     
12218     parseDate : function(value){
12219         if(!value || value instanceof Date){
12220             return value;
12221         }
12222         var v = Date.parseDate(value, this.format);
12223         if (!v && this.useIso) {
12224             v = Date.parseDate(value, 'Y-m-d');
12225         }
12226         if(!v && this.altFormats){
12227             if(!this.altFormatsArray){
12228                 this.altFormatsArray = this.altFormats.split("|");
12229             }
12230             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12231                 v = Date.parseDate(value, this.altFormatsArray[i]);
12232             }
12233         }
12234         return v;
12235     },
12236     
12237     formatDate : function(date, fmt){
12238         return (!date || !(date instanceof Date)) ?
12239         date : date.dateFormat(fmt || this.format);
12240     },
12241     
12242     onFocus : function()
12243     {
12244         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12245         this.show();
12246     },
12247     
12248     onBlur : function()
12249     {
12250         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12251         this.hide();
12252     },
12253     
12254     show : function()
12255     {
12256         this.picker().show();
12257         this.update();
12258         this.place();
12259         
12260         this.fireEvent('show', this, this.date);
12261     },
12262     
12263     hide : function()
12264     {
12265         if(this.isInline) return;
12266         this.picker().hide();
12267         this.viewMode = this.startViewMode;
12268         this.showMode();
12269         
12270         this.fireEvent('hide', this, this.date);
12271         
12272     },
12273     
12274     onMousedown: function(e){
12275         e.stopPropagation();
12276         e.preventDefault();
12277     },
12278     
12279     keyup: function(e){
12280         Roo.bootstrap.DateField.superclass.keyup.call(this);
12281         this.update();
12282         
12283     },
12284
12285     setValue: function(v){
12286         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12287         
12288         this.fireEvent('select', this, this.date);
12289         
12290     },
12291     
12292     fireKey: function(e){
12293         if (!this.picker().isVisible()){
12294             if (e.keyCode == 27) // allow escape to hide and re-show picker
12295                 this.show();
12296             return;
12297         }
12298         var dateChanged = false,
12299         dir, day, month,
12300         newDate, newViewDate;
12301         switch(e.keyCode){
12302             case 27: // escape
12303                 this.hide();
12304                 e.preventDefault();
12305                 break;
12306             case 37: // left
12307             case 39: // right
12308                 if (!this.keyboardNavigation) break;
12309                 dir = e.keyCode == 37 ? -1 : 1;
12310                 
12311                 if (e.ctrlKey){
12312                     newDate = this.moveYear(this.date, dir);
12313                     newViewDate = this.moveYear(this.viewDate, dir);
12314                 } else if (e.shiftKey){
12315                     newDate = this.moveMonth(this.date, dir);
12316                     newViewDate = this.moveMonth(this.viewDate, dir);
12317                 } else {
12318                     newDate = new Date(this.date);
12319                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12320                     newViewDate = new Date(this.viewDate);
12321                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12322                 }
12323                 if (this.dateWithinRange(newDate)){
12324                     this.date = newDate;
12325                     this.viewDate = newViewDate;
12326                     this.setValue(this.formatDate(this.date));
12327                     this.update();
12328                     e.preventDefault();
12329                     dateChanged = true;
12330                 }
12331                 break;
12332             case 38: // up
12333             case 40: // down
12334                 if (!this.keyboardNavigation) break;
12335                 dir = e.keyCode == 38 ? -1 : 1;
12336                 if (e.ctrlKey){
12337                     newDate = this.moveYear(this.date, dir);
12338                     newViewDate = this.moveYear(this.viewDate, dir);
12339                 } else if (e.shiftKey){
12340                     newDate = this.moveMonth(this.date, dir);
12341                     newViewDate = this.moveMonth(this.viewDate, dir);
12342                 } else {
12343                     newDate = new Date(this.date);
12344                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12345                     newViewDate = new Date(this.viewDate);
12346                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12347                 }
12348                 if (this.dateWithinRange(newDate)){
12349                     this.date = newDate;
12350                     this.viewDate = newViewDate;
12351                     this.setValue(this.formatDate(this.date));
12352                     this.update();
12353                     e.preventDefault();
12354                     dateChanged = true;
12355                 }
12356                 break;
12357             case 13: // enter
12358                 this.setValue(this.formatDate(this.date));
12359                 this.hide();
12360                 e.preventDefault();
12361                 break;
12362             case 9: // tab
12363                 this.setValue(this.formatDate(this.date));
12364                 this.hide();
12365                 break;
12366         }
12367     },
12368     
12369     
12370     onClick: function(e) {
12371         e.stopPropagation();
12372         e.preventDefault();
12373         
12374         var target = e.getTarget();
12375         
12376         if(target.nodeName.toLowerCase() === 'i'){
12377             target = Roo.get(target).dom.parentNode;
12378         }
12379         
12380         var nodeName = target.nodeName;
12381         var className = target.className;
12382         var html = target.innerHTML;
12383         
12384         switch(nodeName.toLowerCase()) {
12385             case 'th':
12386                 switch(className) {
12387                     case 'switch':
12388                         this.showMode(1);
12389                         break;
12390                     case 'prev':
12391                     case 'next':
12392                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12393                         switch(this.viewMode){
12394                                 case 0:
12395                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12396                                         break;
12397                                 case 1:
12398                                 case 2:
12399                                         this.viewDate = this.moveYear(this.viewDate, dir);
12400                                         break;
12401                         }
12402                         this.fill();
12403                         break;
12404                     case 'today':
12405                         var date = new Date();
12406                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12407                         this.fill()
12408                         this.setValue(this.formatDate(this.date));
12409                         this.hide();
12410                         break;
12411                 }
12412                 break;
12413             case 'span':
12414                 if (className.indexOf('disabled') === -1) {
12415                     this.viewDate.setUTCDate(1);
12416                     if (className.indexOf('month') !== -1) {
12417                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12418                     } else {
12419                         var year = parseInt(html, 10) || 0;
12420                         this.viewDate.setUTCFullYear(year);
12421                         
12422                     }
12423                     this.showMode(-1);
12424                     this.fill();
12425                 }
12426                 break;
12427                 
12428             case 'td':
12429                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12430                     var day = parseInt(html, 10) || 1;
12431                     var year = this.viewDate.getUTCFullYear(),
12432                         month = this.viewDate.getUTCMonth();
12433
12434                     if (className.indexOf('old') !== -1) {
12435                         if(month === 0 ){
12436                             month = 11;
12437                             year -= 1;
12438                         }else{
12439                             month -= 1;
12440                         }
12441                     } else if (className.indexOf('new') !== -1) {
12442                         if (month == 11) {
12443                             month = 0;
12444                             year += 1;
12445                         } else {
12446                             month += 1;
12447                         }
12448                     }
12449                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12450                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12451                     this.fill();
12452                     this.setValue(this.formatDate(this.date));
12453                     this.hide();
12454                 }
12455                 break;
12456         }
12457     },
12458     
12459     setStartDate: function(startDate){
12460         this.startDate = startDate || -Infinity;
12461         if (this.startDate !== -Infinity) {
12462             this.startDate = this.parseDate(this.startDate);
12463         }
12464         this.update();
12465         this.updateNavArrows();
12466     },
12467
12468     setEndDate: function(endDate){
12469         this.endDate = endDate || Infinity;
12470         if (this.endDate !== Infinity) {
12471             this.endDate = this.parseDate(this.endDate);
12472         }
12473         this.update();
12474         this.updateNavArrows();
12475     },
12476     
12477     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12478         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12479         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12480             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12481         }
12482         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12483             return parseInt(d, 10);
12484         });
12485         this.update();
12486         this.updateNavArrows();
12487     },
12488     
12489     updateNavArrows: function() {
12490         var d = new Date(this.viewDate),
12491         year = d.getUTCFullYear(),
12492         month = d.getUTCMonth();
12493         
12494         Roo.each(this.picker().select('.prev', true).elements, function(v){
12495             v.show();
12496             switch (this.viewMode) {
12497                 case 0:
12498
12499                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12500                         v.hide();
12501                     }
12502                     break;
12503                 case 1:
12504                 case 2:
12505                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12506                         v.hide();
12507                     }
12508                     break;
12509             }
12510         });
12511         
12512         Roo.each(this.picker().select('.next', true).elements, function(v){
12513             v.show();
12514             switch (this.viewMode) {
12515                 case 0:
12516
12517                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12518                         v.hide();
12519                     }
12520                     break;
12521                 case 1:
12522                 case 2:
12523                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12524                         v.hide();
12525                     }
12526                     break;
12527             }
12528         })
12529     },
12530     
12531     moveMonth: function(date, dir){
12532         if (!dir) return date;
12533         var new_date = new Date(date.valueOf()),
12534         day = new_date.getUTCDate(),
12535         month = new_date.getUTCMonth(),
12536         mag = Math.abs(dir),
12537         new_month, test;
12538         dir = dir > 0 ? 1 : -1;
12539         if (mag == 1){
12540             test = dir == -1
12541             // If going back one month, make sure month is not current month
12542             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12543             ? function(){
12544                 return new_date.getUTCMonth() == month;
12545             }
12546             // If going forward one month, make sure month is as expected
12547             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12548             : function(){
12549                 return new_date.getUTCMonth() != new_month;
12550             };
12551             new_month = month + dir;
12552             new_date.setUTCMonth(new_month);
12553             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12554             if (new_month < 0 || new_month > 11)
12555                 new_month = (new_month + 12) % 12;
12556         } else {
12557             // For magnitudes >1, move one month at a time...
12558             for (var i=0; i<mag; i++)
12559                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12560                 new_date = this.moveMonth(new_date, dir);
12561             // ...then reset the day, keeping it in the new month
12562             new_month = new_date.getUTCMonth();
12563             new_date.setUTCDate(day);
12564             test = function(){
12565                 return new_month != new_date.getUTCMonth();
12566             };
12567         }
12568         // Common date-resetting loop -- if date is beyond end of month, make it
12569         // end of month
12570         while (test()){
12571             new_date.setUTCDate(--day);
12572             new_date.setUTCMonth(new_month);
12573         }
12574         return new_date;
12575     },
12576
12577     moveYear: function(date, dir){
12578         return this.moveMonth(date, dir*12);
12579     },
12580
12581     dateWithinRange: function(date){
12582         return date >= this.startDate && date <= this.endDate;
12583     },
12584
12585     
12586     remove: function() {
12587         this.picker().remove();
12588     }
12589    
12590 });
12591
12592 Roo.apply(Roo.bootstrap.DateField,  {
12593     
12594     head : {
12595         tag: 'thead',
12596         cn: [
12597         {
12598             tag: 'tr',
12599             cn: [
12600             {
12601                 tag: 'th',
12602                 cls: 'prev',
12603                 html: '<i class="icon-arrow-left"/>'
12604             },
12605             {
12606                 tag: 'th',
12607                 cls: 'switch',
12608                 colspan: '5'
12609             },
12610             {
12611                 tag: 'th',
12612                 cls: 'next',
12613                 html: '<i class="icon-arrow-right"/>'
12614             }
12615
12616             ]
12617         }
12618         ]
12619     },
12620     
12621     content : {
12622         tag: 'tbody',
12623         cn: [
12624         {
12625             tag: 'tr',
12626             cn: [
12627             {
12628                 tag: 'td',
12629                 colspan: '7'
12630             }
12631             ]
12632         }
12633         ]
12634     },
12635     
12636     footer : {
12637         tag: 'tfoot',
12638         cn: [
12639         {
12640             tag: 'tr',
12641             cn: [
12642             {
12643                 tag: 'th',
12644                 colspan: '7',
12645                 cls: 'today'
12646             }
12647                     
12648             ]
12649         }
12650         ]
12651     },
12652     
12653     dates:{
12654         en: {
12655             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12656             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12657             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12658             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12659             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12660             today: "Today"
12661         }
12662     },
12663     
12664     modes: [
12665     {
12666         clsName: 'days',
12667         navFnc: 'Month',
12668         navStep: 1
12669     },
12670     {
12671         clsName: 'months',
12672         navFnc: 'FullYear',
12673         navStep: 1
12674     },
12675     {
12676         clsName: 'years',
12677         navFnc: 'FullYear',
12678         navStep: 10
12679     }]
12680 });
12681
12682 Roo.apply(Roo.bootstrap.DateField,  {
12683   
12684     template : {
12685         tag: 'div',
12686         cls: 'datepicker dropdown-menu',
12687         cn: [
12688         {
12689             tag: 'div',
12690             cls: 'datepicker-days',
12691             cn: [
12692             {
12693                 tag: 'table',
12694                 cls: 'table-condensed',
12695                 cn:[
12696                 Roo.bootstrap.DateField.head,
12697                 {
12698                     tag: 'tbody'
12699                 },
12700                 Roo.bootstrap.DateField.footer
12701                 ]
12702             }
12703             ]
12704         },
12705         {
12706             tag: 'div',
12707             cls: 'datepicker-months',
12708             cn: [
12709             {
12710                 tag: 'table',
12711                 cls: 'table-condensed',
12712                 cn:[
12713                 Roo.bootstrap.DateField.head,
12714                 Roo.bootstrap.DateField.content,
12715                 Roo.bootstrap.DateField.footer
12716                 ]
12717             }
12718             ]
12719         },
12720         {
12721             tag: 'div',
12722             cls: 'datepicker-years',
12723             cn: [
12724             {
12725                 tag: 'table',
12726                 cls: 'table-condensed',
12727                 cn:[
12728                 Roo.bootstrap.DateField.head,
12729                 Roo.bootstrap.DateField.content,
12730                 Roo.bootstrap.DateField.footer
12731                 ]
12732             }
12733             ]
12734         }
12735         ]
12736     }
12737 });
12738
12739  
12740
12741  /*
12742  * - LGPL
12743  *
12744  * TimeField
12745  * 
12746  */
12747
12748 /**
12749  * @class Roo.bootstrap.TimeField
12750  * @extends Roo.bootstrap.Input
12751  * Bootstrap DateField class
12752  * 
12753  * 
12754  * @constructor
12755  * Create a new TimeField
12756  * @param {Object} config The config object
12757  */
12758
12759 Roo.bootstrap.TimeField = function(config){
12760     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12761     this.addEvents({
12762             /**
12763              * @event show
12764              * Fires when this field show.
12765              * @param {Roo.bootstrap.DateField} this
12766              * @param {Mixed} date The date value
12767              */
12768             show : true,
12769             /**
12770              * @event show
12771              * Fires when this field hide.
12772              * @param {Roo.bootstrap.DateField} this
12773              * @param {Mixed} date The date value
12774              */
12775             hide : true,
12776             /**
12777              * @event select
12778              * Fires when select a date.
12779              * @param {Roo.bootstrap.DateField} this
12780              * @param {Mixed} date The date value
12781              */
12782             select : true
12783         });
12784 };
12785
12786 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12787     
12788     /**
12789      * @cfg {String} format
12790      * The default time format string which can be overriden for localization support.  The format must be
12791      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12792      */
12793     format : "H:i",
12794        
12795     onRender: function(ct, position)
12796     {
12797         
12798         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12799                 
12800         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12801         
12802         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12803         
12804         this.pop = this.picker().select('>.datepicker-time',true).first();
12805         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12806         
12807         this.picker().on('mousedown', this.onMousedown, this);
12808         this.picker().on('click', this.onClick, this);
12809         
12810         this.picker().addClass('datepicker-dropdown');
12811     
12812         this.fillTime();
12813         this.update();
12814             
12815         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12816         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12817         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12818         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12819         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12820         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12821
12822     },
12823     
12824     fireKey: function(e){
12825         if (!this.picker().isVisible()){
12826             if (e.keyCode == 27) // allow escape to hide and re-show picker
12827                 this.show();
12828             return;
12829         }
12830
12831         e.preventDefault();
12832         
12833         switch(e.keyCode){
12834             case 27: // escape
12835                 this.hide();
12836                 break;
12837             case 37: // left
12838             case 39: // right
12839                 this.onTogglePeriod();
12840                 break;
12841             case 38: // up
12842                 this.onIncrementMinutes();
12843                 break;
12844             case 40: // down
12845                 this.onDecrementMinutes();
12846                 break;
12847             case 13: // enter
12848             case 9: // tab
12849                 this.setTime();
12850                 break;
12851         }
12852     },
12853     
12854     onClick: function(e) {
12855         e.stopPropagation();
12856         e.preventDefault();
12857     },
12858     
12859     picker : function()
12860     {
12861         return this.el.select('.datepicker', true).first();
12862     },
12863     
12864     fillTime: function()
12865     {    
12866         var time = this.pop.select('tbody', true).first();
12867         
12868         time.dom.innerHTML = '';
12869         
12870         time.createChild({
12871             tag: 'tr',
12872             cn: [
12873                 {
12874                     tag: 'td',
12875                     cn: [
12876                         {
12877                             tag: 'a',
12878                             href: '#',
12879                             cls: 'btn',
12880                             cn: [
12881                                 {
12882                                     tag: 'span',
12883                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12884                                 }
12885                             ]
12886                         } 
12887                     ]
12888                 },
12889                 {
12890                     tag: 'td',
12891                     cls: 'separator'
12892                 },
12893                 {
12894                     tag: 'td',
12895                     cn: [
12896                         {
12897                             tag: 'a',
12898                             href: '#',
12899                             cls: 'btn',
12900                             cn: [
12901                                 {
12902                                     tag: 'span',
12903                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12904                                 }
12905                             ]
12906                         }
12907                     ]
12908                 },
12909                 {
12910                     tag: 'td',
12911                     cls: 'separator'
12912                 }
12913             ]
12914         });
12915         
12916         time.createChild({
12917             tag: 'tr',
12918             cn: [
12919                 {
12920                     tag: 'td',
12921                     cn: [
12922                         {
12923                             tag: 'span',
12924                             cls: 'timepicker-hour',
12925                             html: '00'
12926                         }  
12927                     ]
12928                 },
12929                 {
12930                     tag: 'td',
12931                     cls: 'separator',
12932                     html: ':'
12933                 },
12934                 {
12935                     tag: 'td',
12936                     cn: [
12937                         {
12938                             tag: 'span',
12939                             cls: 'timepicker-minute',
12940                             html: '00'
12941                         }  
12942                     ]
12943                 },
12944                 {
12945                     tag: 'td',
12946                     cls: 'separator'
12947                 },
12948                 {
12949                     tag: 'td',
12950                     cn: [
12951                         {
12952                             tag: 'button',
12953                             type: 'button',
12954                             cls: 'btn btn-primary period',
12955                             html: 'AM'
12956                             
12957                         }
12958                     ]
12959                 }
12960             ]
12961         });
12962         
12963         time.createChild({
12964             tag: 'tr',
12965             cn: [
12966                 {
12967                     tag: 'td',
12968                     cn: [
12969                         {
12970                             tag: 'a',
12971                             href: '#',
12972                             cls: 'btn',
12973                             cn: [
12974                                 {
12975                                     tag: 'span',
12976                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12977                                 }
12978                             ]
12979                         }
12980                     ]
12981                 },
12982                 {
12983                     tag: 'td',
12984                     cls: 'separator'
12985                 },
12986                 {
12987                     tag: 'td',
12988                     cn: [
12989                         {
12990                             tag: 'a',
12991                             href: '#',
12992                             cls: 'btn',
12993                             cn: [
12994                                 {
12995                                     tag: 'span',
12996                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12997                                 }
12998                             ]
12999                         }
13000                     ]
13001                 },
13002                 {
13003                     tag: 'td',
13004                     cls: 'separator'
13005                 }
13006             ]
13007         });
13008         
13009     },
13010     
13011     update: function()
13012     {
13013         
13014         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13015         
13016         this.fill();
13017     },
13018     
13019     fill: function() 
13020     {
13021         var hours = this.time.getHours();
13022         var minutes = this.time.getMinutes();
13023         var period = 'AM';
13024         
13025         if(hours > 11){
13026             period = 'PM';
13027         }
13028         
13029         if(hours == 0){
13030             hours = 12;
13031         }
13032         
13033         
13034         if(hours > 12){
13035             hours = hours - 12;
13036         }
13037         
13038         if(hours < 10){
13039             hours = '0' + hours;
13040         }
13041         
13042         if(minutes < 10){
13043             minutes = '0' + minutes;
13044         }
13045         
13046         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13047         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13048         this.pop.select('button', true).first().dom.innerHTML = period;
13049         
13050     },
13051     
13052     place: function()
13053     {   
13054         this.picker().removeClass(['bottom', 'top']);
13055         
13056         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13057             /*
13058              * place to the top of element!
13059              *
13060              */
13061             
13062             this.picker().addClass('top');
13063             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13064             
13065             return;
13066         }
13067         
13068         this.picker().addClass('bottom');
13069         
13070         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13071     },
13072   
13073     onFocus : function()
13074     {
13075         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13076         this.show();
13077     },
13078     
13079     onBlur : function()
13080     {
13081         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13082         this.hide();
13083     },
13084     
13085     show : function()
13086     {
13087         this.picker().show();
13088         this.pop.show();
13089         this.update();
13090         this.place();
13091         
13092         this.fireEvent('show', this, this.date);
13093     },
13094     
13095     hide : function()
13096     {
13097         this.picker().hide();
13098         this.pop.hide();
13099         
13100         this.fireEvent('hide', this, this.date);
13101     },
13102     
13103     setTime : function()
13104     {
13105         this.hide();
13106         this.setValue(this.time.format(this.format));
13107         
13108         this.fireEvent('select', this, this.date);
13109         
13110         
13111     },
13112     
13113     onMousedown: function(e){
13114         e.stopPropagation();
13115         e.preventDefault();
13116     },
13117     
13118     onIncrementHours: function()
13119     {
13120         Roo.log('onIncrementHours');
13121         this.time = this.time.add(Date.HOUR, 1);
13122         this.update();
13123         
13124     },
13125     
13126     onDecrementHours: function()
13127     {
13128         Roo.log('onDecrementHours');
13129         this.time = this.time.add(Date.HOUR, -1);
13130         this.update();
13131     },
13132     
13133     onIncrementMinutes: function()
13134     {
13135         Roo.log('onIncrementMinutes');
13136         this.time = this.time.add(Date.MINUTE, 1);
13137         this.update();
13138     },
13139     
13140     onDecrementMinutes: function()
13141     {
13142         Roo.log('onDecrementMinutes');
13143         this.time = this.time.add(Date.MINUTE, -1);
13144         this.update();
13145     },
13146     
13147     onTogglePeriod: function()
13148     {
13149         Roo.log('onTogglePeriod');
13150         this.time = this.time.add(Date.HOUR, 12);
13151         this.update();
13152     }
13153     
13154    
13155 });
13156
13157 Roo.apply(Roo.bootstrap.TimeField,  {
13158     
13159     content : {
13160         tag: 'tbody',
13161         cn: [
13162             {
13163                 tag: 'tr',
13164                 cn: [
13165                 {
13166                     tag: 'td',
13167                     colspan: '7'
13168                 }
13169                 ]
13170             }
13171         ]
13172     },
13173     
13174     footer : {
13175         tag: 'tfoot',
13176         cn: [
13177             {
13178                 tag: 'tr',
13179                 cn: [
13180                 {
13181                     tag: 'th',
13182                     colspan: '7',
13183                     cls: '',
13184                     cn: [
13185                         {
13186                             tag: 'button',
13187                             cls: 'btn btn-info ok',
13188                             html: 'OK'
13189                         }
13190                     ]
13191                 }
13192
13193                 ]
13194             }
13195         ]
13196     }
13197 });
13198
13199 Roo.apply(Roo.bootstrap.TimeField,  {
13200   
13201     template : {
13202         tag: 'div',
13203         cls: 'datepicker dropdown-menu',
13204         cn: [
13205             {
13206                 tag: 'div',
13207                 cls: 'datepicker-time',
13208                 cn: [
13209                 {
13210                     tag: 'table',
13211                     cls: 'table-condensed',
13212                     cn:[
13213                     Roo.bootstrap.TimeField.content,
13214                     Roo.bootstrap.TimeField.footer
13215                     ]
13216                 }
13217                 ]
13218             }
13219         ]
13220     }
13221 });
13222
13223  
13224
13225  /*
13226  * - LGPL
13227  *
13228  * CheckBox
13229  * 
13230  */
13231
13232 /**
13233  * @class Roo.bootstrap.CheckBox
13234  * @extends Roo.bootstrap.Input
13235  * Bootstrap CheckBox class
13236  * 
13237  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13238  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13239  * @cfg {String} boxLabel The text that appears beside the checkbox
13240  * @cfg {Boolean} checked initnal the element
13241  * 
13242  * @constructor
13243  * Create a new CheckBox
13244  * @param {Object} config The config object
13245  */
13246
13247 Roo.bootstrap.CheckBox = function(config){
13248     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13249    
13250         this.addEvents({
13251             /**
13252             * @event check
13253             * Fires when the element is checked or unchecked.
13254             * @param {Roo.bootstrap.CheckBox} this This input
13255             * @param {Boolean} checked The new checked value
13256             */
13257            check : true
13258         });
13259 };
13260
13261 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13262     
13263     inputType: 'checkbox',
13264     inputValue: 1,
13265     valueOff: 0,
13266     boxLabel: false,
13267     checked: false,
13268     
13269     getAutoCreate : function()
13270     {
13271         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13272         
13273         var id = Roo.id();
13274         
13275         var cfg = {};
13276         
13277         cfg.cls = 'form-group' //input-group
13278         
13279         var input =  {
13280             tag: 'input',
13281             id : id,
13282             type : this.inputType,
13283             value : (!this.checked) ? this.valueOff : this.inputValue,
13284             cls : 'form-box',
13285             placeholder : this.placeholder || ''
13286             
13287         };
13288         
13289         if (this.disabled) {
13290             input.disabled=true;
13291         }
13292         
13293         if(this.checked){
13294             input.checked = this.checked;
13295         }
13296         
13297         if (this.name) {
13298             input.name = this.name;
13299         }
13300         
13301         if (this.size) {
13302             input.cls += ' input-' + this.size;
13303         }
13304         
13305         var settings=this;
13306         ['xs','sm','md','lg'].map(function(size){
13307             if (settings[size]) {
13308                 cfg.cls += ' col-' + size + '-' + settings[size];
13309             }
13310         });
13311         
13312         var inputblock = input;
13313         
13314         if (this.before || this.after) {
13315             
13316             inputblock = {
13317                 cls : 'input-group',
13318                 cn :  [] 
13319             };
13320             if (this.before) {
13321                 inputblock.cn.push({
13322                     tag :'span',
13323                     cls : 'input-group-addon',
13324                     html : this.before
13325                 });
13326             }
13327             inputblock.cn.push(input);
13328             if (this.after) {
13329                 inputblock.cn.push({
13330                     tag :'span',
13331                     cls : 'input-group-addon',
13332                     html : this.after
13333                 });
13334             }
13335             
13336         };
13337         
13338         if (align ==='left' && this.fieldLabel.length) {
13339                 Roo.log("left and has label");
13340                 cfg.cn = [
13341                     
13342                     {
13343                         tag: 'label',
13344                         'for' :  id,
13345                         cls : 'control-label col-md-' + this.labelWidth,
13346                         html : this.fieldLabel
13347                         
13348                     },
13349                     {
13350                         cls : "col-md-" + (12 - this.labelWidth), 
13351                         cn: [
13352                             inputblock
13353                         ]
13354                     }
13355                     
13356                 ];
13357         } else if ( this.fieldLabel.length) {
13358                 Roo.log(" label");
13359                 cfg.cn = [
13360                    
13361                     {
13362                         tag: this.boxLabel ? 'span' : 'label',
13363                         'for': id,
13364                         cls: 'control-label box-input-label',
13365                         //cls : 'input-group-addon',
13366                         html : this.fieldLabel
13367                         
13368                     },
13369                     
13370                     inputblock
13371                     
13372                 ];
13373
13374         } else {
13375             
13376                    Roo.log(" no label && no align");
13377                 cfg.cn = [
13378                     
13379                         inputblock
13380                     
13381                 ];
13382                 
13383                 
13384         };
13385         
13386         if(this.boxLabel){
13387             cfg.cn.push({
13388                 tag: 'label',
13389                 'for': id,
13390                 cls: 'box-label',
13391                 html: this.boxLabel
13392             })
13393         }
13394         
13395         return cfg;
13396         
13397     },
13398     
13399     /**
13400      * return the real input element.
13401      */
13402     inputEl: function ()
13403     {
13404         return this.el.select('input.form-box',true).first();
13405     },
13406     
13407     label: function()
13408     {
13409         return this.el.select('label.control-label',true).first();
13410     },
13411     
13412     initEvents : function()
13413     {
13414 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13415         
13416         this.inputEl().on('click', this.onClick,  this);
13417         
13418     },
13419     
13420     onClick : function()
13421     {   
13422         this.setChecked(!this.checked);
13423     },
13424     
13425     setChecked : function(state,suppressEvent)
13426     {
13427         this.checked = state;
13428         
13429         this.inputEl().dom.checked = state;
13430         
13431         if(suppressEvent !== true){
13432             this.fireEvent('check', this, state);
13433         }
13434         
13435         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13436         
13437     },
13438     
13439     setValue : function(v,suppressEvent)
13440     {
13441         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13442     }
13443     
13444 });
13445
13446  
13447 /*
13448  * - LGPL
13449  *
13450  * Radio
13451  * 
13452  */
13453
13454 /**
13455  * @class Roo.bootstrap.Radio
13456  * @extends Roo.bootstrap.CheckBox
13457  * Bootstrap Radio class
13458
13459  * @constructor
13460  * Create a new Radio
13461  * @param {Object} config The config object
13462  */
13463
13464 Roo.bootstrap.Radio = function(config){
13465     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13466    
13467 };
13468
13469 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13470     
13471     inputType: 'radio',
13472     inputValue: '',
13473     valueOff: '',
13474     
13475     getAutoCreate : function()
13476     {
13477         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13478         
13479         var id = Roo.id();
13480         
13481         var cfg = {};
13482         
13483         cfg.cls = 'form-group' //input-group
13484         
13485         var input =  {
13486             tag: 'input',
13487             id : id,
13488             type : this.inputType,
13489             value : (!this.checked) ? this.valueOff : this.inputValue,
13490             cls : 'form-box',
13491             placeholder : this.placeholder || ''
13492             
13493         };
13494         
13495         if (this.disabled) {
13496             input.disabled=true;
13497         }
13498         
13499         if(this.checked){
13500             input.checked = this.checked;
13501         }
13502         
13503         if (this.name) {
13504             input.name = this.name;
13505         }
13506         
13507         if (this.size) {
13508             input.cls += ' input-' + this.size;
13509         }
13510         
13511         var settings=this;
13512         ['xs','sm','md','lg'].map(function(size){
13513             if (settings[size]) {
13514                 cfg.cls += ' col-' + size + '-' + settings[size];
13515             }
13516         });
13517         
13518         var inputblock = input;
13519         
13520         if (this.before || this.after) {
13521             
13522             inputblock = {
13523                 cls : 'input-group',
13524                 cn :  [] 
13525             };
13526             if (this.before) {
13527                 inputblock.cn.push({
13528                     tag :'span',
13529                     cls : 'input-group-addon',
13530                     html : this.before
13531                 });
13532             }
13533             inputblock.cn.push(input);
13534             if (this.after) {
13535                 inputblock.cn.push({
13536                     tag :'span',
13537                     cls : 'input-group-addon',
13538                     html : this.after
13539                 });
13540             }
13541             
13542         };
13543         
13544         if (align ==='left' && this.fieldLabel.length) {
13545                 Roo.log("left and has label");
13546                 cfg.cn = [
13547                     
13548                     {
13549                         tag: 'label',
13550                         'for' :  id,
13551                         cls : 'control-label col-md-' + this.labelWidth,
13552                         html : this.fieldLabel
13553                         
13554                     },
13555                     {
13556                         cls : "col-md-" + (12 - this.labelWidth), 
13557                         cn: [
13558                             inputblock
13559                         ]
13560                     }
13561                     
13562                 ];
13563         } else if ( this.fieldLabel.length) {
13564                 Roo.log(" label");
13565                  cfg.cn = [
13566                    
13567                     {
13568                         tag: 'label',
13569                         'for': id,
13570                         cls: 'control-label box-input-label',
13571                         //cls : 'input-group-addon',
13572                         html : this.fieldLabel
13573                         
13574                     },
13575                     
13576                     inputblock
13577                     
13578                 ];
13579
13580         } else {
13581             
13582                    Roo.log(" no label && no align");
13583                 cfg.cn = [
13584                     
13585                         inputblock
13586                     
13587                 ];
13588                 
13589                 
13590         };
13591         
13592         if(this.boxLabel){
13593             cfg.cn.push({
13594                 tag: 'label',
13595                 'for': id,
13596                 cls: 'box-label',
13597                 html: this.boxLabel
13598             })
13599         }
13600         
13601         return cfg;
13602         
13603     },
13604    
13605     onClick : function()
13606     {   
13607         this.setChecked(true);
13608     },
13609     
13610     setChecked : function(state,suppressEvent)
13611     {
13612         if(state){
13613             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13614                 v.dom.checked = false;
13615             });
13616         }
13617         
13618         this.checked = state;
13619         this.inputEl().dom.checked = state;
13620         
13621         if(suppressEvent !== true){
13622             this.fireEvent('check', this, state);
13623         }
13624         
13625         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13626         
13627     },
13628     
13629     getGroupValue : function()
13630     {
13631         var value = ''
13632         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13633             if(v.dom.checked == true){
13634                 value = v.dom.value;
13635             }
13636         });
13637         
13638         return value;
13639     },
13640     
13641     /**
13642      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13643      * @return {Mixed} value The field value
13644      */
13645     getValue : function(){
13646         return this.getGroupValue();
13647     }
13648     
13649 });
13650
13651  
13652 //<script type="text/javascript">
13653
13654 /*
13655  * Based  Ext JS Library 1.1.1
13656  * Copyright(c) 2006-2007, Ext JS, LLC.
13657  * LGPL
13658  *
13659  */
13660  
13661 /**
13662  * @class Roo.HtmlEditorCore
13663  * @extends Roo.Component
13664  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13665  *
13666  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13667  */
13668
13669 Roo.HtmlEditorCore = function(config){
13670     
13671     
13672     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13673     this.addEvents({
13674         /**
13675          * @event initialize
13676          * Fires when the editor is fully initialized (including the iframe)
13677          * @param {Roo.HtmlEditorCore} this
13678          */
13679         initialize: true,
13680         /**
13681          * @event activate
13682          * Fires when the editor is first receives the focus. Any insertion must wait
13683          * until after this event.
13684          * @param {Roo.HtmlEditorCore} this
13685          */
13686         activate: true,
13687          /**
13688          * @event beforesync
13689          * Fires before the textarea is updated with content from the editor iframe. Return false
13690          * to cancel the sync.
13691          * @param {Roo.HtmlEditorCore} this
13692          * @param {String} html
13693          */
13694         beforesync: true,
13695          /**
13696          * @event beforepush
13697          * Fires before the iframe editor is updated with content from the textarea. Return false
13698          * to cancel the push.
13699          * @param {Roo.HtmlEditorCore} this
13700          * @param {String} html
13701          */
13702         beforepush: true,
13703          /**
13704          * @event sync
13705          * Fires when the textarea is updated with content from the editor iframe.
13706          * @param {Roo.HtmlEditorCore} this
13707          * @param {String} html
13708          */
13709         sync: true,
13710          /**
13711          * @event push
13712          * Fires when the iframe editor is updated with content from the textarea.
13713          * @param {Roo.HtmlEditorCore} this
13714          * @param {String} html
13715          */
13716         push: true,
13717         
13718         /**
13719          * @event editorevent
13720          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13721          * @param {Roo.HtmlEditorCore} this
13722          */
13723         editorevent: true
13724     });
13725      
13726 };
13727
13728
13729 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13730
13731
13732      /**
13733      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13734      */
13735     
13736     owner : false,
13737     
13738      /**
13739      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13740      *                        Roo.resizable.
13741      */
13742     resizable : false,
13743      /**
13744      * @cfg {Number} height (in pixels)
13745      */   
13746     height: 300,
13747    /**
13748      * @cfg {Number} width (in pixels)
13749      */   
13750     width: 500,
13751     
13752     /**
13753      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13754      * 
13755      */
13756     stylesheets: false,
13757     
13758     // id of frame..
13759     frameId: false,
13760     
13761     // private properties
13762     validationEvent : false,
13763     deferHeight: true,
13764     initialized : false,
13765     activated : false,
13766     sourceEditMode : false,
13767     onFocus : Roo.emptyFn,
13768     iframePad:3,
13769     hideMode:'offsets',
13770     
13771     clearUp: true,
13772     
13773      
13774     
13775
13776     /**
13777      * Protected method that will not generally be called directly. It
13778      * is called when the editor initializes the iframe with HTML contents. Override this method if you
13779      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13780      */
13781     getDocMarkup : function(){
13782         // body styles..
13783         var st = '';
13784         Roo.log(this.stylesheets);
13785         
13786         // inherit styels from page...?? 
13787         if (this.stylesheets === false) {
13788             
13789             Roo.get(document.head).select('style').each(function(node) {
13790                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13791             });
13792             
13793             Roo.get(document.head).select('link').each(function(node) { 
13794                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13795             });
13796             
13797         } else if (!this.stylesheets.length) {
13798                 // simple..
13799                 st = '<style type="text/css">' +
13800                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13801                    '</style>';
13802         } else {
13803             Roo.each(this.stylesheets, function(s) {
13804                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13805             });
13806             
13807         }
13808         
13809         st +=  '<style type="text/css">' +
13810             'IMG { cursor: pointer } ' +
13811         '</style>';
13812
13813         
13814         return '<html><head>' + st  +
13815             //<style type="text/css">' +
13816             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13817             //'</style>' +
13818             ' </head><body class="roo-htmleditor-body"></body></html>';
13819     },
13820
13821     // private
13822     onRender : function(ct, position)
13823     {
13824         var _t = this;
13825         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13826         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13827         
13828         
13829         this.el.dom.style.border = '0 none';
13830         this.el.dom.setAttribute('tabIndex', -1);
13831         this.el.addClass('x-hidden hide');
13832         
13833         
13834         
13835         if(Roo.isIE){ // fix IE 1px bogus margin
13836             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13837         }
13838        
13839         
13840         this.frameId = Roo.id();
13841         
13842          
13843         
13844         var iframe = this.owner.wrap.createChild({
13845             tag: 'iframe',
13846             cls: 'form-control', // bootstrap..
13847             id: this.frameId,
13848             name: this.frameId,
13849             frameBorder : 'no',
13850             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13851         }, this.el
13852         );
13853         
13854         
13855         this.iframe = iframe.dom;
13856
13857          this.assignDocWin();
13858         
13859         this.doc.designMode = 'on';
13860        
13861         this.doc.open();
13862         this.doc.write(this.getDocMarkup());
13863         this.doc.close();
13864
13865         
13866         var task = { // must defer to wait for browser to be ready
13867             run : function(){
13868                 //console.log("run task?" + this.doc.readyState);
13869                 this.assignDocWin();
13870                 if(this.doc.body || this.doc.readyState == 'complete'){
13871                     try {
13872                         this.doc.designMode="on";
13873                     } catch (e) {
13874                         return;
13875                     }
13876                     Roo.TaskMgr.stop(task);
13877                     this.initEditor.defer(10, this);
13878                 }
13879             },
13880             interval : 10,
13881             duration: 10000,
13882             scope: this
13883         };
13884         Roo.TaskMgr.start(task);
13885
13886         
13887          
13888     },
13889
13890     // private
13891     onResize : function(w, h)
13892     {
13893          Roo.log('resize: ' +w + ',' + h );
13894         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13895         if(!this.iframe){
13896             return;
13897         }
13898         if(typeof w == 'number'){
13899             
13900             this.iframe.style.width = w + 'px';
13901         }
13902         if(typeof h == 'number'){
13903             
13904             this.iframe.style.height = h + 'px';
13905             if(this.doc){
13906                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13907             }
13908         }
13909         
13910     },
13911
13912     /**
13913      * Toggles the editor between standard and source edit mode.
13914      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13915      */
13916     toggleSourceEdit : function(sourceEditMode){
13917         
13918         this.sourceEditMode = sourceEditMode === true;
13919         
13920         if(this.sourceEditMode){
13921  
13922             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13923             
13924         }else{
13925             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13926             //this.iframe.className = '';
13927             this.deferFocus();
13928         }
13929         //this.setSize(this.owner.wrap.getSize());
13930         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13931     },
13932
13933     
13934   
13935
13936     /**
13937      * Protected method that will not generally be called directly. If you need/want
13938      * custom HTML cleanup, this is the method you should override.
13939      * @param {String} html The HTML to be cleaned
13940      * return {String} The cleaned HTML
13941      */
13942     cleanHtml : function(html){
13943         html = String(html);
13944         if(html.length > 5){
13945             if(Roo.isSafari){ // strip safari nonsense
13946                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13947             }
13948         }
13949         if(html == '&nbsp;'){
13950             html = '';
13951         }
13952         return html;
13953     },
13954
13955     /**
13956      * HTML Editor -> Textarea
13957      * Protected method that will not generally be called directly. Syncs the contents
13958      * of the editor iframe with the textarea.
13959      */
13960     syncValue : function(){
13961         if(this.initialized){
13962             var bd = (this.doc.body || this.doc.documentElement);
13963             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13964             var html = bd.innerHTML;
13965             if(Roo.isSafari){
13966                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13967                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13968                 if(m && m[1]){
13969                     html = '<div style="'+m[0]+'">' + html + '</div>';
13970                 }
13971             }
13972             html = this.cleanHtml(html);
13973             // fix up the special chars.. normaly like back quotes in word...
13974             // however we do not want to do this with chinese..
13975             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13976                 var cc = b.charCodeAt();
13977                 if (
13978                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13979                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13980                     (cc >= 0xf900 && cc < 0xfb00 )
13981                 ) {
13982                         return b;
13983                 }
13984                 return "&#"+cc+";" 
13985             });
13986             if(this.owner.fireEvent('beforesync', this, html) !== false){
13987                 this.el.dom.value = html;
13988                 this.owner.fireEvent('sync', this, html);
13989             }
13990         }
13991     },
13992
13993     /**
13994      * Protected method that will not generally be called directly. Pushes the value of the textarea
13995      * into the iframe editor.
13996      */
13997     pushValue : function(){
13998         if(this.initialized){
13999             var v = this.el.dom.value.trim();
14000             
14001 //            if(v.length < 1){
14002 //                v = '&#160;';
14003 //            }
14004             
14005             if(this.owner.fireEvent('beforepush', this, v) !== false){
14006                 var d = (this.doc.body || this.doc.documentElement);
14007                 d.innerHTML = v;
14008                 this.cleanUpPaste();
14009                 this.el.dom.value = d.innerHTML;
14010                 this.owner.fireEvent('push', this, v);
14011             }
14012         }
14013     },
14014
14015     // private
14016     deferFocus : function(){
14017         this.focus.defer(10, this);
14018     },
14019
14020     // doc'ed in Field
14021     focus : function(){
14022         if(this.win && !this.sourceEditMode){
14023             this.win.focus();
14024         }else{
14025             this.el.focus();
14026         }
14027     },
14028     
14029     assignDocWin: function()
14030     {
14031         var iframe = this.iframe;
14032         
14033          if(Roo.isIE){
14034             this.doc = iframe.contentWindow.document;
14035             this.win = iframe.contentWindow;
14036         } else {
14037             if (!Roo.get(this.frameId)) {
14038                 return;
14039             }
14040             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14041             this.win = Roo.get(this.frameId).dom.contentWindow;
14042         }
14043     },
14044     
14045     // private
14046     initEditor : function(){
14047         //console.log("INIT EDITOR");
14048         this.assignDocWin();
14049         
14050         
14051         
14052         this.doc.designMode="on";
14053         this.doc.open();
14054         this.doc.write(this.getDocMarkup());
14055         this.doc.close();
14056         
14057         var dbody = (this.doc.body || this.doc.documentElement);
14058         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14059         // this copies styles from the containing element into thsi one..
14060         // not sure why we need all of this..
14061         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14062         ss['background-attachment'] = 'fixed'; // w3c
14063         dbody.bgProperties = 'fixed'; // ie
14064         Roo.DomHelper.applyStyles(dbody, ss);
14065         Roo.EventManager.on(this.doc, {
14066             //'mousedown': this.onEditorEvent,
14067             'mouseup': this.onEditorEvent,
14068             'dblclick': this.onEditorEvent,
14069             'click': this.onEditorEvent,
14070             'keyup': this.onEditorEvent,
14071             buffer:100,
14072             scope: this
14073         });
14074         if(Roo.isGecko){
14075             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14076         }
14077         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14078             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14079         }
14080         this.initialized = true;
14081
14082         this.owner.fireEvent('initialize', this);
14083         this.pushValue();
14084     },
14085
14086     // private
14087     onDestroy : function(){
14088         
14089         
14090         
14091         if(this.rendered){
14092             
14093             //for (var i =0; i < this.toolbars.length;i++) {
14094             //    // fixme - ask toolbars for heights?
14095             //    this.toolbars[i].onDestroy();
14096            // }
14097             
14098             //this.wrap.dom.innerHTML = '';
14099             //this.wrap.remove();
14100         }
14101     },
14102
14103     // private
14104     onFirstFocus : function(){
14105         
14106         this.assignDocWin();
14107         
14108         
14109         this.activated = true;
14110          
14111     
14112         if(Roo.isGecko){ // prevent silly gecko errors
14113             this.win.focus();
14114             var s = this.win.getSelection();
14115             if(!s.focusNode || s.focusNode.nodeType != 3){
14116                 var r = s.getRangeAt(0);
14117                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14118                 r.collapse(true);
14119                 this.deferFocus();
14120             }
14121             try{
14122                 this.execCmd('useCSS', true);
14123                 this.execCmd('styleWithCSS', false);
14124             }catch(e){}
14125         }
14126         this.owner.fireEvent('activate', this);
14127     },
14128
14129     // private
14130     adjustFont: function(btn){
14131         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14132         //if(Roo.isSafari){ // safari
14133         //    adjust *= 2;
14134        // }
14135         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14136         if(Roo.isSafari){ // safari
14137             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14138             v =  (v < 10) ? 10 : v;
14139             v =  (v > 48) ? 48 : v;
14140             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14141             
14142         }
14143         
14144         
14145         v = Math.max(1, v+adjust);
14146         
14147         this.execCmd('FontSize', v  );
14148     },
14149
14150     onEditorEvent : function(e){
14151         this.owner.fireEvent('editorevent', this, e);
14152       //  this.updateToolbar();
14153         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14154     },
14155
14156     insertTag : function(tg)
14157     {
14158         // could be a bit smarter... -> wrap the current selected tRoo..
14159         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14160             
14161             range = this.createRange(this.getSelection());
14162             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14163             wrappingNode.appendChild(range.extractContents());
14164             range.insertNode(wrappingNode);
14165
14166             return;
14167             
14168             
14169             
14170         }
14171         this.execCmd("formatblock",   tg);
14172         
14173     },
14174     
14175     insertText : function(txt)
14176     {
14177         
14178         
14179         var range = this.createRange();
14180         range.deleteContents();
14181                //alert(Sender.getAttribute('label'));
14182                
14183         range.insertNode(this.doc.createTextNode(txt));
14184     } ,
14185     
14186      
14187
14188     /**
14189      * Executes a Midas editor command on the editor document and performs necessary focus and
14190      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14191      * @param {String} cmd The Midas command
14192      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14193      */
14194     relayCmd : function(cmd, value){
14195         this.win.focus();
14196         this.execCmd(cmd, value);
14197         this.owner.fireEvent('editorevent', this);
14198         //this.updateToolbar();
14199         this.owner.deferFocus();
14200     },
14201
14202     /**
14203      * Executes a Midas editor command directly on the editor document.
14204      * For visual commands, you should use {@link #relayCmd} instead.
14205      * <b>This should only be called after the editor is initialized.</b>
14206      * @param {String} cmd The Midas command
14207      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14208      */
14209     execCmd : function(cmd, value){
14210         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14211         this.syncValue();
14212     },
14213  
14214  
14215    
14216     /**
14217      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14218      * to insert tRoo.
14219      * @param {String} text | dom node.. 
14220      */
14221     insertAtCursor : function(text)
14222     {
14223         
14224         
14225         
14226         if(!this.activated){
14227             return;
14228         }
14229         /*
14230         if(Roo.isIE){
14231             this.win.focus();
14232             var r = this.doc.selection.createRange();
14233             if(r){
14234                 r.collapse(true);
14235                 r.pasteHTML(text);
14236                 this.syncValue();
14237                 this.deferFocus();
14238             
14239             }
14240             return;
14241         }
14242         */
14243         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14244             this.win.focus();
14245             
14246             
14247             // from jquery ui (MIT licenced)
14248             var range, node;
14249             var win = this.win;
14250             
14251             if (win.getSelection && win.getSelection().getRangeAt) {
14252                 range = win.getSelection().getRangeAt(0);
14253                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14254                 range.insertNode(node);
14255             } else if (win.document.selection && win.document.selection.createRange) {
14256                 // no firefox support
14257                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14258                 win.document.selection.createRange().pasteHTML(txt);
14259             } else {
14260                 // no firefox support
14261                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14262                 this.execCmd('InsertHTML', txt);
14263             } 
14264             
14265             this.syncValue();
14266             
14267             this.deferFocus();
14268         }
14269     },
14270  // private
14271     mozKeyPress : function(e){
14272         if(e.ctrlKey){
14273             var c = e.getCharCode(), cmd;
14274           
14275             if(c > 0){
14276                 c = String.fromCharCode(c).toLowerCase();
14277                 switch(c){
14278                     case 'b':
14279                         cmd = 'bold';
14280                         break;
14281                     case 'i':
14282                         cmd = 'italic';
14283                         break;
14284                     
14285                     case 'u':
14286                         cmd = 'underline';
14287                         break;
14288                     
14289                     case 'v':
14290                         this.cleanUpPaste.defer(100, this);
14291                         return;
14292                         
14293                 }
14294                 if(cmd){
14295                     this.win.focus();
14296                     this.execCmd(cmd);
14297                     this.deferFocus();
14298                     e.preventDefault();
14299                 }
14300                 
14301             }
14302         }
14303     },
14304
14305     // private
14306     fixKeys : function(){ // load time branching for fastest keydown performance
14307         if(Roo.isIE){
14308             return function(e){
14309                 var k = e.getKey(), r;
14310                 if(k == e.TAB){
14311                     e.stopEvent();
14312                     r = this.doc.selection.createRange();
14313                     if(r){
14314                         r.collapse(true);
14315                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14316                         this.deferFocus();
14317                     }
14318                     return;
14319                 }
14320                 
14321                 if(k == e.ENTER){
14322                     r = this.doc.selection.createRange();
14323                     if(r){
14324                         var target = r.parentElement();
14325                         if(!target || target.tagName.toLowerCase() != 'li'){
14326                             e.stopEvent();
14327                             r.pasteHTML('<br />');
14328                             r.collapse(false);
14329                             r.select();
14330                         }
14331                     }
14332                 }
14333                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14334                     this.cleanUpPaste.defer(100, this);
14335                     return;
14336                 }
14337                 
14338                 
14339             };
14340         }else if(Roo.isOpera){
14341             return function(e){
14342                 var k = e.getKey();
14343                 if(k == e.TAB){
14344                     e.stopEvent();
14345                     this.win.focus();
14346                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14347                     this.deferFocus();
14348                 }
14349                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14350                     this.cleanUpPaste.defer(100, this);
14351                     return;
14352                 }
14353                 
14354             };
14355         }else if(Roo.isSafari){
14356             return function(e){
14357                 var k = e.getKey();
14358                 
14359                 if(k == e.TAB){
14360                     e.stopEvent();
14361                     this.execCmd('InsertText','\t');
14362                     this.deferFocus();
14363                     return;
14364                 }
14365                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14366                     this.cleanUpPaste.defer(100, this);
14367                     return;
14368                 }
14369                 
14370              };
14371         }
14372     }(),
14373     
14374     getAllAncestors: function()
14375     {
14376         var p = this.getSelectedNode();
14377         var a = [];
14378         if (!p) {
14379             a.push(p); // push blank onto stack..
14380             p = this.getParentElement();
14381         }
14382         
14383         
14384         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14385             a.push(p);
14386             p = p.parentNode;
14387         }
14388         a.push(this.doc.body);
14389         return a;
14390     },
14391     lastSel : false,
14392     lastSelNode : false,
14393     
14394     
14395     getSelection : function() 
14396     {
14397         this.assignDocWin();
14398         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14399     },
14400     
14401     getSelectedNode: function() 
14402     {
14403         // this may only work on Gecko!!!
14404         
14405         // should we cache this!!!!
14406         
14407         
14408         
14409          
14410         var range = this.createRange(this.getSelection()).cloneRange();
14411         
14412         if (Roo.isIE) {
14413             var parent = range.parentElement();
14414             while (true) {
14415                 var testRange = range.duplicate();
14416                 testRange.moveToElementText(parent);
14417                 if (testRange.inRange(range)) {
14418                     break;
14419                 }
14420                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14421                     break;
14422                 }
14423                 parent = parent.parentElement;
14424             }
14425             return parent;
14426         }
14427         
14428         // is ancestor a text element.
14429         var ac =  range.commonAncestorContainer;
14430         if (ac.nodeType == 3) {
14431             ac = ac.parentNode;
14432         }
14433         
14434         var ar = ac.childNodes;
14435          
14436         var nodes = [];
14437         var other_nodes = [];
14438         var has_other_nodes = false;
14439         for (var i=0;i<ar.length;i++) {
14440             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14441                 continue;
14442             }
14443             // fullly contained node.
14444             
14445             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14446                 nodes.push(ar[i]);
14447                 continue;
14448             }
14449             
14450             // probably selected..
14451             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14452                 other_nodes.push(ar[i]);
14453                 continue;
14454             }
14455             // outer..
14456             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14457                 continue;
14458             }
14459             
14460             
14461             has_other_nodes = true;
14462         }
14463         if (!nodes.length && other_nodes.length) {
14464             nodes= other_nodes;
14465         }
14466         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14467             return false;
14468         }
14469         
14470         return nodes[0];
14471     },
14472     createRange: function(sel)
14473     {
14474         // this has strange effects when using with 
14475         // top toolbar - not sure if it's a great idea.
14476         //this.editor.contentWindow.focus();
14477         if (typeof sel != "undefined") {
14478             try {
14479                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14480             } catch(e) {
14481                 return this.doc.createRange();
14482             }
14483         } else {
14484             return this.doc.createRange();
14485         }
14486     },
14487     getParentElement: function()
14488     {
14489         
14490         this.assignDocWin();
14491         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14492         
14493         var range = this.createRange(sel);
14494          
14495         try {
14496             var p = range.commonAncestorContainer;
14497             while (p.nodeType == 3) { // text node
14498                 p = p.parentNode;
14499             }
14500             return p;
14501         } catch (e) {
14502             return null;
14503         }
14504     
14505     },
14506     /***
14507      *
14508      * Range intersection.. the hard stuff...
14509      *  '-1' = before
14510      *  '0' = hits..
14511      *  '1' = after.
14512      *         [ -- selected range --- ]
14513      *   [fail]                        [fail]
14514      *
14515      *    basically..
14516      *      if end is before start or  hits it. fail.
14517      *      if start is after end or hits it fail.
14518      *
14519      *   if either hits (but other is outside. - then it's not 
14520      *   
14521      *    
14522      **/
14523     
14524     
14525     // @see http://www.thismuchiknow.co.uk/?p=64.
14526     rangeIntersectsNode : function(range, node)
14527     {
14528         var nodeRange = node.ownerDocument.createRange();
14529         try {
14530             nodeRange.selectNode(node);
14531         } catch (e) {
14532             nodeRange.selectNodeContents(node);
14533         }
14534     
14535         var rangeStartRange = range.cloneRange();
14536         rangeStartRange.collapse(true);
14537     
14538         var rangeEndRange = range.cloneRange();
14539         rangeEndRange.collapse(false);
14540     
14541         var nodeStartRange = nodeRange.cloneRange();
14542         nodeStartRange.collapse(true);
14543     
14544         var nodeEndRange = nodeRange.cloneRange();
14545         nodeEndRange.collapse(false);
14546     
14547         return rangeStartRange.compareBoundaryPoints(
14548                  Range.START_TO_START, nodeEndRange) == -1 &&
14549                rangeEndRange.compareBoundaryPoints(
14550                  Range.START_TO_START, nodeStartRange) == 1;
14551         
14552          
14553     },
14554     rangeCompareNode : function(range, node)
14555     {
14556         var nodeRange = node.ownerDocument.createRange();
14557         try {
14558             nodeRange.selectNode(node);
14559         } catch (e) {
14560             nodeRange.selectNodeContents(node);
14561         }
14562         
14563         
14564         range.collapse(true);
14565     
14566         nodeRange.collapse(true);
14567      
14568         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14569         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
14570          
14571         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14572         
14573         var nodeIsBefore   =  ss == 1;
14574         var nodeIsAfter    = ee == -1;
14575         
14576         if (nodeIsBefore && nodeIsAfter)
14577             return 0; // outer
14578         if (!nodeIsBefore && nodeIsAfter)
14579             return 1; //right trailed.
14580         
14581         if (nodeIsBefore && !nodeIsAfter)
14582             return 2;  // left trailed.
14583         // fully contined.
14584         return 3;
14585     },
14586
14587     // private? - in a new class?
14588     cleanUpPaste :  function()
14589     {
14590         // cleans up the whole document..
14591         Roo.log('cleanuppaste');
14592         
14593         this.cleanUpChildren(this.doc.body);
14594         var clean = this.cleanWordChars(this.doc.body.innerHTML);
14595         if (clean != this.doc.body.innerHTML) {
14596             this.doc.body.innerHTML = clean;
14597         }
14598         
14599     },
14600     
14601     cleanWordChars : function(input) {// change the chars to hex code
14602         var he = Roo.HtmlEditorCore;
14603         
14604         var output = input;
14605         Roo.each(he.swapCodes, function(sw) { 
14606             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14607             
14608             output = output.replace(swapper, sw[1]);
14609         });
14610         
14611         return output;
14612     },
14613     
14614     
14615     cleanUpChildren : function (n)
14616     {
14617         if (!n.childNodes.length) {
14618             return;
14619         }
14620         for (var i = n.childNodes.length-1; i > -1 ; i--) {
14621            this.cleanUpChild(n.childNodes[i]);
14622         }
14623     },
14624     
14625     
14626         
14627     
14628     cleanUpChild : function (node)
14629     {
14630         var ed = this;
14631         //console.log(node);
14632         if (node.nodeName == "#text") {
14633             // clean up silly Windows -- stuff?
14634             return; 
14635         }
14636         if (node.nodeName == "#comment") {
14637             node.parentNode.removeChild(node);
14638             // clean up silly Windows -- stuff?
14639             return; 
14640         }
14641         
14642         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14643             // remove node.
14644             node.parentNode.removeChild(node);
14645             return;
14646             
14647         }
14648         
14649         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14650         
14651         // remove <a name=....> as rendering on yahoo mailer is borked with this.
14652         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14653         
14654         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14655         //    remove_keep_children = true;
14656         //}
14657         
14658         if (remove_keep_children) {
14659             this.cleanUpChildren(node);
14660             // inserts everything just before this node...
14661             while (node.childNodes.length) {
14662                 var cn = node.childNodes[0];
14663                 node.removeChild(cn);
14664                 node.parentNode.insertBefore(cn, node);
14665             }
14666             node.parentNode.removeChild(node);
14667             return;
14668         }
14669         
14670         if (!node.attributes || !node.attributes.length) {
14671             this.cleanUpChildren(node);
14672             return;
14673         }
14674         
14675         function cleanAttr(n,v)
14676         {
14677             
14678             if (v.match(/^\./) || v.match(/^\//)) {
14679                 return;
14680             }
14681             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14682                 return;
14683             }
14684             if (v.match(/^#/)) {
14685                 return;
14686             }
14687 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14688             node.removeAttribute(n);
14689             
14690         }
14691         
14692         function cleanStyle(n,v)
14693         {
14694             if (v.match(/expression/)) { //XSS?? should we even bother..
14695                 node.removeAttribute(n);
14696                 return;
14697             }
14698             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14699             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14700             
14701             
14702             var parts = v.split(/;/);
14703             var clean = [];
14704             
14705             Roo.each(parts, function(p) {
14706                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14707                 if (!p.length) {
14708                     return true;
14709                 }
14710                 var l = p.split(':').shift().replace(/\s+/g,'');
14711                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14712                 
14713                 if ( cblack.indexOf(l) > -1) {
14714 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14715                     //node.removeAttribute(n);
14716                     return true;
14717                 }
14718                 //Roo.log()
14719                 // only allow 'c whitelisted system attributes'
14720                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14721 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14722                     //node.removeAttribute(n);
14723                     return true;
14724                 }
14725                 
14726                 
14727                  
14728                 
14729                 clean.push(p);
14730                 return true;
14731             });
14732             if (clean.length) { 
14733                 node.setAttribute(n, clean.join(';'));
14734             } else {
14735                 node.removeAttribute(n);
14736             }
14737             
14738         }
14739         
14740         
14741         for (var i = node.attributes.length-1; i > -1 ; i--) {
14742             var a = node.attributes[i];
14743             //console.log(a);
14744             
14745             if (a.name.toLowerCase().substr(0,2)=='on')  {
14746                 node.removeAttribute(a.name);
14747                 continue;
14748             }
14749             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14750                 node.removeAttribute(a.name);
14751                 continue;
14752             }
14753             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14754                 cleanAttr(a.name,a.value); // fixme..
14755                 continue;
14756             }
14757             if (a.name == 'style') {
14758                 cleanStyle(a.name,a.value);
14759                 continue;
14760             }
14761             /// clean up MS crap..
14762             // tecnically this should be a list of valid class'es..
14763             
14764             
14765             if (a.name == 'class') {
14766                 if (a.value.match(/^Mso/)) {
14767                     node.className = '';
14768                 }
14769                 
14770                 if (a.value.match(/body/)) {
14771                     node.className = '';
14772                 }
14773                 continue;
14774             }
14775             
14776             // style cleanup!?
14777             // class cleanup?
14778             
14779         }
14780         
14781         
14782         this.cleanUpChildren(node);
14783         
14784         
14785     }
14786     
14787     
14788     // hide stuff that is not compatible
14789     /**
14790      * @event blur
14791      * @hide
14792      */
14793     /**
14794      * @event change
14795      * @hide
14796      */
14797     /**
14798      * @event focus
14799      * @hide
14800      */
14801     /**
14802      * @event specialkey
14803      * @hide
14804      */
14805     /**
14806      * @cfg {String} fieldClass @hide
14807      */
14808     /**
14809      * @cfg {String} focusClass @hide
14810      */
14811     /**
14812      * @cfg {String} autoCreate @hide
14813      */
14814     /**
14815      * @cfg {String} inputType @hide
14816      */
14817     /**
14818      * @cfg {String} invalidClass @hide
14819      */
14820     /**
14821      * @cfg {String} invalidText @hide
14822      */
14823     /**
14824      * @cfg {String} msgFx @hide
14825      */
14826     /**
14827      * @cfg {String} validateOnBlur @hide
14828      */
14829 });
14830
14831 Roo.HtmlEditorCore.white = [
14832         'area', 'br', 'img', 'input', 'hr', 'wbr',
14833         
14834        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14835        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14836        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14837        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14838        'table',   'ul',         'xmp', 
14839        
14840        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14841       'thead',   'tr', 
14842      
14843       'dir', 'menu', 'ol', 'ul', 'dl',
14844        
14845       'embed',  'object'
14846 ];
14847
14848
14849 Roo.HtmlEditorCore.black = [
14850     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14851         'applet', // 
14852         'base',   'basefont', 'bgsound', 'blink',  'body', 
14853         'frame',  'frameset', 'head',    'html',   'ilayer', 
14854         'iframe', 'layer',  'link',     'meta',    'object',   
14855         'script', 'style' ,'title',  'xml' // clean later..
14856 ];
14857 Roo.HtmlEditorCore.clean = [
14858     'script', 'style', 'title', 'xml'
14859 ];
14860 Roo.HtmlEditorCore.remove = [
14861     'font'
14862 ];
14863 // attributes..
14864
14865 Roo.HtmlEditorCore.ablack = [
14866     'on'
14867 ];
14868     
14869 Roo.HtmlEditorCore.aclean = [ 
14870     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14871 ];
14872
14873 // protocols..
14874 Roo.HtmlEditorCore.pwhite= [
14875         'http',  'https',  'mailto'
14876 ];
14877
14878 // white listed style attributes.
14879 Roo.HtmlEditorCore.cwhite= [
14880       //  'text-align', /// default is to allow most things..
14881       
14882          
14883 //        'font-size'//??
14884 ];
14885
14886 // black listed style attributes.
14887 Roo.HtmlEditorCore.cblack= [
14888       //  'font-size' -- this can be set by the project 
14889 ];
14890
14891
14892 Roo.HtmlEditorCore.swapCodes   =[ 
14893     [    8211, "--" ], 
14894     [    8212, "--" ], 
14895     [    8216,  "'" ],  
14896     [    8217, "'" ],  
14897     [    8220, '"' ],  
14898     [    8221, '"' ],  
14899     [    8226, "*" ],  
14900     [    8230, "..." ]
14901 ]; 
14902
14903     /*
14904  * - LGPL
14905  *
14906  * HtmlEditor
14907  * 
14908  */
14909
14910 /**
14911  * @class Roo.bootstrap.HtmlEditor
14912  * @extends Roo.bootstrap.TextArea
14913  * Bootstrap HtmlEditor class
14914
14915  * @constructor
14916  * Create a new HtmlEditor
14917  * @param {Object} config The config object
14918  */
14919
14920 Roo.bootstrap.HtmlEditor = function(config){
14921     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14922     if (!this.toolbars) {
14923         this.toolbars = [];
14924     }
14925     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14926     this.addEvents({
14927             /**
14928              * @event initialize
14929              * Fires when the editor is fully initialized (including the iframe)
14930              * @param {HtmlEditor} this
14931              */
14932             initialize: true,
14933             /**
14934              * @event activate
14935              * Fires when the editor is first receives the focus. Any insertion must wait
14936              * until after this event.
14937              * @param {HtmlEditor} this
14938              */
14939             activate: true,
14940              /**
14941              * @event beforesync
14942              * Fires before the textarea is updated with content from the editor iframe. Return false
14943              * to cancel the sync.
14944              * @param {HtmlEditor} this
14945              * @param {String} html
14946              */
14947             beforesync: true,
14948              /**
14949              * @event beforepush
14950              * Fires before the iframe editor is updated with content from the textarea. Return false
14951              * to cancel the push.
14952              * @param {HtmlEditor} this
14953              * @param {String} html
14954              */
14955             beforepush: true,
14956              /**
14957              * @event sync
14958              * Fires when the textarea is updated with content from the editor iframe.
14959              * @param {HtmlEditor} this
14960              * @param {String} html
14961              */
14962             sync: true,
14963              /**
14964              * @event push
14965              * Fires when the iframe editor is updated with content from the textarea.
14966              * @param {HtmlEditor} this
14967              * @param {String} html
14968              */
14969             push: true,
14970              /**
14971              * @event editmodechange
14972              * Fires when the editor switches edit modes
14973              * @param {HtmlEditor} this
14974              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14975              */
14976             editmodechange: true,
14977             /**
14978              * @event editorevent
14979              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14980              * @param {HtmlEditor} this
14981              */
14982             editorevent: true,
14983             /**
14984              * @event firstfocus
14985              * Fires when on first focus - needed by toolbars..
14986              * @param {HtmlEditor} this
14987              */
14988             firstfocus: true,
14989             /**
14990              * @event autosave
14991              * Auto save the htmlEditor value as a file into Events
14992              * @param {HtmlEditor} this
14993              */
14994             autosave: true,
14995             /**
14996              * @event savedpreview
14997              * preview the saved version of htmlEditor
14998              * @param {HtmlEditor} this
14999              */
15000             savedpreview: true
15001         });
15002 };
15003
15004
15005 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15006     
15007     
15008       /**
15009      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15010      */
15011     toolbars : false,
15012    
15013      /**
15014      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15015      *                        Roo.resizable.
15016      */
15017     resizable : false,
15018      /**
15019      * @cfg {Number} height (in pixels)
15020      */   
15021     height: 300,
15022    /**
15023      * @cfg {Number} width (in pixels)
15024      */   
15025     width: false,
15026     
15027     /**
15028      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15029      * 
15030      */
15031     stylesheets: false,
15032     
15033     // id of frame..
15034     frameId: false,
15035     
15036     // private properties
15037     validationEvent : false,
15038     deferHeight: true,
15039     initialized : false,
15040     activated : false,
15041     
15042     onFocus : Roo.emptyFn,
15043     iframePad:3,
15044     hideMode:'offsets',
15045     
15046     
15047     tbContainer : false,
15048     
15049     toolbarContainer :function() {
15050         return this.wrap.select('.x-html-editor-tb',true).first();
15051     },
15052
15053     /**
15054      * Protected method that will not generally be called directly. It
15055      * is called when the editor creates its toolbar. Override this method if you need to
15056      * add custom toolbar buttons.
15057      * @param {HtmlEditor} editor
15058      */
15059     createToolbar : function(){
15060         
15061         Roo.log("create toolbars");
15062         
15063         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15064         this.toolbars[0].render(this.toolbarContainer());
15065         
15066         return;
15067         
15068 //        if (!editor.toolbars || !editor.toolbars.length) {
15069 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15070 //        }
15071 //        
15072 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15073 //            editor.toolbars[i] = Roo.factory(
15074 //                    typeof(editor.toolbars[i]) == 'string' ?
15075 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15076 //                Roo.bootstrap.HtmlEditor);
15077 //            editor.toolbars[i].init(editor);
15078 //        }
15079     },
15080
15081      
15082     // private
15083     onRender : function(ct, position)
15084     {
15085        // Roo.log("Call onRender: " + this.xtype);
15086         var _t = this;
15087         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15088       
15089         this.wrap = this.inputEl().wrap({
15090             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15091         });
15092         
15093         this.editorcore.onRender(ct, position);
15094          
15095         if (this.resizable) {
15096             this.resizeEl = new Roo.Resizable(this.wrap, {
15097                 pinned : true,
15098                 wrap: true,
15099                 dynamic : true,
15100                 minHeight : this.height,
15101                 height: this.height,
15102                 handles : this.resizable,
15103                 width: this.width,
15104                 listeners : {
15105                     resize : function(r, w, h) {
15106                         _t.onResize(w,h); // -something
15107                     }
15108                 }
15109             });
15110             
15111         }
15112         this.createToolbar(this);
15113        
15114         
15115         if(!this.width && this.resizable){
15116             this.setSize(this.wrap.getSize());
15117         }
15118         if (this.resizeEl) {
15119             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15120             // should trigger onReize..
15121         }
15122         
15123     },
15124
15125     // private
15126     onResize : function(w, h)
15127     {
15128         Roo.log('resize: ' +w + ',' + h );
15129         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15130         var ew = false;
15131         var eh = false;
15132         
15133         if(this.inputEl() ){
15134             if(typeof w == 'number'){
15135                 var aw = w - this.wrap.getFrameWidth('lr');
15136                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15137                 ew = aw;
15138             }
15139             if(typeof h == 'number'){
15140                  var tbh = -11;  // fixme it needs to tool bar size!
15141                 for (var i =0; i < this.toolbars.length;i++) {
15142                     // fixme - ask toolbars for heights?
15143                     tbh += this.toolbars[i].el.getHeight();
15144                     //if (this.toolbars[i].footer) {
15145                     //    tbh += this.toolbars[i].footer.el.getHeight();
15146                     //}
15147                 }
15148               
15149                 
15150                 
15151                 
15152                 
15153                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15154                 ah -= 5; // knock a few pixes off for look..
15155                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15156                 var eh = ah;
15157             }
15158         }
15159         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15160         this.editorcore.onResize(ew,eh);
15161         
15162     },
15163
15164     /**
15165      * Toggles the editor between standard and source edit mode.
15166      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15167      */
15168     toggleSourceEdit : function(sourceEditMode)
15169     {
15170         this.editorcore.toggleSourceEdit(sourceEditMode);
15171         
15172         if(this.editorcore.sourceEditMode){
15173             Roo.log('editor - showing textarea');
15174             
15175 //            Roo.log('in');
15176 //            Roo.log(this.syncValue());
15177             this.syncValue();
15178             this.inputEl().removeClass('hide');
15179             this.inputEl().dom.removeAttribute('tabIndex');
15180             this.inputEl().focus();
15181         }else{
15182             Roo.log('editor - hiding textarea');
15183 //            Roo.log('out')
15184 //            Roo.log(this.pushValue()); 
15185             this.pushValue();
15186             
15187             this.inputEl().addClass('hide');
15188             this.inputEl().dom.setAttribute('tabIndex', -1);
15189             //this.deferFocus();
15190         }
15191          
15192         if(this.resizable){
15193             this.setSize(this.wrap.getSize());
15194         }
15195         
15196         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15197     },
15198  
15199     // private (for BoxComponent)
15200     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15201
15202     // private (for BoxComponent)
15203     getResizeEl : function(){
15204         return this.wrap;
15205     },
15206
15207     // private (for BoxComponent)
15208     getPositionEl : function(){
15209         return this.wrap;
15210     },
15211
15212     // private
15213     initEvents : function(){
15214         this.originalValue = this.getValue();
15215     },
15216
15217 //    /**
15218 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15219 //     * @method
15220 //     */
15221 //    markInvalid : Roo.emptyFn,
15222 //    /**
15223 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15224 //     * @method
15225 //     */
15226 //    clearInvalid : Roo.emptyFn,
15227
15228     setValue : function(v){
15229         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15230         this.editorcore.pushValue();
15231     },
15232
15233      
15234     // private
15235     deferFocus : function(){
15236         this.focus.defer(10, this);
15237     },
15238
15239     // doc'ed in Field
15240     focus : function(){
15241         this.editorcore.focus();
15242         
15243     },
15244       
15245
15246     // private
15247     onDestroy : function(){
15248         
15249         
15250         
15251         if(this.rendered){
15252             
15253             for (var i =0; i < this.toolbars.length;i++) {
15254                 // fixme - ask toolbars for heights?
15255                 this.toolbars[i].onDestroy();
15256             }
15257             
15258             this.wrap.dom.innerHTML = '';
15259             this.wrap.remove();
15260         }
15261     },
15262
15263     // private
15264     onFirstFocus : function(){
15265         //Roo.log("onFirstFocus");
15266         this.editorcore.onFirstFocus();
15267          for (var i =0; i < this.toolbars.length;i++) {
15268             this.toolbars[i].onFirstFocus();
15269         }
15270         
15271     },
15272     
15273     // private
15274     syncValue : function()
15275     {   
15276         this.editorcore.syncValue();
15277     },
15278     
15279     pushValue : function()
15280     {   
15281         this.editorcore.pushValue();
15282     }
15283      
15284     
15285     // hide stuff that is not compatible
15286     /**
15287      * @event blur
15288      * @hide
15289      */
15290     /**
15291      * @event change
15292      * @hide
15293      */
15294     /**
15295      * @event focus
15296      * @hide
15297      */
15298     /**
15299      * @event specialkey
15300      * @hide
15301      */
15302     /**
15303      * @cfg {String} fieldClass @hide
15304      */
15305     /**
15306      * @cfg {String} focusClass @hide
15307      */
15308     /**
15309      * @cfg {String} autoCreate @hide
15310      */
15311     /**
15312      * @cfg {String} inputType @hide
15313      */
15314     /**
15315      * @cfg {String} invalidClass @hide
15316      */
15317     /**
15318      * @cfg {String} invalidText @hide
15319      */
15320     /**
15321      * @cfg {String} msgFx @hide
15322      */
15323     /**
15324      * @cfg {String} validateOnBlur @hide
15325      */
15326 });
15327  
15328     
15329    
15330    
15331    
15332       
15333
15334 /**
15335  * @class Roo.bootstrap.HtmlEditorToolbar1
15336  * Basic Toolbar
15337  * 
15338  * Usage:
15339  *
15340  new Roo.bootstrap.HtmlEditor({
15341     ....
15342     toolbars : [
15343         new Roo.bootstrap.HtmlEditorToolbar1({
15344             disable : { fonts: 1 , format: 1, ..., ... , ...],
15345             btns : [ .... ]
15346         })
15347     }
15348      
15349  * 
15350  * @cfg {Object} disable List of elements to disable..
15351  * @cfg {Array} btns List of additional buttons.
15352  * 
15353  * 
15354  * NEEDS Extra CSS? 
15355  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15356  */
15357  
15358 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15359 {
15360     
15361     Roo.apply(this, config);
15362     
15363     // default disabled, based on 'good practice'..
15364     this.disable = this.disable || {};
15365     Roo.applyIf(this.disable, {
15366         fontSize : true,
15367         colors : true,
15368         specialElements : true
15369     });
15370     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15371     
15372     this.editor = config.editor;
15373     this.editorcore = config.editor.editorcore;
15374     
15375     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15376     
15377     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15378     // dont call parent... till later.
15379 }
15380 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
15381     
15382     
15383     bar : true,
15384     
15385     editor : false,
15386     editorcore : false,
15387     
15388     
15389     formats : [
15390         "p" ,  
15391         "h1","h2","h3","h4","h5","h6", 
15392         "pre", "code", 
15393         "abbr", "acronym", "address", "cite", "samp", "var",
15394         'div','span'
15395     ],
15396     
15397     onRender : function(ct, position)
15398     {
15399        // Roo.log("Call onRender: " + this.xtype);
15400         
15401        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15402        Roo.log(this.el);
15403        this.el.dom.style.marginBottom = '0';
15404        var _this = this;
15405        var editorcore = this.editorcore;
15406        var editor= this.editor;
15407        
15408        var children = [];
15409        var btn = function(id,cmd , toggle, handler){
15410        
15411             var  event = toggle ? 'toggle' : 'click';
15412        
15413             var a = {
15414                 size : 'sm',
15415                 xtype: 'Button',
15416                 xns: Roo.bootstrap,
15417                 glyphicon : id,
15418                 cmd : id || cmd,
15419                 enableToggle:toggle !== false,
15420                 //html : 'submit'
15421                 pressed : toggle ? false : null,
15422                 listeners : {}
15423             }
15424             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15425                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15426             }
15427             children.push(a);
15428             return a;
15429        }
15430         
15431         var style = {
15432                 xtype: 'Button',
15433                 size : 'sm',
15434                 xns: Roo.bootstrap,
15435                 glyphicon : 'font',
15436                 //html : 'submit'
15437                 menu : {
15438                     xtype: 'Menu',
15439                     xns: Roo.bootstrap,
15440                     items:  []
15441                 }
15442         };
15443         Roo.each(this.formats, function(f) {
15444             style.menu.items.push({
15445                 xtype :'MenuItem',
15446                 xns: Roo.bootstrap,
15447                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15448                 tagname : f,
15449                 listeners : {
15450                     click : function()
15451                     {
15452                         editorcore.insertTag(this.tagname);
15453                         editor.focus();
15454                     }
15455                 }
15456                 
15457             });
15458         });
15459          children.push(style);   
15460             
15461             
15462         btn('bold',false,true);
15463         btn('italic',false,true);
15464         btn('align-left', 'justifyleft',true);
15465         btn('align-center', 'justifycenter',true);
15466         btn('align-right' , 'justifyright',true);
15467         btn('link', false, false, function(btn) {
15468             //Roo.log("create link?");
15469             var url = prompt(this.createLinkText, this.defaultLinkValue);
15470             if(url && url != 'http:/'+'/'){
15471                 this.editorcore.relayCmd('createlink', url);
15472             }
15473         }),
15474         btn('list','insertunorderedlist',true);
15475         btn('pencil', false,true, function(btn){
15476                 Roo.log(this);
15477                 
15478                 this.toggleSourceEdit(btn.pressed);
15479         });
15480         /*
15481         var cog = {
15482                 xtype: 'Button',
15483                 size : 'sm',
15484                 xns: Roo.bootstrap,
15485                 glyphicon : 'cog',
15486                 //html : 'submit'
15487                 menu : {
15488                     xtype: 'Menu',
15489                     xns: Roo.bootstrap,
15490                     items:  []
15491                 }
15492         };
15493         
15494         cog.menu.items.push({
15495             xtype :'MenuItem',
15496             xns: Roo.bootstrap,
15497             html : Clean styles,
15498             tagname : f,
15499             listeners : {
15500                 click : function()
15501                 {
15502                     editorcore.insertTag(this.tagname);
15503                     editor.focus();
15504                 }
15505             }
15506             
15507         });
15508        */
15509         
15510          
15511        this.xtype = 'Navbar';
15512         
15513         for(var i=0;i< children.length;i++) {
15514             
15515             this.buttons.add(this.addxtypeChild(children[i]));
15516             
15517         }
15518         
15519         editor.on('editorevent', this.updateToolbar, this);
15520     },
15521     onBtnClick : function(id)
15522     {
15523        this.editorcore.relayCmd(id);
15524        this.editorcore.focus();
15525     },
15526     
15527     /**
15528      * Protected method that will not generally be called directly. It triggers
15529      * a toolbar update by reading the markup state of the current selection in the editor.
15530      */
15531     updateToolbar: function(){
15532
15533         if(!this.editorcore.activated){
15534             this.editor.onFirstFocus(); // is this neeed?
15535             return;
15536         }
15537
15538         var btns = this.buttons; 
15539         var doc = this.editorcore.doc;
15540         btns.get('bold').setActive(doc.queryCommandState('bold'));
15541         btns.get('italic').setActive(doc.queryCommandState('italic'));
15542         //btns.get('underline').setActive(doc.queryCommandState('underline'));
15543         
15544         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15545         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15546         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15547         
15548         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15549         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15550          /*
15551         
15552         var ans = this.editorcore.getAllAncestors();
15553         if (this.formatCombo) {
15554             
15555             
15556             var store = this.formatCombo.store;
15557             this.formatCombo.setValue("");
15558             for (var i =0; i < ans.length;i++) {
15559                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15560                     // select it..
15561                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15562                     break;
15563                 }
15564             }
15565         }
15566         
15567         
15568         
15569         // hides menus... - so this cant be on a menu...
15570         Roo.bootstrap.MenuMgr.hideAll();
15571         */
15572         Roo.bootstrap.MenuMgr.hideAll();
15573         //this.editorsyncValue();
15574     },
15575     onFirstFocus: function() {
15576         this.buttons.each(function(item){
15577            item.enable();
15578         });
15579     },
15580     toggleSourceEdit : function(sourceEditMode){
15581         
15582           
15583         if(sourceEditMode){
15584             Roo.log("disabling buttons");
15585            this.buttons.each( function(item){
15586                 if(item.cmd != 'pencil'){
15587                     item.disable();
15588                 }
15589             });
15590           
15591         }else{
15592             Roo.log("enabling buttons");
15593             if(this.editorcore.initialized){
15594                 this.buttons.each( function(item){
15595                     item.enable();
15596                 });
15597             }
15598             
15599         }
15600         Roo.log("calling toggole on editor");
15601         // tell the editor that it's been pressed..
15602         this.editor.toggleSourceEdit(sourceEditMode);
15603        
15604     }
15605 });
15606
15607
15608
15609
15610
15611 /**
15612  * @class Roo.bootstrap.Table.AbstractSelectionModel
15613  * @extends Roo.util.Observable
15614  * Abstract base class for grid SelectionModels.  It provides the interface that should be
15615  * implemented by descendant classes.  This class should not be directly instantiated.
15616  * @constructor
15617  */
15618 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15619     this.locked = false;
15620     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15621 };
15622
15623
15624 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
15625     /** @ignore Called by the grid automatically. Do not call directly. */
15626     init : function(grid){
15627         this.grid = grid;
15628         this.initEvents();
15629     },
15630
15631     /**
15632      * Locks the selections.
15633      */
15634     lock : function(){
15635         this.locked = true;
15636     },
15637
15638     /**
15639      * Unlocks the selections.
15640      */
15641     unlock : function(){
15642         this.locked = false;
15643     },
15644
15645     /**
15646      * Returns true if the selections are locked.
15647      * @return {Boolean}
15648      */
15649     isLocked : function(){
15650         return this.locked;
15651     }
15652 });
15653 /**
15654  * @class Roo.bootstrap.Table.ColumnModel
15655  * @extends Roo.util.Observable
15656  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15657  * the columns in the table.
15658  
15659  * @constructor
15660  * @param {Object} config An Array of column config objects. See this class's
15661  * config objects for details.
15662 */
15663 Roo.bootstrap.Table.ColumnModel = function(config){
15664         /**
15665      * The config passed into the constructor
15666      */
15667     this.config = config;
15668     this.lookup = {};
15669
15670     // if no id, create one
15671     // if the column does not have a dataIndex mapping,
15672     // map it to the order it is in the config
15673     for(var i = 0, len = config.length; i < len; i++){
15674         var c = config[i];
15675         if(typeof c.dataIndex == "undefined"){
15676             c.dataIndex = i;
15677         }
15678         if(typeof c.renderer == "string"){
15679             c.renderer = Roo.util.Format[c.renderer];
15680         }
15681         if(typeof c.id == "undefined"){
15682             c.id = Roo.id();
15683         }
15684 //        if(c.editor && c.editor.xtype){
15685 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15686 //        }
15687 //        if(c.editor && c.editor.isFormField){
15688 //            c.editor = new Roo.grid.GridEditor(c.editor);
15689 //        }
15690
15691         this.lookup[c.id] = c;
15692     }
15693
15694     /**
15695      * The width of columns which have no width specified (defaults to 100)
15696      * @type Number
15697      */
15698     this.defaultWidth = 100;
15699
15700     /**
15701      * Default sortable of columns which have no sortable specified (defaults to false)
15702      * @type Boolean
15703      */
15704     this.defaultSortable = false;
15705
15706     this.addEvents({
15707         /**
15708              * @event widthchange
15709              * Fires when the width of a column changes.
15710              * @param {ColumnModel} this
15711              * @param {Number} columnIndex The column index
15712              * @param {Number} newWidth The new width
15713              */
15714             "widthchange": true,
15715         /**
15716              * @event headerchange
15717              * Fires when the text of a header changes.
15718              * @param {ColumnModel} this
15719              * @param {Number} columnIndex The column index
15720              * @param {Number} newText The new header text
15721              */
15722             "headerchange": true,
15723         /**
15724              * @event hiddenchange
15725              * Fires when a column is hidden or "unhidden".
15726              * @param {ColumnModel} this
15727              * @param {Number} columnIndex The column index
15728              * @param {Boolean} hidden true if hidden, false otherwise
15729              */
15730             "hiddenchange": true,
15731             /**
15732          * @event columnmoved
15733          * Fires when a column is moved.
15734          * @param {ColumnModel} this
15735          * @param {Number} oldIndex
15736          * @param {Number} newIndex
15737          */
15738         "columnmoved" : true,
15739         /**
15740          * @event columlockchange
15741          * Fires when a column's locked state is changed
15742          * @param {ColumnModel} this
15743          * @param {Number} colIndex
15744          * @param {Boolean} locked true if locked
15745          */
15746         "columnlockchange" : true
15747     });
15748     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15749 };
15750 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15751     /**
15752      * @cfg {String} header The header text to display in the Grid view.
15753      */
15754     /**
15755      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15756      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15757      * specified, the column's index is used as an index into the Record's data Array.
15758      */
15759     /**
15760      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15761      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15762      */
15763     /**
15764      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15765      * Defaults to the value of the {@link #defaultSortable} property.
15766      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15767      */
15768     /**
15769      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
15770      */
15771     /**
15772      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
15773      */
15774     /**
15775      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15776      */
15777     /**
15778      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15779      */
15780     /**
15781      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15782      * given the cell's data value. See {@link #setRenderer}. If not specified, the
15783      * default renderer uses the raw data value.
15784      */
15785     /**
15786      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
15787      */
15788
15789     /**
15790      * Returns the id of the column at the specified index.
15791      * @param {Number} index The column index
15792      * @return {String} the id
15793      */
15794     getColumnId : function(index){
15795         return this.config[index].id;
15796     },
15797
15798     /**
15799      * Returns the column for a specified id.
15800      * @param {String} id The column id
15801      * @return {Object} the column
15802      */
15803     getColumnById : function(id){
15804         return this.lookup[id];
15805     },
15806
15807     
15808     /**
15809      * Returns the column for a specified dataIndex.
15810      * @param {String} dataIndex The column dataIndex
15811      * @return {Object|Boolean} the column or false if not found
15812      */
15813     getColumnByDataIndex: function(dataIndex){
15814         var index = this.findColumnIndex(dataIndex);
15815         return index > -1 ? this.config[index] : false;
15816     },
15817     
15818     /**
15819      * Returns the index for a specified column id.
15820      * @param {String} id The column id
15821      * @return {Number} the index, or -1 if not found
15822      */
15823     getIndexById : function(id){
15824         for(var i = 0, len = this.config.length; i < len; i++){
15825             if(this.config[i].id == id){
15826                 return i;
15827             }
15828         }
15829         return -1;
15830     },
15831     
15832     /**
15833      * Returns the index for a specified column dataIndex.
15834      * @param {String} dataIndex The column dataIndex
15835      * @return {Number} the index, or -1 if not found
15836      */
15837     
15838     findColumnIndex : function(dataIndex){
15839         for(var i = 0, len = this.config.length; i < len; i++){
15840             if(this.config[i].dataIndex == dataIndex){
15841                 return i;
15842             }
15843         }
15844         return -1;
15845     },
15846     
15847     
15848     moveColumn : function(oldIndex, newIndex){
15849         var c = this.config[oldIndex];
15850         this.config.splice(oldIndex, 1);
15851         this.config.splice(newIndex, 0, c);
15852         this.dataMap = null;
15853         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15854     },
15855
15856     isLocked : function(colIndex){
15857         return this.config[colIndex].locked === true;
15858     },
15859
15860     setLocked : function(colIndex, value, suppressEvent){
15861         if(this.isLocked(colIndex) == value){
15862             return;
15863         }
15864         this.config[colIndex].locked = value;
15865         if(!suppressEvent){
15866             this.fireEvent("columnlockchange", this, colIndex, value);
15867         }
15868     },
15869
15870     getTotalLockedWidth : function(){
15871         var totalWidth = 0;
15872         for(var i = 0; i < this.config.length; i++){
15873             if(this.isLocked(i) && !this.isHidden(i)){
15874                 this.totalWidth += this.getColumnWidth(i);
15875             }
15876         }
15877         return totalWidth;
15878     },
15879
15880     getLockedCount : function(){
15881         for(var i = 0, len = this.config.length; i < len; i++){
15882             if(!this.isLocked(i)){
15883                 return i;
15884             }
15885         }
15886     },
15887
15888     /**
15889      * Returns the number of columns.
15890      * @return {Number}
15891      */
15892     getColumnCount : function(visibleOnly){
15893         if(visibleOnly === true){
15894             var c = 0;
15895             for(var i = 0, len = this.config.length; i < len; i++){
15896                 if(!this.isHidden(i)){
15897                     c++;
15898                 }
15899             }
15900             return c;
15901         }
15902         return this.config.length;
15903     },
15904
15905     /**
15906      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15907      * @param {Function} fn
15908      * @param {Object} scope (optional)
15909      * @return {Array} result
15910      */
15911     getColumnsBy : function(fn, scope){
15912         var r = [];
15913         for(var i = 0, len = this.config.length; i < len; i++){
15914             var c = this.config[i];
15915             if(fn.call(scope||this, c, i) === true){
15916                 r[r.length] = c;
15917             }
15918         }
15919         return r;
15920     },
15921
15922     /**
15923      * Returns true if the specified column is sortable.
15924      * @param {Number} col The column index
15925      * @return {Boolean}
15926      */
15927     isSortable : function(col){
15928         if(typeof this.config[col].sortable == "undefined"){
15929             return this.defaultSortable;
15930         }
15931         return this.config[col].sortable;
15932     },
15933
15934     /**
15935      * Returns the rendering (formatting) function defined for the column.
15936      * @param {Number} col The column index.
15937      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15938      */
15939     getRenderer : function(col){
15940         if(!this.config[col].renderer){
15941             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15942         }
15943         return this.config[col].renderer;
15944     },
15945
15946     /**
15947      * Sets the rendering (formatting) function for a column.
15948      * @param {Number} col The column index
15949      * @param {Function} fn The function to use to process the cell's raw data
15950      * to return HTML markup for the grid view. The render function is called with
15951      * the following parameters:<ul>
15952      * <li>Data value.</li>
15953      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15954      * <li>css A CSS style string to apply to the table cell.</li>
15955      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15956      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15957      * <li>Row index</li>
15958      * <li>Column index</li>
15959      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15960      */
15961     setRenderer : function(col, fn){
15962         this.config[col].renderer = fn;
15963     },
15964
15965     /**
15966      * Returns the width for the specified column.
15967      * @param {Number} col The column index
15968      * @return {Number}
15969      */
15970     getColumnWidth : function(col){
15971         return this.config[col].width * 1 || this.defaultWidth;
15972     },
15973
15974     /**
15975      * Sets the width for a column.
15976      * @param {Number} col The column index
15977      * @param {Number} width The new width
15978      */
15979     setColumnWidth : function(col, width, suppressEvent){
15980         this.config[col].width = width;
15981         this.totalWidth = null;
15982         if(!suppressEvent){
15983              this.fireEvent("widthchange", this, col, width);
15984         }
15985     },
15986
15987     /**
15988      * Returns the total width of all columns.
15989      * @param {Boolean} includeHidden True to include hidden column widths
15990      * @return {Number}
15991      */
15992     getTotalWidth : function(includeHidden){
15993         if(!this.totalWidth){
15994             this.totalWidth = 0;
15995             for(var i = 0, len = this.config.length; i < len; i++){
15996                 if(includeHidden || !this.isHidden(i)){
15997                     this.totalWidth += this.getColumnWidth(i);
15998                 }
15999             }
16000         }
16001         return this.totalWidth;
16002     },
16003
16004     /**
16005      * Returns the header for the specified column.
16006      * @param {Number} col The column index
16007      * @return {String}
16008      */
16009     getColumnHeader : function(col){
16010         return this.config[col].header;
16011     },
16012
16013     /**
16014      * Sets the header for a column.
16015      * @param {Number} col The column index
16016      * @param {String} header The new header
16017      */
16018     setColumnHeader : function(col, header){
16019         this.config[col].header = header;
16020         this.fireEvent("headerchange", this, col, header);
16021     },
16022
16023     /**
16024      * Returns the tooltip for the specified column.
16025      * @param {Number} col The column index
16026      * @return {String}
16027      */
16028     getColumnTooltip : function(col){
16029             return this.config[col].tooltip;
16030     },
16031     /**
16032      * Sets the tooltip for a column.
16033      * @param {Number} col The column index
16034      * @param {String} tooltip The new tooltip
16035      */
16036     setColumnTooltip : function(col, tooltip){
16037             this.config[col].tooltip = tooltip;
16038     },
16039
16040     /**
16041      * Returns the dataIndex for the specified column.
16042      * @param {Number} col The column index
16043      * @return {Number}
16044      */
16045     getDataIndex : function(col){
16046         return this.config[col].dataIndex;
16047     },
16048
16049     /**
16050      * Sets the dataIndex for a column.
16051      * @param {Number} col The column index
16052      * @param {Number} dataIndex The new dataIndex
16053      */
16054     setDataIndex : function(col, dataIndex){
16055         this.config[col].dataIndex = dataIndex;
16056     },
16057
16058     
16059     
16060     /**
16061      * Returns true if the cell is editable.
16062      * @param {Number} colIndex The column index
16063      * @param {Number} rowIndex The row index
16064      * @return {Boolean}
16065      */
16066     isCellEditable : function(colIndex, rowIndex){
16067         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16068     },
16069
16070     /**
16071      * Returns the editor defined for the cell/column.
16072      * return false or null to disable editing.
16073      * @param {Number} colIndex The column index
16074      * @param {Number} rowIndex The row index
16075      * @return {Object}
16076      */
16077     getCellEditor : function(colIndex, rowIndex){
16078         return this.config[colIndex].editor;
16079     },
16080
16081     /**
16082      * Sets if a column is editable.
16083      * @param {Number} col The column index
16084      * @param {Boolean} editable True if the column is editable
16085      */
16086     setEditable : function(col, editable){
16087         this.config[col].editable = editable;
16088     },
16089
16090
16091     /**
16092      * Returns true if the column is hidden.
16093      * @param {Number} colIndex The column index
16094      * @return {Boolean}
16095      */
16096     isHidden : function(colIndex){
16097         return this.config[colIndex].hidden;
16098     },
16099
16100
16101     /**
16102      * Returns true if the column width cannot be changed
16103      */
16104     isFixed : function(colIndex){
16105         return this.config[colIndex].fixed;
16106     },
16107
16108     /**
16109      * Returns true if the column can be resized
16110      * @return {Boolean}
16111      */
16112     isResizable : function(colIndex){
16113         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16114     },
16115     /**
16116      * Sets if a column is hidden.
16117      * @param {Number} colIndex The column index
16118      * @param {Boolean} hidden True if the column is hidden
16119      */
16120     setHidden : function(colIndex, hidden){
16121         this.config[colIndex].hidden = hidden;
16122         this.totalWidth = null;
16123         this.fireEvent("hiddenchange", this, colIndex, hidden);
16124     },
16125
16126     /**
16127      * Sets the editor for a column.
16128      * @param {Number} col The column index
16129      * @param {Object} editor The editor object
16130      */
16131     setEditor : function(col, editor){
16132         this.config[col].editor = editor;
16133     }
16134 });
16135
16136 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16137         if(typeof value == "string" && value.length < 1){
16138             return "&#160;";
16139         }
16140         return value;
16141 };
16142
16143 // Alias for backwards compatibility
16144 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16145
16146 /**
16147  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16148  * @class Roo.bootstrap.Table.RowSelectionModel
16149  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16150  * It supports multiple selections and keyboard selection/navigation. 
16151  * @constructor
16152  * @param {Object} config
16153  */
16154
16155 Roo.bootstrap.Table.RowSelectionModel = function(config){
16156     Roo.apply(this, config);
16157     this.selections = new Roo.util.MixedCollection(false, function(o){
16158         return o.id;
16159     });
16160
16161     this.last = false;
16162     this.lastActive = false;
16163
16164     this.addEvents({
16165         /**
16166              * @event selectionchange
16167              * Fires when the selection changes
16168              * @param {SelectionModel} this
16169              */
16170             "selectionchange" : true,
16171         /**
16172              * @event afterselectionchange
16173              * Fires after the selection changes (eg. by key press or clicking)
16174              * @param {SelectionModel} this
16175              */
16176             "afterselectionchange" : true,
16177         /**
16178              * @event beforerowselect
16179              * Fires when a row is selected being selected, return false to cancel.
16180              * @param {SelectionModel} this
16181              * @param {Number} rowIndex The selected index
16182              * @param {Boolean} keepExisting False if other selections will be cleared
16183              */
16184             "beforerowselect" : true,
16185         /**
16186              * @event rowselect
16187              * Fires when a row is selected.
16188              * @param {SelectionModel} this
16189              * @param {Number} rowIndex The selected index
16190              * @param {Roo.data.Record} r The record
16191              */
16192             "rowselect" : true,
16193         /**
16194              * @event rowdeselect
16195              * Fires when a row is deselected.
16196              * @param {SelectionModel} this
16197              * @param {Number} rowIndex The selected index
16198              */
16199         "rowdeselect" : true
16200     });
16201     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16202     this.locked = false;
16203 };
16204
16205 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16206     /**
16207      * @cfg {Boolean} singleSelect
16208      * True to allow selection of only one row at a time (defaults to false)
16209      */
16210     singleSelect : false,
16211
16212     // private
16213     initEvents : function(){
16214
16215         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16216             this.grid.on("mousedown", this.handleMouseDown, this);
16217         }else{ // allow click to work like normal
16218             this.grid.on("rowclick", this.handleDragableRowClick, this);
16219         }
16220
16221         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16222             "up" : function(e){
16223                 if(!e.shiftKey){
16224                     this.selectPrevious(e.shiftKey);
16225                 }else if(this.last !== false && this.lastActive !== false){
16226                     var last = this.last;
16227                     this.selectRange(this.last,  this.lastActive-1);
16228                     this.grid.getView().focusRow(this.lastActive);
16229                     if(last !== false){
16230                         this.last = last;
16231                     }
16232                 }else{
16233                     this.selectFirstRow();
16234                 }
16235                 this.fireEvent("afterselectionchange", this);
16236             },
16237             "down" : function(e){
16238                 if(!e.shiftKey){
16239                     this.selectNext(e.shiftKey);
16240                 }else if(this.last !== false && this.lastActive !== false){
16241                     var last = this.last;
16242                     this.selectRange(this.last,  this.lastActive+1);
16243                     this.grid.getView().focusRow(this.lastActive);
16244                     if(last !== false){
16245                         this.last = last;
16246                     }
16247                 }else{
16248                     this.selectFirstRow();
16249                 }
16250                 this.fireEvent("afterselectionchange", this);
16251             },
16252             scope: this
16253         });
16254
16255         var view = this.grid.view;
16256         view.on("refresh", this.onRefresh, this);
16257         view.on("rowupdated", this.onRowUpdated, this);
16258         view.on("rowremoved", this.onRemove, this);
16259     },
16260
16261     // private
16262     onRefresh : function(){
16263         var ds = this.grid.dataSource, i, v = this.grid.view;
16264         var s = this.selections;
16265         s.each(function(r){
16266             if((i = ds.indexOfId(r.id)) != -1){
16267                 v.onRowSelect(i);
16268             }else{
16269                 s.remove(r);
16270             }
16271         });
16272     },
16273
16274     // private
16275     onRemove : function(v, index, r){
16276         this.selections.remove(r);
16277     },
16278
16279     // private
16280     onRowUpdated : function(v, index, r){
16281         if(this.isSelected(r)){
16282             v.onRowSelect(index);
16283         }
16284     },
16285
16286     /**
16287      * Select records.
16288      * @param {Array} records The records to select
16289      * @param {Boolean} keepExisting (optional) True to keep existing selections
16290      */
16291     selectRecords : function(records, keepExisting){
16292         if(!keepExisting){
16293             this.clearSelections();
16294         }
16295         var ds = this.grid.dataSource;
16296         for(var i = 0, len = records.length; i < len; i++){
16297             this.selectRow(ds.indexOf(records[i]), true);
16298         }
16299     },
16300
16301     /**
16302      * Gets the number of selected rows.
16303      * @return {Number}
16304      */
16305     getCount : function(){
16306         return this.selections.length;
16307     },
16308
16309     /**
16310      * Selects the first row in the grid.
16311      */
16312     selectFirstRow : function(){
16313         this.selectRow(0);
16314     },
16315
16316     /**
16317      * Select the last row.
16318      * @param {Boolean} keepExisting (optional) True to keep existing selections
16319      */
16320     selectLastRow : function(keepExisting){
16321         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16322     },
16323
16324     /**
16325      * Selects the row immediately following the last selected row.
16326      * @param {Boolean} keepExisting (optional) True to keep existing selections
16327      */
16328     selectNext : function(keepExisting){
16329         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16330             this.selectRow(this.last+1, keepExisting);
16331             this.grid.getView().focusRow(this.last);
16332         }
16333     },
16334
16335     /**
16336      * Selects the row that precedes the last selected row.
16337      * @param {Boolean} keepExisting (optional) True to keep existing selections
16338      */
16339     selectPrevious : function(keepExisting){
16340         if(this.last){
16341             this.selectRow(this.last-1, keepExisting);
16342             this.grid.getView().focusRow(this.last);
16343         }
16344     },
16345
16346     /**
16347      * Returns the selected records
16348      * @return {Array} Array of selected records
16349      */
16350     getSelections : function(){
16351         return [].concat(this.selections.items);
16352     },
16353
16354     /**
16355      * Returns the first selected record.
16356      * @return {Record}
16357      */
16358     getSelected : function(){
16359         return this.selections.itemAt(0);
16360     },
16361
16362
16363     /**
16364      * Clears all selections.
16365      */
16366     clearSelections : function(fast){
16367         if(this.locked) return;
16368         if(fast !== true){
16369             var ds = this.grid.dataSource;
16370             var s = this.selections;
16371             s.each(function(r){
16372                 this.deselectRow(ds.indexOfId(r.id));
16373             }, this);
16374             s.clear();
16375         }else{
16376             this.selections.clear();
16377         }
16378         this.last = false;
16379     },
16380
16381
16382     /**
16383      * Selects all rows.
16384      */
16385     selectAll : function(){
16386         if(this.locked) return;
16387         this.selections.clear();
16388         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16389             this.selectRow(i, true);
16390         }
16391     },
16392
16393     /**
16394      * Returns True if there is a selection.
16395      * @return {Boolean}
16396      */
16397     hasSelection : function(){
16398         return this.selections.length > 0;
16399     },
16400
16401     /**
16402      * Returns True if the specified row is selected.
16403      * @param {Number/Record} record The record or index of the record to check
16404      * @return {Boolean}
16405      */
16406     isSelected : function(index){
16407         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16408         return (r && this.selections.key(r.id) ? true : false);
16409     },
16410
16411     /**
16412      * Returns True if the specified record id is selected.
16413      * @param {String} id The id of record to check
16414      * @return {Boolean}
16415      */
16416     isIdSelected : function(id){
16417         return (this.selections.key(id) ? true : false);
16418     },
16419
16420     // private
16421     handleMouseDown : function(e, t){
16422         var view = this.grid.getView(), rowIndex;
16423         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16424             return;
16425         };
16426         if(e.shiftKey && this.last !== false){
16427             var last = this.last;
16428             this.selectRange(last, rowIndex, e.ctrlKey);
16429             this.last = last; // reset the last
16430             view.focusRow(rowIndex);
16431         }else{
16432             var isSelected = this.isSelected(rowIndex);
16433             if(e.button !== 0 && isSelected){
16434                 view.focusRow(rowIndex);
16435             }else if(e.ctrlKey && isSelected){
16436                 this.deselectRow(rowIndex);
16437             }else if(!isSelected){
16438                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16439                 view.focusRow(rowIndex);
16440             }
16441         }
16442         this.fireEvent("afterselectionchange", this);
16443     },
16444     // private
16445     handleDragableRowClick :  function(grid, rowIndex, e) 
16446     {
16447         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16448             this.selectRow(rowIndex, false);
16449             grid.view.focusRow(rowIndex);
16450              this.fireEvent("afterselectionchange", this);
16451         }
16452     },
16453     
16454     /**
16455      * Selects multiple rows.
16456      * @param {Array} rows Array of the indexes of the row to select
16457      * @param {Boolean} keepExisting (optional) True to keep existing selections
16458      */
16459     selectRows : function(rows, keepExisting){
16460         if(!keepExisting){
16461             this.clearSelections();
16462         }
16463         for(var i = 0, len = rows.length; i < len; i++){
16464             this.selectRow(rows[i], true);
16465         }
16466     },
16467
16468     /**
16469      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16470      * @param {Number} startRow The index of the first row in the range
16471      * @param {Number} endRow The index of the last row in the range
16472      * @param {Boolean} keepExisting (optional) True to retain existing selections
16473      */
16474     selectRange : function(startRow, endRow, keepExisting){
16475         if(this.locked) return;
16476         if(!keepExisting){
16477             this.clearSelections();
16478         }
16479         if(startRow <= endRow){
16480             for(var i = startRow; i <= endRow; i++){
16481                 this.selectRow(i, true);
16482             }
16483         }else{
16484             for(var i = startRow; i >= endRow; i--){
16485                 this.selectRow(i, true);
16486             }
16487         }
16488     },
16489
16490     /**
16491      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16492      * @param {Number} startRow The index of the first row in the range
16493      * @param {Number} endRow The index of the last row in the range
16494      */
16495     deselectRange : function(startRow, endRow, preventViewNotify){
16496         if(this.locked) return;
16497         for(var i = startRow; i <= endRow; i++){
16498             this.deselectRow(i, preventViewNotify);
16499         }
16500     },
16501
16502     /**
16503      * Selects a row.
16504      * @param {Number} row The index of the row to select
16505      * @param {Boolean} keepExisting (optional) True to keep existing selections
16506      */
16507     selectRow : function(index, keepExisting, preventViewNotify){
16508         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16509         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16510             if(!keepExisting || this.singleSelect){
16511                 this.clearSelections();
16512             }
16513             var r = this.grid.dataSource.getAt(index);
16514             this.selections.add(r);
16515             this.last = this.lastActive = index;
16516             if(!preventViewNotify){
16517                 this.grid.getView().onRowSelect(index);
16518             }
16519             this.fireEvent("rowselect", this, index, r);
16520             this.fireEvent("selectionchange", this);
16521         }
16522     },
16523
16524     /**
16525      * Deselects a row.
16526      * @param {Number} row The index of the row to deselect
16527      */
16528     deselectRow : function(index, preventViewNotify){
16529         if(this.locked) return;
16530         if(this.last == index){
16531             this.last = false;
16532         }
16533         if(this.lastActive == index){
16534             this.lastActive = false;
16535         }
16536         var r = this.grid.dataSource.getAt(index);
16537         this.selections.remove(r);
16538         if(!preventViewNotify){
16539             this.grid.getView().onRowDeselect(index);
16540         }
16541         this.fireEvent("rowdeselect", this, index);
16542         this.fireEvent("selectionchange", this);
16543     },
16544
16545     // private
16546     restoreLast : function(){
16547         if(this._last){
16548             this.last = this._last;
16549         }
16550     },
16551
16552     // private
16553     acceptsNav : function(row, col, cm){
16554         return !cm.isHidden(col) && cm.isCellEditable(col, row);
16555     },
16556
16557     // private
16558     onEditorKey : function(field, e){
16559         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16560         if(k == e.TAB){
16561             e.stopEvent();
16562             ed.completeEdit();
16563             if(e.shiftKey){
16564                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16565             }else{
16566                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16567             }
16568         }else if(k == e.ENTER && !e.ctrlKey){
16569             e.stopEvent();
16570             ed.completeEdit();
16571             if(e.shiftKey){
16572                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16573             }else{
16574                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16575             }
16576         }else if(k == e.ESC){
16577             ed.cancelEdit();
16578         }
16579         if(newCell){
16580             g.startEditing(newCell[0], newCell[1]);
16581         }
16582     }
16583 });/*
16584  * - LGPL
16585  *
16586  * element
16587  * 
16588  */
16589
16590 /**
16591  * @class Roo.bootstrap.MessageBar
16592  * @extends Roo.bootstrap.Component
16593  * Bootstrap MessageBar class
16594  * @cfg {String} html contents of the MessageBar
16595  * @cfg {String} weight (info | success | warning | danger) default info
16596  * @cfg {String} beforeClass insert the bar before the given class
16597  * @cfg {Boolean} closable (true | false) default false
16598  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16599  * 
16600  * @constructor
16601  * Create a new Element
16602  * @param {Object} config The config object
16603  */
16604
16605 Roo.bootstrap.MessageBar = function(config){
16606     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16607 };
16608
16609 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
16610     
16611     html: '',
16612     weight: 'info',
16613     closable: false,
16614     fixed: false,
16615     beforeClass: 'bootstrap-sticky-wrap',
16616     
16617     getAutoCreate : function(){
16618         
16619         var cfg = {
16620             tag: 'div',
16621             cls: 'alert alert-dismissable alert-' + this.weight,
16622             cn: [
16623                 {
16624                     tag: 'span',
16625                     cls: 'message',
16626                     html: this.html || ''
16627                 }
16628             ]
16629         }
16630         
16631         if(this.fixed){
16632             cfg.cls += ' alert-messages-fixed';
16633         }
16634         
16635         if(this.closable){
16636             cfg.cn.push({
16637                 tag: 'button',
16638                 cls: 'close',
16639                 html: 'x'
16640             });
16641         }
16642         
16643         return cfg;
16644     },
16645     
16646     onRender : function(ct, position)
16647     {
16648         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16649         
16650         if(!this.el){
16651             var cfg = Roo.apply({},  this.getAutoCreate());
16652             cfg.id = Roo.id();
16653             
16654             if (this.cls) {
16655                 cfg.cls += ' ' + this.cls;
16656             }
16657             if (this.style) {
16658                 cfg.style = this.style;
16659             }
16660             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16661             
16662             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16663         }
16664         
16665         this.el.select('>button.close').on('click', this.hide, this);
16666         
16667     },
16668     
16669     show : function()
16670     {
16671         if (!this.rendered) {
16672             this.render();
16673         }
16674         
16675         this.el.show();
16676         
16677         this.fireEvent('show', this);
16678         
16679     },
16680     
16681     hide : function()
16682     {
16683         if (!this.rendered) {
16684             this.render();
16685         }
16686         
16687         this.el.hide();
16688         
16689         this.fireEvent('hide', this);
16690     },
16691     
16692     update : function()
16693     {
16694 //        var e = this.el.dom.firstChild;
16695 //        
16696 //        if(this.closable){
16697 //            e = e.nextSibling;
16698 //        }
16699 //        
16700 //        e.data = this.html || '';
16701
16702         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16703     }
16704    
16705 });
16706
16707  
16708
16709